文章大綱
一、什么是RESTful
二、為什么要使用RESTful
三、RESTful實戰
四、項目源碼下載
五、參考文章

一、什么是RESTful
1. RESTful概念
REST 是面向資源的,這個概念非常重要,而資源是通過 URI 進行暴露。
URI 的設計只要負責把資源通過合理方式暴露出來就可以了。對資源的操作與它無關,操作是通過 HTTP動詞來體現,所以REST 通過 URI 暴露資源時,會強調不要在 URI 中出現動詞。
比如:左邊是錯誤的設計,而右邊是正確的
GET /rest/api/getDogs --> GET /rest/api/dogs 獲取所有小狗狗
GET /rest/api/addDogs --> POST /rest/api/dogs 添加一個小狗狗
GET /rest/api/editDogs/:dog_id --> PUT /rest/api/dogs/:dog_id 修改一個小狗狗 GET /rest/api/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 刪除一個小狗狗
REST很好地利用了HTTP本身就有的一些特征,如HTTP動詞、HTTP狀態碼、HTTP報頭等等
REST API 是基於 HTTP的,所以你的API應該去使用 HTTP的一些標准。這樣所有的HTTP客戶端(如瀏覽器)才能夠直接理解你的API(當然還有其他好處,如利於緩存等等)。REST 實際上也非常強調應該利用好 HTTP本來就有的特征,而不是只把 HTTP當成一個傳輸層這么簡單了。
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: xxx
{
"url" : "/api/categories/1", "label" : "Food", "items_url" : "/api/items?category=1", "brands" : [ { "label" : "友臣", "brand_key" : "32073", "url" : "/api/brands/32073" }, { "label" : "樂事", "brand_key" : "56632", "url" : "/api/brands/56632" } ... ] }
看這個響應,包含了http里面的狀態碼等信息。還會有http的一些報頭。
Authorization 認證報頭
Cache-Control 緩存報頭
Cnotent-Type 消息體類型報頭
2. REST 系統的特征
(1)客戶-服務器(Client-Server),提供服務的服務器和使用服務的客戶需要被隔離對待。
(2)無 狀態(Stateless),來自客戶的每一個請求必須包含服務器處理該請求所需的所有信息。換句話說,服務器端不能存儲來自某個客戶的某個請求中的信息,並在該客戶的其他請求中使用。
(3)可緩存(Cachable),服務器必須讓客戶知道請求是否可以被緩存。(Ross:更詳細解釋請參考 理解本真的REST架構風格 以及 StackOverflow 的這個問題 中對緩存的解釋。)
(4)分層系統(Layered System),服務器和客戶之間的通信必須被這樣標准化:允許服務器和客戶之間的中間層(Ross:代理,網關等)可以代替服務器對客戶的請求進行回應,而且這些對客戶來說不需要特別支持。
(5)統一接口(Uniform Interface),客戶和服務器之間通信的方法必須是統一化的。(Ross:GET,POST,PUT.DELETE, etc)
(6)支持按需代碼(Code-On-Demand,可選),服務器可以提供一些代碼或者腳本(Ross:Javascrpt,flash,etc)並在客戶的運行環境中執行。這條准則是這些准則中唯一不必必須滿足的一條。(Ross:比如客戶可以在客戶端下載腳本生成密碼訪問服務器。)
其中重點對(2)中內容進行說明:
GET(SELECT):從服務器取出資源(一項或多項)。
POST(CREATE):在服務器新建一個資源。
PUT(UPDATE):在服務器更新資源(客戶端提供完整資源數據)。
PATCH(UPDATE):在服務器更新資源(客戶端提供需要修改的資源數據)。
DELETE(DELETE):從服務器刪除資源。
二、為什么要使用RESTful
http是目前在互聯網上使用最多的協議,沒有之一。
可是http的創始人一直都覺得,在過去10幾年來,所有的人都在錯誤的使用Http.這句話怎么說呢?
如果說你要刪除一個數據,以往的做法通常是 delete/{id}
如果你要更新一個數據,可能是Post數據放Body,然后方法是 update/{id}, 或者是artichle/{id}?method=update
這種做法讓Roy Fielding很暴燥,他覺得這個世界不該這樣的,所有的人都在誤解而且在嚴重錯誤的誤解Http的設計初衷,好比是發明了火葯卻只用它來做煙花爆竹。
那么正確的使用方式是什么呢?如果你要看Rest各種特性,你恐怕真的很難理解Rest,但是如果你看錯誤的使用http的人倒底兒了哪些錯,什么是Rest就特別容易理解了。
常見使用錯誤一:混亂
一萬個人心里有一萬個Url的命名規則,Url是統一資源定位符,重點是資源。而很多人卻把它當成了萬金油,每一個獨立的虛擬的網頁都可以隨意使用,各種操作都能夠迭加。這是混亂的來源之一。
比如:
https://localhost:8080/myweb/getUserById?id=1 https://localhost:8080/myweb/user/getById?id=1 https://localhost:8080/myweb/x/y?id=1
常見使用錯誤二:貪婪
有狀態和無狀態全部混在一起。特別是在購物車或者是登錄的應用中,經常刷新就丟失帶來的用戶體驗簡直棒棒噠。每一個請求並不能單獨的響應一些功能,很多的功能混雜在一起里。這是人性貪婪的本質,也是各種Hack的起源,只要能夠把問題解決掉,總會有人用他認為最方便的方式去解決問題,比如說汽車門把手壞掉了直接系根繩子當把手,emmmm這樣確實很棒啊。
常見使用錯誤三:無序
返回的結果往往是很隨意,各種錯誤信息本來就是用Http的狀態碼構成的,可是很多人還是喜歡把錯誤信息返回在返回值中。最常見的就是Code和Message,當然對於這一點,我個人是保留疑問的,我的觀點是,Http本身的錯誤和服務器的內部錯誤還是需要在不斷層面分開的,不能混在一起。可是在大神眼里並非如此。
那么怎么解決這些問題呢?
強迫症患者的福音就是先頒規則,第一個規則就是明確Url是什么,該怎么用。就是所有的Url本質來講,都應該是一種資源。一個獨立的Url地址,就是對應一個獨一無二的資源。怎么樣?這種感覺是不是棒棒噠?一個冰淇淋,一個老師,一間房子,在Url上對應的都是一個資源,不會有多余的Url跟他對應,也不會表示有多個Url地址
注意,這里點的是Url地址,並不是單獨的參數,他就是一個/room/{room_id}這樣的東西,舉個栗子,/room/3242 這就表示3242號房間。這是一個清爽的世界啊,你想想,之前的Url是什么都要,我開房,可能是/open/room/3242 我要退房可能是/exit/3242/room,我要打理房間,可能是room/3242?method=clean.夠了!這些亂七八糟的東西全夠了,讓世界回歸清爽的本質,一間房,就是/room/3242 沒有別的Url地址了。
在過去的混亂世界里,經常用的就是Get和Post。如果不是因為Get不支持大數據傳輸,我想連Post都不會有人使用。(想像一下Roy Fielding在憤怒的對着電腦屏幕喊,Http的Method一共有八個,你們為毛只逮着Get一只羊的毛薅薅薅薅薅)。
而對資源最常見的操作是什么?CRUD,對不對,就是創建,讀,更新,刪除。再看Http的Method?是不是非常完美?其實也怪Fielding老爺子一開始命名不准確,如果剛開始就是把Get方法叫做Read,Put方法叫做Update,Post叫做Create這該多好。。。
你用一個Get,大家又發現沒什么限制沒什么所謂,又很難理解Put和Post的差別,法無禁止即可為啊,呃,老爺子不要瞪我,我瞎說的。總之,這四種方法夠不夠你浪?你有本身找出來更多的對資源的操作來啊,我還有4個Method沒用過呢。如果這4個真的不夠了,有什么問題,大不了我再重新更改http協議啊。其實簡單說,對於Rest理解到這里就夠了。后續的東西,都是在這一條基礎上空想出來的,比強迫症更強迫症,當然,無狀態我是百分百支持的。以上的各種表述可能不太准確,也純屬是我的意淫和各種小道資料,並未考據,但是憑良心講,我是早就看不慣黑暗年代里的Url命名風格了,所以當時最早接觸到Rest的時候,瞬間就找到了真愛,我靠,這不就是我一直想要的答案嗎?但是我一直想的僅僅是命名規范,從來沒有把自己的思考角度放在一個url就是一個資源,所有的操作都是對資源的更改而言的角度上啊。所以你能理解到的程度,更多的就是在於你要弄清楚你要解決的什么問題,如果你的問題只是理解Rest,恐怕你很理解,如果你的問題是怎么解決Url混亂的問題,你反而很快能弄懂了~
三、RESTful實戰
1. 新建Spring Boot項目




