1. 程式人生 > 實用技巧 >第三章:REST

第三章:REST

REST是什麼

  1. REST即Representational State Transfer.(資源)表現層狀態轉換是目前最流行的一種網際網路軟體架構,他結構清晰,符合標準,易於理解,擴充套件方便,所以正得到越來越多的網站的採用。
    1. 資源(resources):網路上的一個實體,或者說是網路上的一個具體資訊。它可以是一段文字,一張圖片,一首歌曲,一種服務,總之就是一個具體的存在。可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的URI。獲取這個資源,訪問它的URI就可以了,因此URI即為每一個資源的獨一無二的識別符。
    2. 表現層(Representation):把資源具體呈現出來的形式,叫做他的表現層(Representation)。比如,文字可以用txt格式表現,也可以用HTML格式,XML格式,JSON格式表現,甚至可以採用二進位制格式。
    3. 狀態轉換(State Transfer):每發出一個請求,就代表了客戶端和伺服器的一次互動過程。HTTP協議,是一個無狀態協議,即所有狀態都儲存在伺服器端。因此,如果客戶端想要操作伺服器,必須通過某種手段,讓伺服器端發生“狀態轉化”(State Transfer)而這種轉化是建立在表現層之上的,所以就是“表現層狀態轉化”
    4. 具體說,就是HTTP協議裡面,四個表示操作方式的動詞:GET,POST,PUT,DELETE。他們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源,PUT用來更新資源,DELETE用來刪除資源。
  2. 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直接實現