第三章:REST
REST是什麼
- 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用來刪除資源。
- URL風格:
※請求路徑相同,根據請求方法確定使用哪個方法來處理請求。
以前實現CRUD(增刪改查):
查詢:findAll GET
插入:insertUser POST
更新:getUserById GET updateUser PUT
刪除:deletUser GET
REST風格實現CRUD(增刪改查):
操作 URL 請求方式
查詢: /user/1001 GET
插入: /user/ POST
更新: 查詢後再修改 /user/ PUT
刪除: /user/1001 DELETE
♦雖然服務端可以處理PUT和DELETE,但是客戶端form表單只有GET和POST兩種方式向伺服器端傳送請求。那麼請求方式PUT,DELETE的時候要怎麼向伺服器端傳送請求呢??
思路:客戶端都以POST的方式傳送請求,同時帶過去一個引數_method,然後對請求方式和引數進行判斷:
POST&&_method=PUT:PUT
POST&&_method=DELETE:DELETE
POST&&_method=null:POST
上面的處理要在客戶端傳送請求後DispatcherServelet前處理,這樣的處理可以用監聽(listener)或過濾器(filter)
web.xml中個元件的順序:listener>>filter>>servlet
當然spring MVC已經給我們想好了,提供了一個filter:HiddenHttpMethodFilter
原始碼:
HiddenHttpMethodFilter.java
1 package org.springframework.web.filter; 2 3 import java.io.IOException; 4 import java.util.Locale; 5 import javax.servlet.FilterChain; 6 import javax.servlet.ServletException; 7 import javax.servlet.ServletRequest; 8 import javax.servlet.http.HttpServletRequest; 9 import javax.servlet.http.HttpServletRequestWrapper; 10 import javax.servlet.http.HttpServletResponse; 11 import org.springframework.util.Assert; 12 import org.springframework.util.StringUtils; 13 14 public class HiddenHttpMethodFilter extends OncePerRequestFilter { 15 public static final String DEFAULT_METHOD_PARAM = "_method"; 16 private String methodParam = "_method"; 17 18 public HiddenHttpMethodFilter() { 19 } 20 21 public void setMethodParam(String methodParam) { 22 Assert.hasText(methodParam, "'methodParam' must not be empty"); 23 this.methodParam = methodParam; 24 } 25 26 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 27 HttpServletRequest requestToUse = request; 28 if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) { 29 String paramValue = request.getParameter(this.methodParam); 30 if (StringUtils.hasLength(paramValue)) { 31 requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, paramValue); 32 } 33 } 34 35 filterChain.doFilter((ServletRequest)requestToUse, response); 36 } 37 38 private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { 39 private final String method; 40 41 public HttpMethodRequestWrapper(HttpServletRequest request, String method) { 42 super(request); 43 this.method = method.toUpperCase(Locale.ENGLISH); 44 } 45 46 public String getMethod() { 47 return this.method; 48 } 49 } 50 }
REST CRUD
web.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" 5 version="4.0"> 6 <filter> 7 <filter-name>HiddenHttpMethodFilter</filter-name> 8 <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> 9 </filter> 10 <filter-mapping> 11 <filter-name>HiddenHttpMethodFilter</filter-name> 12 <url-pattern>/*</url-pattern> 13 </filter-mapping> 14 <servlet> 15 <servlet-name>springMVC</servlet-name> 16 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 17 </servlet> 18 <servlet-mapping> 19 <servlet-name>springMVC</servlet-name> 20 <url-pattern>/</url-pattern> 21 </servlet-mapping> 22 </web-app>
springMVC-servlet.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 6 7 <context:component-scan base-package="com.iwakan.controller"/> 8 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 9 <!-- 10 /WEB-INF下的東西不能直接訪問,只能通過轉發進行訪問。 11 轉發和重定向的區別在於,地址釋出發生變化,重定向地址發生變化, 12 如果用重定向訪問的化話,又相當於直接訪問WEB-INF下的頁面了 13 --> 14 <property name="prefix" value="/WEB-INF/view/"/> 15 <property name="suffix" value=".jsp"></property> 16 </bean> 17 </beans>
rest.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <html> 3 <head> 4 <title>Rest</title> 5 </head> 6 <body> 7 <a href="/testREST/1001">測試GET</a> 8 <br> 9 <br> 10 <form action="/testREST" method="post"> 11 <input type="submit" value="測試POST"> 12 </form> 13 <br> 14 <form action="/testREST" method="post"> 15 <input type="hidden" name="_method" value="PUT"> 16 <input type="submit" value="測試PUT"> 17 </form> 18 <br> 19 <form action="/testREST/1001" method="post"> 20 <input type="hidden" name="_method" value="DELETE"> 21 <input type="submit" value="測試DELETE"> 22 </form> 23 </body> 24 </html>
RestController.java
1 package com.iwakan.controller; 2 3 import org.springframework.stereotype.Controller; 4 import org.springframework.web.bind.annotation.PathVariable; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RequestMethod; 7 8 @Controller 9 public class RestController { 10 11 @RequestMapping(value = "/testREST/{id}", method = RequestMethod.GET) 12 public String getUserById(@PathVariable("id") Integer id) { 13 System.out.println("GET:id==" + id); 14 return "success"; 15 } 16 17 @RequestMapping(value = "/testREST" ,method = RequestMethod.POST) 18 public String insertUser() { 19 System.out.println("POST"); 20 return "success"; 21 } 22 23 @RequestMapping(value = "/testREST",method = RequestMethod.PUT) 24 public String UpdateUser(){ 25 System.out.println("PUT"); 26 return "success"; 27 } 28 29 @RequestMapping(value = "/testREST/{id}",method = RequestMethod.DELETE) 30 public String deleteUser(@PathVariable("id") Integer id){ 31 System.out.println("DELETE:id=="+id); 32 return "success"; 33 } 34 }
success.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <html> 3 <head> 4 <title>成功</title> 5 </head> 6 <body> 7 <h2>成功</h2> 8 </body> 9 </html>
當點選上面連個按鈕時報錯:
解決辦法:
三種簡單處理的辦法!
第一:tomcat換到7.0以及以下版本
第二:請求先轉給一個Controller,再返回jsp頁面
第三種:在你的success頁面頭部檔案將
<%@ page language=”java” contentType=”text/html; charset=UTF-8”
pageEncoding=”UTF-8” isErrorPage=”true”%>
success.jsp
1 <%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" isErrorPage="true" language="java" %> 2 <html> 3 <head> 4 <title>成功</title> 5 </head> 6 <body> 7 <h2>成功</h2> 8 </body> 9 </html>
修改完成後,在點選就成功了。
用Ajax也可以實現REST風格。
※ form表單只有GET和POST兩種請求,只能通過HiddenHttpMethodFilter來實現。然而,AJax有八種請求方式,所以可以在不通過HiddenHttpMethodFilter直接實現