REST


社會在發展,時代在進步。人們日常使用的電子產品也從個人電腦發展到平板電腦,智能手機,智能手表,智慧屏,等等。這些電子產品裝有各種各樣的系統,各種各樣的系統裝有各種各樣的軟件,各種各樣的軟件需向后台服務器請求各種各樣的資源,各種各樣的資源在各種各樣的系統里又有各種各樣的表現形式。比如,同樣是淘寶訂單,當用瀏覽器查看時,訂單就以網頁的形式表現;當用手機淘寶APP查看時,訂單就以手機淘寶APP的形式表現。

毫無疑問,計算機是處理數據的機器。因此,計算機的核心總是數據。在計算機看來,各種各樣的前台終端向后台服務器請求的各種各樣的資源都是數據。前台終端拿到后台服務器的數據之后,理應根據自身的特點表現這些數據。比如,如果前台終端是瀏覽器,則以HTML的形式表現這些數據;如果前台終端是鴻蒙系統,則以鴻蒙系統用戶界面的形式表現這些數據;如果前台終端是IOS系統,則以IOS系統用戶界面的形式表現這些數據。由此可見,后台服務器僅向前台終端響應一份HTML是不合時宜的。畢竟HTML在瀏覽器上表現非常容易,在手機上以手機系統用戶界面的形式表現就難免讓人發瘋了。

因此,后台服務器最好只向前台終端響應一份數據,比如一份JSON格式的數據。這樣,如果前台終端是瀏覽器,則可操縱HTML把數據填進網頁進行表現;如果前台終端是鴻蒙系統,則可調用鴻蒙系統用戶界面API進行表現;如果前台終端是IOS系統,則可調用IOS系統用戶界面API進行表現。於是,同樣一份數據就能在不同的前台終端以不同的形式完美表現了。

基於這樣的思考,前后台分離的開發方式出現了,風靡了,流行了。后台開發人員大可不必關心前台是怎樣實現的,只需寫好API供前台調用之后響應一份數據給前台就行。前台開發人員同樣不必關心后台是怎樣實現的,只需調用后台API拿到數據之后以前台終端支持的方式進行表現就行。於是,一種新的編程思想出現了,這種編程思想就是REST。

REST又稱REST API,RESTful,RESTful API,是英文REpresentational State Transfer的縮寫,中文通常翻譯成表述性狀態轉移,一個抽象到讓人摸不着頭腦的術語。值得慶幸的得,REST本身並沒有“表述性狀態轉移”這樣的術語那樣難以理解。畢竟REST只是一種編程思想,闡述了面向數據的后台API應該怎么實現。為了理解REST這種編程思想,讓我們以庖丁解牛的手法拆開REST,瞧瞧里頭都有哪些東西。只是拆開之前,我們還需知道人們提到表述性狀態轉移時總會習慣性地說漏一個詞,這個詞就是資源。因此,表述性狀態轉移的完整叫法應是資源表述性狀態轉移(Resource REpresentational State Transfer),能被拆成這樣:
1.資源(Resource)
 資源就是數據。
2.表述性(REpresentational)
 表述性是資源的表述方式。比如,同一份數據既可以用JSON表述,也可以用XML表述。
3.狀態(State)
 狀態是指資源的狀態。常用的HTTP請求方法有GET,POST,DELETE,PUT,PATCH,等等。簡單來說,這些HTTP請求方法就是資源的狀態。
4.轉移(Transfer)
 轉移是指資源的轉移。也就是說,資源是流動的。既可從前台流向后台,也可從后台流向前台。資源的轉移可以簡單地理解為數據的流動。

可以看到REST是圍繞資源展開的。資源,也就是數據,是REST的核心。前台向后台發起請求之后,后台響應給前台的應是以某種方式表述的數據。至於前台收到數據之后如何表現則是前台自己的事了。比如,前台收到后台一份以JSON這種格式進行表述的數據之后,能以HTML的形式表現這些數據,把數據渲染成一張好看的網頁;也能以鴻蒙系統用戶界面的形式表現這些數據,把數據渲染成鴻蒙系統風格的用戶界面。

