zoukankan      html  css  js  c++  java
  • Springboot 2.x Rest 配置

    一、Rest 简介

    从下面的 URL 风格可以看出,我们针对用户的不同操作,URL 都是相同的,我们只是通过 HTTP 的请求方式来确定对 user 进行 增、删、改、查.

    URL HTTP 请求方式 具体操作
    /user/1 POST 添加 id 为 1 的用户记录
    /user/1 DELETE 删除 id 为 1 的用户记录
    /user/1 PUT 修改 id 为 1 的用户记录
    /user/1 GET 查询 id 为 1 的用户记录

     

     

     

     

     

     

     

    二、Rest 配置原理

    Springboot 2.x 对于 Rest 风格的配置都是由 WebMvcAutoConfiguration 这个类完成的,我们这里选取 Springboot-2.3.7.RELEASE 这个版本来探究一下 Springboot 底层对于 Rest 是如何配置的,首先找到 WebMvcAutoConfiguration 这个类,该类中存在一个组件 OrderedHiddenHttpMethodFilter

    代码块一、OrderedHiddenHttpMethodFilter

    // 1、下面的条件成立的情况下,往 IOC 容器中注入该组件
    @Bean
    // 2、如果当前环境下不存在 HiddenHttpMethodFilter 这个类
    @ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
    // 3、如果 application.properties 中没有配置 spring.mvc.hiddenmethod.filter.enabled=true ,那么该判断条件不成立
    @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
    public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
    	return new OrderedHiddenHttpMethodFilter();
    }
    

    点开 OrderedHiddenHttpMethodFilter 这个过滤器

    代码块二、OrderedHiddenHttpMethodFilter 继承 HiddenHttpMethodFilter

    public class OrderedHiddenHttpMethodFilter extends HiddenHttpMethodFilter implements OrderedFilter {
        public static final int DEFAULT_ORDER = -10000;
        private int order = -10000;
    
        public OrderedHiddenHttpMethodFilter() {
        }
    
        public int getOrder() {
            return this.order;
        }
    
        public void setOrder(int order) {
            this.order = order;
        }
    }

    点开 HiddenHttpMethodFilter 这个过滤器

    代码块三、HiddenHttpMethodFilter 

    public class HiddenHttpMethodFilter extends OncePerRequestFilter {
    	// 1、允许的请求方式,下面的静态代码块初始化的时候就赋值了 PUT、DELETE、PATCH
        private static final List<String> ALLOWED_METHODS;
    	// 2、默认的请求方式参数 _method
        public static final String DEFAULT_METHOD_PARAM = "_method";
    	// 3、成员变量初始赋值为 _method ,可以通过 setMethodParam 改变该值
        private String methodParam = "_method";
    
        public HiddenHttpMethodFilter() {
        }
    	
        public void setMethodParam(String methodParam) {
            Assert.hasText(methodParam, "'methodParam' must not be empty");
            this.methodParam = methodParam;
        }
    	// 4、过滤器 doFilter 方法
        protected void doFilterInternal(HttpServletRequest request, 
    	HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    		// 4.1、将原生的 request 赋值给 requestToUse
            HttpServletRequest requestToUse = request;
    		// 4.2、如果请求方式为 POST 并且没有发生异常,则判断条件成立 (form 表单提交方式由于不能发送 PUT、DELETE 方式需要借助包装模式转换)
            if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
    			// 4.3、获取页面 form 表单中 key = _method 所携带的值 value (this.method 的初始值为 _method )
                String paramValue = request.getParameter(this.methodParam);
    			// 4.4、如果参数值存在
                if (StringUtils.hasLength(paramValue)) {
    				// 4.5 将该参数值转为大写
                    String method = paramValue.toUpperCase(Locale.ENGLISH);
    				// 4.6、ALLOWED_METHODS 有三种,分别是 PUT、DELETE、PATCH
                    if (ALLOWED_METHODS.contains(method)) {
    					// 4.7、使用包装模式,将 form 表单传入的请求方式替换原生请求的 POST 方式
                        requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
                    }
                }
            }
    		// 8、放行将包装过的 requestToUse (如果该配置类生效了,使用类似 postman 客户端的时候会直接放行)
            filterChain.doFilter((ServletRequest)requestToUse, response);
        }
    
    	// 5、静态代码块初始化允许的请求方式,总共有三种,分别是 PUT、DELETE、PATCH
        static {
            ALLOWED_METHODS = Collections.unmodifiableList(
    		Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
        }
    
    	// 6、静态内部类 HttpMethodRequestWrapper ,它继承 HttpServletRequestWrapper
    	// HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest
        private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
            private final String method;
    		// 7、静态内部类构造方法
            public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
                super(request);
                this.method = method;
            }
    
            public String getMethod() {
                return this.method;
            }
        }
    }

    三、总结

    3.1、Form 表单提交方式

    传统的 Form 表单的提交方式,只支持 GET、POST 两种提交方式,无法发送 DELETE、PUT 等请求方式,如果你想要支持 DELETE、PUT 这种提交方式,就必须借助于 HiddenHttpMethodFilter 这个过滤器来将 POST 请求方式转为 PUT、DELETE(Springboot 默认配置的是 OrderedHiddenHttpMethodFilter),所以我们需要向 IOC 容器中注入该组件,这样请求发出后,会被该过滤器拦截,过滤器经过相应的处理之后,将请求放行

    对于 Form 表单这种提交方式,如果要往 IOC 容器中注入 OrderedHiddenHttpMethodFilter 这个过滤器,那么我们就必须要满足它的判断条件

    3.1.1、容器中不能存在 HiddenHttpMethodFilter 类型的过滤器,否则判断条件就失效了(代码块一中的判断条件)

    3.1.2、application.properties 中必须配置 spring.mvc.hiddenmethod.filter.enabled=true ,否则判断条件就会失效(代码块一中的判断条件)

    # 如果是 Form 表单的提交方式,必须要开启此配置才能支持 Rest ,而对于 postman 等客户端,则不需要此配置
    spring.mvc.hiddenmethod.filter.enabled=true

    3.1.3、页面表单中必须携带一个请求参数 _method ,并且它的请求方式必须要为 POST(代码块三中的过滤器 doFilter 方法)

    // 请求方式必须为 POST 
    <form action="/user" method="post">
    	// 必须携带请求参数 _method
        <input name="_method" type="hidden" value="delete"/>
        <input value="REST-DELETE 提交" type="submit"/>
    </form>

    3.2、其它客户端

    例如 postman ,它是可以直接 PUT、PATCH、DELETE 等请求方式,不需要借助于 OrderedHiddenHttpMethodFilter ,所以客户端的方式无需任何配置

    四、实践

    4.1、Form 表单发送 PUT、DELETE 请求方式

    4.1.1、index.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8"/>
        <title>首页</title>
        <link rel="icon" th:href="@{/public/favicon.ico}" type="image/x-icon"/>
        <link rel="bookmark" th:href="@{/public/favicon.ico}" type="image/x-icon"/>
    </head>
    <body>
    
    <h1>welcome to index page!!!</h1>
    测试REST风格;
    <form action="/user" method="post">
        <input value="REST-POST 提交" type="submit"/>
    </form>
    
    <form action="/user" method="post">
        <input name="_method" type="hidden" value="delete"/>
        <input value="REST-DELETE 提交" type="submit"/>
    </form>
    
    <form action="/user" method="post">
        <input name="_method" type="hidden" value="PUT"/>
        <input value="REST-PUT 提交" type="submit"/>
    </form>
    
    <form action="/user" method="get">
        <input value="REST-GET 提交" type="submit"/>
    </form>
    
    </body>
    </html>
    

    4.1.2、RestReqController

    @RestController
    public class RestReqController {
        // 可以使用 @PostMapping("/user") 替换
        @RequestMapping(value = "/user", method = RequestMethod.POST)
        public String saveUser() {
            return "POST-张三";
        }
    
        // 可以使用 @DeleteMapping("/user") 替换
        @RequestMapping(value = "/user", method = RequestMethod.DELETE)
        public String deleteUser() {
            return "DELETE-张三";
        }
    
        // 可以使用 @PutMapping("/user") 替换
        @RequestMapping(value = "/user", method = RequestMethod.PUT)
        public String putUser() {
            return "PUT-张三";
        }
    
        // 可以使用 @GetMapping("/user") 替换
        @RequestMapping(value = "/user", method = RequestMethod.GET)
        public String getUser() {
            return "GET-张三";
        }
    }

    4.2、扩展

    如何定义 _method 的名称

    经过上面的代码我们分析如下,当我们满足了判断条件,启用了 Springboot 默认的配置,即向容器中注入了一个 OrderedHiddenHttpMethodFilter 过滤器,该过滤器继承自 HiddenHttpMethodFilter ,我们实现请求方式转换实际上用的是 HiddenHttpMethodFilter 这个过滤器的功能,那么我们完全可以自己向 IOC 容器中注入 HiddenHttpMethodFilter 这个组件,然后调用它的 setMethodParam 方法来改变 methodParam 的默认值,这样我们就可以使用自己喜欢的名称了

    4.2.1、自定义配置类向容器中注入 HiddenHttpMethodFilter 组件

    @Configuration(proxyBeanMethods = false)
    public class MyWebMvcConfig {
    
        @Bean
        public HiddenHttpMethodFilter myMethodFilter(){
            HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
            hiddenHttpMethodFilter.setMethodParam("_xiaomaomao");
            return hiddenHttpMethodFilter;
        }
    }
    

    4.2.2、Form 表单请求参数名称改为 _xiaomaomao

    <form action="/user" method="post">
        <input name="_xiaomaomao" type="hidden" value="delete"/>
        <input value="REST-DELETE 提交" type="submit"/>
    </form>
    

      

  • 相关阅读:
    easyui-tree/combotree 子节点前端懒加载(主要解决ie11下加载慢
    解决 Chrome 下表单自动填充问题 (两种方法
    代码编辑器:本地JS文件上传并加载到页面
    PC端使用rem进行屏幕适配
    ECharts 点击非图表区域的点击事件不触发问题
    Angular2+ 使用 Post 请求下载文件
    Express + Element-ui 实现图片/文件上传
    phpMyAdmin -- 没有权限操作用户
    Note of Moment -- 日期处理
    Angular 自定义表单控件 -- CheckboxGroupComponent
  • 原文地址:https://www.cnblogs.com/xiaomaomao/p/14286984.html
Copyright © 2011-2022 走看看