HiddenHttpMethodFilter (REST)

概述

REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。
它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
目前在三种主流的Web服务实现方案中,因为REST模式的Web服务与复杂的SOAP和XML-RPC对比来讲明显的更加简洁,越来越多的web服务开始采用REST风格设计和实现。
例如,Amazon.com提供接近REST风格的Web服务进行图书查找;雅虎提供的Web服务也是REST风格的。

参考资料

  1. 理解本真的REST架构风格
  2. 深入浅出REST

什么是REST风格

  1. REST:即 Representational State Transfer。(资源)表现层状态转化 。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用

    • 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。
      它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。
      可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。
      获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符。
    • 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation) 。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
    • 状态转化(State Transfer) :每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。
      而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。
    • 具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。
  2. URL风格

    1
    2
    3
    4
    /order/1  HTTP GET :得到 id = 1 的 order   
    /order/1 HTTP DELETE:删除 id = 1的 order
    /order/1 HTTP PUT:更新id = 1的 order
    /order HTTP POST:新增 order
  3. HiddenHttpMethodFilter

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

实验代码

  1. 配置HiddenHttpMethodFilter过滤器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <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>
  2. 配置前端控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map all requests to the DispatcherServlet for handling -->
    <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
  3. 配置springmvc-servlet

    1
    2
    3
    4
    5
    <context:component-scan base-package="xyz.lyhcc"></context:component-scan>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/"></property>
    <property name="suffix" value=".jsp"></property>
    </bean>
  4. 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
       @RequestMapping(value="/book/{id}", method=RequestMethod.GET)
    public String RESTGet(@PathVariable(value="id")Integer id) {
    System.out.println("GET " + id);
    return "success";
    }
    @RequestMapping(value="/book", method=RequestMethod.POST)
    public String RESTPost() {
    System.out.println("Post ");
    return "success";
    }
    @RequestMapping(value="/book/{id}", method=RequestMethod.PUT)
    public String RESTPut(@PathVariable(value="id")Integer id) {
    System.out.println("Put " + id);
    return "success";
    }
    @RequestMapping(value="/book/{id}", method=RequestMethod.DELETE)
    public String RESTDelete(@PathVariable(value="id")Integer id) {
    System.out.println("Delete " + id);
    return "success";
    }
  5. 请求链接

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <fieldset>
    <!-- 实验1 测试 REST风格 GET 请求 -->
    <a href="book/1">testREST GET</a><br/><br/>

    <!-- 实验2 测试 REST风格 POST 请求 -->
    <form action="book" method="POST">
    <input type="submit" value="testRESTPost">
    </form>

    <!-- 实验3 测试 REST风格 PUT 请求 -->
    <form action="book/1" method="POST">
    <input type="hidden" name="_method" value="PUT">
    <input type="submit" value="testRESTPut">
    </form>

    <!-- 实验4 测试 REST风格 DELETE 请求 -->
    <form action="book/1" method="POST">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="testRESTDelete">
    </form>
    </fieldset>

    源码分析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    throws ServletException, IOException {

    String paramValue = request.getParameter(this.methodParam);
    if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
    String method = paramValue.toUpperCase(Locale.ENGLISH);
    HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method);
    filterChain.doFilter(wrapper, response);
    }
    else {
    filterChain.doFilter(request, response);
    }
    }

注意高版本Tomcat会出现一个问题

1
2
3
<!-- 在相应的位置添加isError=true -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isErrorPage="true"%>

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×