同時我們也注意到了,前台向后台發起的請求是帶有狀態的。這個狀態就表現在HTTP請求方法上。當前台用GET這種HTTP請求方法向后台請求獲取數據時,請求是帶有GET狀態的;當前台用POST這種HTTP請求方法向后台請求新增數據時,請求是帶有POST狀態的;當前台用DELETE這種HTTP請求方法向后台請求刪除數據時,請求是帶有DELETE狀態的;當前台用PUT或PATCH這種HTTP請求方法向后台請求修改數據時,請求是帶有PUT或PATCH狀態的。因此,請求由兩部分組成:一部分是請求URL;一部分是請求狀態。請求URL指定了資源的位置;請求狀態指定了請求的行為,也就是請求是用於獲取數據的,新增數據的,還是刪除數據的,等等。

由此可見,REST描述的是數據應以某種狀態某種表述方式在前后台之間轉移的編程思想。后台實現的API只要符合這種編程思想就是REST。因此,REST既可采用Spring MVC這種技術進行實現,也可采用諸如.NET,PHP,Python之類的技術進行實現。本書介紹的是Spring,自然應該關注怎樣使用Spring MVC這種技術實現REST。基於這樣的目的,讓我們緊接前文實現的person項目,看看改用REST能夠怎么實現。而這,可從控制器的修改開始。

按照前文的實現,控制器返回的是ModelAndView對象。ModelAndView對象保存的是關於數據模型和視圖名的信息,而REST風格的控制器應該只需返回數據。因此,ModelAndView對象已經不合時宜了,我們需對ControlPersonInfo控制器做些修改,如下:

 1 package com.dream.controller;
 2 
 3 import java.util.*;
 4 import org.springframework.stereotype.*;
 5 import org.springframework.beans.factory.annotation.*;
 6 import org.springframework.web.bind.annotation.*;
 7 import org.springframework.web.context.request.*;
 8 import com.dream.service.*;
 9 
10 @Controller
11 public class ControlPersonInfo {
12     private ServicePersonInfo servicePersonInfo = null;
13 
14     @Autowired
15     public void setServicePersonInfo(ServicePersonInfo servicePersonInfo) {
16         this.servicePersonInfo = servicePersonInfo;
17     }
18 
19     @ResponseBody
20     @RequestMapping(value = "/person_info", method = RequestMethod.GET)
21     public List<ServicePersonInfoResult> visit(WebRequest request) {
22         return this.servicePersonInfo.process();
23     }
24 }

可以看到visit方法沒再返回ModelAndView對象,而是返回保存着人的信息的List<ServicePersonInfoResult>列表對象。更加令人好奇的是,visit方法還新帶了個神秘的@ResponseBody注解。

這是怎么回事呢?

前文曾經提到,添加<mvc:annotation-driven />配置啟用注解驅動的Spring MVC之后,Spring MVC將會創建一些基礎Bean。這些基礎Bean提供了些基礎服務,能幫Spring MVC解析注解,生成請求映射表,重新映射請求,等等。可是我們不知道的是,除了創建基礎Bean之外,Spring MVC還會根據類路徑(Classpath)里JAR包的依賴情況默認再建一些Bean。這些Bean同樣提供了些可以幫助Spring MVC處理請求的服務。而這,就包括能把Java對象轉成JSON格式的數據的Bean。為此,我們需往項目里添加一些JAR包,使Spring MVC發現這些JAR包之后自動創建那些能把Java對象轉成JSON格式的數據的Bean。需要添加的JAR包如下:
1.jackson-annotations
 https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-annotations/2.12.3/
2.jackson-core
 https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.12.3/
3.jackson-databind
 https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.12.3/

