【第6章】spring-mvc之rest和filter


前言

REST(Representational State Transfer,表现层状态转移)是一种软件架构风格,它定义了一组设计原则和约束条件,用于创建Web服务。REST的核心思想是将网络资源视为一系列可以通过HTTP协议进行操作的抽象实体,这些实体以某种形式的表现层来展现其状态,客户端通过操作这些资源的状态来实现与Web服务的交互。

REST的主要原则包括:

  1. 客户端-服务器架构:将用户界面关注点与应用服务器关注点分离,允许它们独立进化。

  2. 无状态:服务器不会在客户端请求之间保留应用状态。每个请求都必须包含理解该请求所必需的信息(通常是通过包含先前服务器返回的所有状态)。

  3. 分层系统:允许将系统分解为若干层,每一层对下一层隐藏其内部实现细节。客户端不需要知道它与之交互的中间层的存在。

  4. 统一接口:通过四个接口约束(识别资源、通过表述操作资源、自描述消息、超媒体作为应用状态的引擎)来简化并解耦组件。

    • 资源识别:每个资源都有一个唯一的URL。
    • 通过表述操作资源:客户端通过HTTP方法对资源进行操作(如GET、POST、PUT、DELETE)。
    • 自描述消息:消息必须能够自我描述,以便客户端能够正确理解和处理。
    • 超媒体作为应用状态的引擎:客户端可以通过阅读返回的文档(通常包含超链接)来发现可以执行的操作。
  5. 按需代码(可选):通过下载并执行代码来扩展客户端功能。

RESTful Web服务使用HTTP协议的方法(GET、POST、PUT、DELETE等)对资源进行操作,并且通常返回JSON或XML格式的响应。RESTful API的设计目标是简单、可预测和易于使用,使得不同平台和语言之间的交互变得更加容易。

RESTful API的设计通常会遵循一些最佳实践,如使用适当的HTTP方法、使用名词作为资源路径、使用HTTP状态码来传达操作结果等。这些实践有助于确保API的一致性和易用性。

总的来说,REST是一种用于构建Web服务的架构风格,它强调资源的状态和客户端与服务器的交互,通过HTTP协议和统一的接口约束来实现这一目标。


一、rest

1. JSP

<%--
  Created by IntelliJ IDEA.
  User: 张军国001
  Date: 2024/4/28
  Time: 20:06
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>rest</title>
</head>
<body>
<form action="/rest/user/1" method="get">
    <button type="submit">查询</button>
</form>
<form action="/rest/user/1" method="post">
    <button type="submit">新增</button>
</form>
<form action="/rest/user/1" method="put">
    <button type="submit">更新</button>
</form>
<form action="/rest/user/1" method="delete">
    <button type="submit">删除</button>
</form>
</body>
</html>

2. Controller

package org.example.springmvc.params.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Create by zjg on 2024/4/28
 */
@Controller
@RequestMapping("/rest")
public class RestController {
    String rest="redirect:/params/rest.jsp";
    @RequestMapping("/hello-rest")
    public String params(){//重定向
        return rest;
    }
    @GetMapping("/user/{id}")
    public String queryUser(@PathVariable Integer id){
        System.out.println("查询用户信息:"+id);
        return rest;
    }
    @PostMapping("/user/{id}")
    public String addUser(@PathVariable Integer id){
        System.out.println("新增用户信息:"+id);
        return rest;
    }
    @PutMapping("/user/{id}")
    public String updateUser(@PathVariable Integer id){
        System.out.println("更新用户信息:"+id);
        return rest;
    }
    @DeleteMapping("/user/{id}")
    public String deleteUser(@PathVariable Integer id){
        System.out.println("删除用户信息:"+id);
        return rest;
    }
}

3. 结果

查询用户信息:1
新增用户信息:1
查询用户信息:1
查询用户信息:1

通过JSP测试发现put和delete走进了get方法,未达到预期效果

截止到当前时间(2024年4月28日),HTML规范本身并不直接支持PUT和DELETE方法。虽然HTML允许自定义表单方法,但大多数浏览器主要支持GET和POST方法。这主要有两方面的原因:

  1. 浏览器兼容性问题:不同浏览器对于PUT和DELETE方法的支持程度不同。由于PUT和DELETE方法在早期互联网应用中使用较少,浏览器开发者没有将其作为高优先级的功能进行开发和测试。因此,在一些浏览器中,对于PUT和DELETE方法的支持存在不一致甚至无法正确处理的情况。
  2. 安全性问题:PUT和DELETE方法相对于GET和POST方法,操作更加危险,因为它们可能会对服务器上的资源进行更改或删除。对于普通网页中的表单提交,这种操作可能会带来潜在的风险和滥用,因此浏览器厂商倾向于限制对这些方法的支持,以减少安全威胁。

