【1】REST简介
以下来源于百度百科:
REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
在三种主流的Web服务实现方案中,因为REST模式的Web服务与复杂的SOAP和XML-RPC对比来讲明显的更加简洁,越来越多的web服务开始采用REST风格设计和实现。
REST : 即 Representational
State
Transfer
,(资源)表现层状态转化(表现层资源状态转移)。
资源
网络上的一个实体或者说是网络上的一个具体信息。 每种资源对应一个特定的URI,因此URI为每一个资源的独一无二的识别符。资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。
与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。
资源的表述
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交
换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格
式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
状态转化/转移
态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资
源的表述,来间接实现操作资源的目的。每发出一个请求,就代表了客户端和服务器端的一次交互过程。HTTP协议是一个无状态协议,即所有的状态都保存在服务器上。
因此用户想要操作服务器,必须通过某种手段,让服务器发生状态变化。而这种状态变化是建立在表现层之上的,所以就是“表现层状态变化”。
REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方
式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。
【2】SpringMVC 与 REST相结合
浏览器form表单只支持GET与POST请求,Spring3.0增加了一个过滤器,可以将这些HTTP请求转换为标准的HTTP方法,使得支持GET、POST、PUT与DELETE请求。
由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求
HiddenHttpMethodFilter 处理put和delete请求的条件:
当前请求的请求方式必须为post
当前请求必须传输请求参数_method
满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式
① HiddenHttpMethodFilter
源码如下:
public class HiddenHttpMethodFilter extends OncePerRequestFilter { //PUT DELETE PATCH private static final List<String> ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name())); /**默认方法参数 _method */ public static final String DEFAULT_METHOD_PARAM = "_method"; private String methodParam = DEFAULT_METHOD_PARAM; public void setMethodParam(String methodParam) { Assert.hasText(methodParam, "'methodParam' must not be empty"); this.methodParam = methodParam; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequest requestToUse = request; if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) { String paramValue = request.getParameter(this.methodParam); if (StringUtils.hasLength(paramValue)) { String method = paramValue.toUpperCase(Locale.ENGLISH); if (ALLOWED_METHODS.contains(method)) { requestToUse = new HttpMethodRequestWrapper(request, method); } } } filterChain.doFilter(requestToUse, response); } //简单request包装器,有参数method标明转换后的参数 private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { private final String method; public HttpMethodRequestWrapper(HttpServletRequest request, String method) { super(request); this.method = method; } @Override public String getMethod() { return this.method; } } }
源码解释如下:
① 判断当前方法是否为POST且request属性中WebUtils.ERROR_EXCEPTION_ATTRIBUTE是否为空。
② 获取_method参数对应的值,判断是否在ALLOWED_METHODS内。如果在,则包装request
③ 继续流转filterChain
② web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <listener> <listener-class>com.web.hh.utils.SpringIocContextListener</listener-class> </listener> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <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> <servlet> <servlet-name>hh-mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>namespace</param-name> <param-value>hh-mvc</param-value> </init-param> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext.xml</param-value> </init-param> <init-param> <param-name>publishContext</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hh-mvc</servlet-name> <!-- 注意这里 --> <url-pattern>/</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
【3】方法测试
① GET
JSP页面,获取资源 且 GET方法默认支持,不需要使用隐藏域。
<a href="springmvc/testRest/1">Test Rest Get</a>
后台code
@RequestMapping(value = "/testRest/{id}", method = RequestMethod.GET) public String testRest(@PathVariable Integer id) { System.out.println("testRest GET: " + id); return SUCCESS; }
可见,与普通的方法并无差异。
② POST
JSP页面:
<form action="springmvc/testRest" method="post"> <input type="submit" value="TestRest POST"/> </form>
后台code:
@RequestMapping(value = "/testRest", method = RequestMethod.POST) public String testRest() { System.out.println("testRest POST"); return SUCCESS; }
同样默认支持,与普通的方法并无差异。
③ PUT
更新资源,默认不支持该方法,需要在JSP页面设隐藏域。
JSP页面:
//form里面method是POST,_method的value为PUT <form action="springmvc/testRest/1" method="post"> <input type="hidden" name="_method" value="PUT"/> //设置隐藏域 注意 name 与value <input type="submit" value="TestRest PUT"/> </form>
后台code
// method为PUT @RequestMapping(value = "/testRest/{id}", method = RequestMethod.PUT) public String testRestPut(@PathVariable("id") Integer id) { System.out.println("testRest Put: " + id); return SUCCESS; }
④ DELETE
删除资源,默认不支持该方法,需要在JSP页面设隐藏域。
JSP页面:
<form action="springmvc/testRest/1" method="post"> <input type="hidden" name="_method" value="DELETE"/> //设置隐藏域 注意 name 与value <input type="submit" value="TestRest DELETE"/> </form>
JSP页面:
<form action="springmvc/testRest/1" method="post"> <input type="hidden" name="_method" value="DELETE"/> //设置隐藏域 注意 name 与value <input type="submit" value="TestRest DELETE"/> </form>
后台code:
//method为DELETE @RequestMapping(value = "/testRest/{id}", method = RequestMethod.DELETE) public String testRestDelete(@PathVariable(value="id") Integer id) { System.out.println("testRest Delete: " + id); return SUCCESS; }
关于PUT和DELETE,均是在表单中设置隐藏域,name='_method',value
设置为对应值。然后form表单发送POST请求,由HiddenHttpMethodFilter根据_method
的值将POST请求转换为PUT请求或者DELETE请求。