於是,啟用了注解驅動的Spring MVC發現類路徑里存有這些JAR包之后就會創建一個MappingJackson2HttpMessageConverter類型的Bean。MappingJackson2HttpMessageConverter是Spring MVC定義的一個類,能夠作為消息轉換器(message converter)把帶有@ResponseBody注解的控制器返回的Java對象轉成JSON格式的數據。因此,visit方法帶有@ResponseBody注解。這樣,消息轉換器就能把visit方法返回的Java對象轉成JSON格式的數據了。同時我們也注意到了,Spring MVC處理請求的過程變成這樣:
1.Web容器收到請求之后把請求交給Web應用程序。
2.Web應用程序收到請求之后把請求統一交給DispatcherServlet處理。
3.DispatcherServlet調用處理器映射把請求重新映射給某個符合請求條件的控制器進行處理。
4.控制器完成請求的處理之后返回一個Java對象給DispatcherServlet
5.DispatcherServlet拿到控制器返回的Java對象之后把它交給消息轉換器。
6.消息轉換器把Java對象轉成JSON格式的數據響應請求。

可以看到Spring MVC處理請求的時候已經不再使用InternalResourceViewResolver視圖解析器,而是使用MappingJackson2HttpMessageConverter消息轉換器把Java對象轉成JSON格式的數據。因此,先前添加的關於InternalResourceViewResolver的配置已經沒用了,可以刪掉了。

於是,后台REST大功告成。運行程序之后往瀏覽器里輸入http://localhost:8080/person/person_info按回車鍵,瀏覽器旋能顯示JSON格式的人的信息如下:

當然,我們也能拋開瀏覽器,通過Postman這個工具請求REST,查看REST響應的JSON,如下:
1.打開Postman

2.點擊+按鈕,添加一個Collection,並設置Collection的名字為Open Spring

3.右擊Open Spring這個Collection,彈出上下文菜單之后點擊Add Request添加一個請求。

4.輸入名稱Person,選擇HTTP請求方法為GET,輸入請求URL為http://localhost:8080/person/person_info;保存之后點擊Send發送請求,Postman隨即收到一份JSON格式的數據。

現在的問題是,后台REST已經能夠正常運行了;前台應該怎樣調用這個REST,把JSON格式的數據通過某種方式進行表現呢?方式是多種多樣的。比如,可以寫個鴻蒙系統應用程序,在應用程序里通過鴻蒙系統API調用后台REST請求JSON格式的數據,隨后調用鴻蒙系統用戶界面API將數據以鴻蒙系統用戶界面的形式進行表現;也可通過AJAX調用后台REST請求JSON格式的數據,隨后通過HTML5以網頁的形式表現這些數據。這里,我們采用AJAX這種方式調用后台REST,采用HTML5這種方式表現后台REST響應的數據。因此,先前實現的JSP文件index.jsp已經沒用了,可以把它刪掉。作為替代,請右擊person > web目錄,彈出上下文菜單;之后點擊New > HTML File,彈出New HTML File對話框;輸入index按回車鍵新建index.html文件,修改如下:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>人的信息</title>
 6     <script src="index.js" type="text/javascript"></script>
 7 </head>
 8 <body>
 9     <div id="personInfoPanel"></div>
10 </body>
11 </html>

這是一個HTML文件,里面除了引用<script src="index.js" type="text/javascript"></script>這個JS文件之外,並無太多內容。因此,我們需往index.js文件寫些JS腳本,向后台REST請求JSON格式的數據,把數據填進網頁里把人的信息顯示出來。為此,請右擊person > web目錄,彈出上下文菜單;之后點擊New > JavaScript File,彈出New JavaScript File對話框;輸入index按回車鍵新建index.js文件,修改如下:

 1 window.onload = function() {
 2     requestPersonInfo();
 3 }
 4 function requestPersonInfo() {
 5     var request = new XMLHttpRequest();
 6     request.onload = requestPersonInfoHandler;
 7     request.open("GET", "person_info");
 8     request.send();
 9 }