創建后項目結構如下:

2. pom.xml文件添加依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.wxc</groupId> <artifactId>restful-test</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <!--Spring Boot 項目默認的編譯版本是 1.6,如果我們想使用其他的編譯版本我們就需要在 pom.xml 文件中定義一個變量--> <java.version>1.8</java.version> </properties> <dependencies> <!-- 加入web開發的支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <!--添加模板引擎(freemarker)依賴包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> </dependencies> <build> <plugins> <!-- maven的編譯插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!-- 沒有該配置,devtools 不生效 --> <fork>true</fork> <addResources>true</addResources> </configuration> </plugin> </plugins> </build> </project>
3. 編寫測試類
com.wxc.test.controller包下創建UserController.java
package com.wxc.test.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @RequestMapping(value = "/user", method = RequestMethod.POST) public String addUser( String user) { System.out.println("開始新增..."); return "開始新增..."+"傳過來參數為:"+user; } @RequestMapping(value = "/user", method = RequestMethod.PUT) public boolean updateUser( String user) { System.out.println("開始更新..."); return true; } @RequestMapping(value = "/user", method = RequestMethod.DELETE) public boolean delete(@RequestParam(value = "userName", required = true) int userId) { System.out.println("開始刪除..."); return true; } @RequestMapping(value = "/user", method = RequestMethod.GET) public String findByUserName(@RequestParam(value = "userName", required = true) String userName) { System.out.println("開始查詢..."); return "開始查詢..."+"傳過來的參數為:"+userName; } @RequestMapping(value = "/userAll", method = RequestMethod.GET) public String findByUserAge() { System.out.println("開始查詢所有數據..."); return "開始查詢所有數據..."; } }
4. 創建項目啟動類
com.wxc.test包下創建Application.java
package com.wxc.test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { //入口運行類 SpringApplication.run(Application.class, args); } }
創建后項目結構如下:

5. 運行項目並訪問




可以看到,接口訪問方式必須和指定的一致,比如POST無法通過GET進行請求
四、項目源碼下載
鏈接:https://pan.baidu.com/s/1QYlIadtgxUZ7g0QyTwZlaA
提取碼:rg60