尽管HTML表单本身不支持PUT和DELETE方法,但开发者可以通过JavaScript脚本或其他技术实现这些接口。例如,在RESTful Web服务中,尽管HTML表单只支持GET和POST,但可以通过JavaScript或其他后端技术来处理PUT和DELETE请求。

二、filter

html会将我们的请求转换为get请求,我们可以添加标志位拦截请求,在请求到达控制器之前将请求重新包装,以达到触发rest的效果。
现实中不建议大家这样做,直接用postman测试即可。

1.JSP

增加标识隐藏域_method,作为请求类型转换依据

<%--
  Created by IntelliJ IDEA.
  User: 张军国001
  Date: 2024/4/28
  Time: 20:06
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>rest</title>
</head>
<body>
<form action="/rest/user/1" method="get">
    <button type="submit">查询</button>
</form>
<form action="/rest/user/1" method="post">
    <button type="submit">新增</button>
</form>
<form action="/rest/user/1" method="put">
    <input hidden="hidden" name="_method" value="put">
    <button type="submit">更新</button>
</form>
<form action="/rest/user/1" method="delete">
    <input hidden="hidden" name="_method" value="delete">
    <button type="submit">删除</button>
</form>
</body>
</html>

2.自定义过滤器

2.1 RequestFilter

请求过滤器的作用为:当接收到带有标识的请求,根据标识值转换请求类型并存储原请求类型。

package org.example.springmvc.params.controller.filter;

import jakarta.servlet.DispatcherType;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import java.io.IOException;
import java.util.List;
import java.util.Locale;

/**
 * Create by zjg on 2024/4/28
 */
@WebFilter(urlPatterns = "/*",dispatcherTypes = DispatcherType.REQUEST)
public class RequestFilter extends HiddenHttpMethodFilter {
    private static final List<String> ALLOWED_METHODS;
    private String methodParam = "_method";
    static {
        ALLOWED_METHODS = List.of(HttpMethod.PUT.name(), HttpMethod.DELETE.name());
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String paramValue = request.getParameter(this.methodParam);
        String method = request.getMethod();
        if ("GET".equals(method) && request.getAttribute("jakarta.servlet.error.exception") == null) {
            if (StringUtils.hasLength(paramValue)) {
                request.setAttribute(this.methodParam,method);
                String newMethod = paramValue.toUpperCase(Locale.ENGLISH);
                if (ALLOWED_METHODS.contains(newMethod)) {
                    request = new HttpMethodRequestWrapper(request, newMethod);
                }
            }
        }
        System.out.println("RequestFilter-before");
        filterChain.doFilter(request, response);
        System.out.println("RequestFilter-after");
    }
    private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
        private final String method;

        public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
            super(request);
            this.method = method;
        }

        public String getMethod() {
            return this.method;
        }
    }
}

2.2 ForwardFilter

转发过滤器的作用为:在转发之前判断请求是否带有标识,有标识则获取原请求类型并进行转换。

package org.example.springmvc.params.controller.filter;

import jakarta.servlet.DispatcherType;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import java.io.IOException;
import java.util.List;
import java.util.Locale;

/**
 * Create by zjg on 2024/4/28
 */
@WebFilter(urlPatterns = "/*",dispatcherTypes = DispatcherType.FORWARD)
public class ForwardFilter extends HiddenHttpMethodFilter {
    private static final List<String> ALLOWED_METHODS;
    private String methodParam = "_method";
    static {
        ALLOWED_METHODS = List.of(HttpMethod.GET.name(), HttpMethod.POST.name());
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String paramValue = request.getParameter(this.methodParam);
        String method = request.getMethod();
        if (paramValue!=null && method.equals(paramValue.toUpperCase(Locale.ENGLISH))) {
            String newMethod = request.getAttribute(this.methodParam)==null?"":String.valueOf(request.getAttribute(this.methodParam)).toUpperCase(Locale.ENGLISH); ;
            if (StringUtils.hasLength(newMethod)) {
                if (ALLOWED_METHODS.contains(newMethod)) {
                    request = new HttpMethodRequestWrapper(request, newMethod);
                }
            }
        }
        System.out.println("ForwardFilter-before");
        filterChain.doFilter(request, response);
        System.out.println("ForwardFilter-after");
    }
    private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
        private final String method;

        public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
            super(request);
            this.method = method;
        }

        public String getMethod() {
            return this.method;
        }
    }
}

3. 执行结果

RequestFilter-before
删除用户信息:1
ForwardFilter-before
ForwardFilter-after
RequestFilter-after

总结

回到顶部

目前这种方式很少涉及到,我这样使用也是为了给大家展示一个过滤器的使用案例。
纸上得来终觉浅,绝知此事要躬行。
与诸君共勉!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值