10 function requestPersonInfoHandler() {
11     if(this.status == 200 && this.responseText != null) {
12         var responseJson = JSON.parse(this.responseText);
13         var personInfoPanel = document.getElementById("personInfoPanel");
14         if (personInfoPanel) {
15             personInfoPanel.innerHTML = processPersonInfo(responseJson);
16         }
17     }
18 }
19 function processPersonInfo(personInfoJson) {
20     var personInfo = "";
21     for(var i = 0; i < personInfoJson.length; i++) {
22         var person = personInfoJson[i];
23         personInfo += "<p>您好!我是" + person.name + ",是個" + person.gender + "!</p>";
24     }
25     return personInfo;
26 }

我們在窗口加載完成事件里通過AJAX發起后台REST請求,拿到JSON格式的數據。之后,我們把JSON格式的數據轉成JS對象,並通過操作DOM把人的信息填進HTML里。於是,用戶在瀏覽器里打開http://localhost:8080/person/index.html之后,能在網頁里看到人的信息。

令人意外的是,當我們滿懷信心往瀏覽器里輸入http://localhost:8080/person/index.html之后,卻發現瀏覽器顯示的是404找不到頁面的錯誤。這是怎么回事呢?

不知大家可還記得,當初配置DispatcherServlet時曾經指定DispatcherServlet能夠映射所有請求。也就是說,不管請求URL是什么,都會映射給DispatcherServlet,由DispatcherServlet進行處理。DispatcherServlet處理請求時會把請求映射給控制器,由相應的控制器進行處理。如果找不到相應的控制器,則會響應一個404找不到頁面的錯誤。

因此,請求http://localhost:8080/person/index.html進入Web應用程序之后也是由DispatcherServlet處理的。可是,我們的程序並沒有提供相應的控制器處理這樣的請求。於是,DispatcherServelt響應一個404找不到頁面的錯誤。

那么,這個問題應該怎么解決呢?

其實,像HTML,JS,CSS,圖片,視頻之類的靜態資源是不需要控制器進行處理的。前台請求這些靜態資源時,應該不作任何處理就把這些靜態資源響應給前台。因此,我們需要告訴DispatcherSerlvet這些靜態資源應該怎么處理。而這,需要修改配置文件servlet-config.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        xmlns:mvc="http://www.springframework.org/schema/mvc"
 6        xsi:schemaLocation="
 7        http://www.springframework.org/schema/beans
 8        http://www.springframework.org/schema/beans/spring-beans.xsd
 9        http://www.springframework.org/schema/context
10        http://www.springframework.org/schema/context/spring-context-4.0.xsd
11        http://www.springframework.org/schema/mvc
12        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
13 
14     <mvc:annotation-driven />
15     <mvc:default-servlet-handler />
16     <context:component-scan base-package="com.dream.controller" />
17     
18 </beans>

這里新增了<mvc:default-servlet-handler />配置,用於告訴DispatcherServlet如果找不到控制器用於映射請求,則把請求交給Web容器本身提供的那個默認的Servlet處理。默認的Servlet收到請求之后,如果發現Web應用程序存在用戶請求的資源,則把資源響應給用戶;如果發現用戶請求的資源並不存在,則響應一個404找不到頁面的錯誤。於是,當我們再次運行Web應用程序時,Web應用程序就能響應http://localhost:8080/person/index.html給瀏覽器了。瀏覽器收到HTML之后加載和運行JS腳本,通過AJAX調用后台REST請求人的信息,收到一份JSON格式的數據。最后,JS解析JSON格式的數據把人的信息填進HTML里。於是,那些曾在三國時期叱吒風雲的帥哥美女再次穿越千年跑進網頁和我們會面。

至此,關於怎樣使用Spring簡化Web開發的基礎知識介紹完了。當然,這里介紹的只是冰山一角,還有很多內容沒有涉及。比如,任何應用程序都不可或缺的異常處理這里就沒有提及。因此,要想比較全面地掌握Spring,還有很長一段路要走。我們將在“細說Spring MVC”時進行詳細介紹。下章,我們將會實現一個小項目,鞏固一下我們這段時間學過的知識。謝謝大家!

返回目錄    下載代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM