zoukankan      html  css  js  c++  java
  • 第二节:REST风格的案例及源码分析

    一、REST 风格的请求

      发送 REST 风格的增删改查请求:

    发起图书的增删改查请求;使用REST风格的URL地址
        /book/1     GET:查询1号图书
        /book/1     DELETE:删除1号图书
        /book/1     PUT:更新1号图书
        /book         POST:添加图书
        http://localhost:8080/index.jsp
    

      具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。

      它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删

      思考:浏览器的 form 表单只提供了 get 与 post 这两种提交方式,如何进行 put 和 delete 方式的提交呢?

      为了支持页面发起 PUT 和 DELETE 的请求,Spring 提供了对 REST 风格的支持:

      (1)SpringMVC 中有一个 Filter,可以把普通的请求转化为规定形式的请求,配置这个 Filter;

      (2)如何发起其他形式请求?

          按照以下要求:

          ① 创建一个 post 类型的表单;

          ② 表单项中携带一个 _method 的参数;

          ③ 这个 _method 的值就是 DELETE 或者 PUT;

      HiddenHttpMethodFilter:浏览器 form 表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支持,Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与 DELETE 请求。

    二、实验代码

      1、控制器代码

    @Controller
    public class RestTestController {
    
        @RequestMapping(value = "/book/{bid}", method = RequestMethod.GET)
        public String getBook(@PathVariable("bid")Integer bid) {
            System.out.println("查询到了" + bid + "图书");
            return "success";
        }
    
        @RequestMapping(value = "/book", method = RequestMethod.POST)
        public String saveBook() {
            System.out.println("添加了新的图书");
            return "success";
        }
    
        @RequestMapping(value = "/book/{bid}", method = RequestMethod.DELETE)
        public String deleteBook(@PathVariable("bid")Integer bid) {
            System.out.println("删除了" + bid + "图书");
            return "success";
        }
    
        @RequestMapping(value = "/book/{bid}", method = RequestMethod.PUT)
        public String updateBook(@PathVariable("bid")Integer bid) {
            System.out.println("更新了" + bid + "图书");
            return "success";
        }
    }

      2、配置 HiddenHttpMethodFilter 过滤器(SpringMVC提供的过滤器)

        <!-- 支持REST风格的过滤器:可以将POST请求转换为PUT或DELETE请求 -->
        <filter>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>

      3、页面请求链接

    </body>
    
    <a href="book/1">查询图书</a>
    
    <form action="book" method="post">
        <input type="submit" value="添加图书">
    </form>
    
    
    <form action="book/1" method="post">
        <input name="_method" value="PUT">
        <input type="submit" value="更新1号图书">
    </form>
    
    <form action="book/1" method="post">
        <input name="_method" value="DELETE">
        <input type="submit" value="删除1号图书">
    </form>
    
    </body>

      4、测试

      5、可能会出现的问题

       (1)这个代码的运行环境需要 Tomcat7.0以及以下版本。

          原因: Tomcat按照JCP规范(JSP2.3版本)的规定,从Tomcat8.x版本开始,不再支持以HTTP PUT方式访问JSP页面,仅支持GET、POST和HEAD方式。
          而在控制器方法中编写的返回值是一个字符串,SpringMVC会认为这是一个jsp页面,所以报错了。

       (2)使用 Tomcat8.0或以上会报JSPs only permit GET POST or HEAD 的错误。

          

           解决方式一:在jsp页面设置为  isErrorPage

    <%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
    

            解决方式二:

            如果在 controller 里的 testRESTPUT 的方法加上 @ResponseBody() 注解, 并且返回一个字符串,就不会操作了。        
            @ResponseBody注解的作用是将控制器方法的返回值通过适当的转换器转换为指定的格式之后,写入到Response对象的body区,通常用来返回JSON数据或者是XML数据。
            注意:在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,他的效果等同于通过Response对象输出指定格式的数据。

    三、HiddenHttpMethodFilter过滤器源码分析

      1、为什么请求隐含参数名称必须叫做 "_method"?

        

          因为在过滤器中默认是获取参数名为 "_method" 的参数值的。

      2、HiddenHttpMethodFilter 的处理过程

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
    
            //获取表单上 _method 带来的值(delete、put)
            String paramValue = request.getParameter(this.methodParam);
            
            //获取表单方式为 POST 而且 _method 有值
            if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
                //把参数转为大写(DELETE、PUT)
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                
                //包装了一下request对象,并且重写了request对象的 getMethod
                HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
                
                //wrapper.getMethod (PUT 或 DELETE)
                filterChain.doFilter(wrapper, response);
            }
            else {
                //直接放行
                filterChain.doFilter(request, response);
            }
        }
    
    
    
        private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
    
            private final String method;
    
            public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
                super(request);
                this.method = method;
            }
            
            //重写了 getMethod 方法
            @Override
            public String getMethod() {
                return this.method;
            }
        }

     

      3、图解

        

  • 相关阅读:
    大写的服,看完这篇你还不懂RocketMQ算我输
    写一个通用的幂等组件,我觉得很有必要
    如何将分布式锁封装的更优雅
    哇,ElasticSearch多字段权重排序居然可以这么玩
    每日一道 LeetCode (52):三数之和
    JVM 第六篇:极致优化 IDEA 启动速度
    JVM 第五篇:命令行 JVM 故障处理工具
    JVM 第四篇:可视化 JVM 故障处理工具
    JVM 第三篇:Java 类加载机制
    JVM 第二篇:垃圾收集器以及算法
  • 原文地址:https://www.cnblogs.com/niujifei/p/15527423.html
Copyright © 2011-2022 走看看