一、說明
REST表示 Representational State Transfer(表示性狀態(tài)轉(zhuǎn)換). 它是可以用來設(shè)計(jì)web services的框架,可以被不同的客戶端調(diào)用。 REST是一種架構(gòu)風(fēng)格,其核心是面向資源,REST專門針對(duì)網(wǎng)絡(luò)應(yīng)用設(shè)計(jì)和開發(fā)方式,以降低開發(fā)的復(fù)雜性,提高系統(tǒng)的可伸縮性。
二、REST提出設(shè)計(jì)概念和準(zhǔn)則
1.網(wǎng)絡(luò)上的所有事物都可以被抽象為資源(resource) 2.每一個(gè)資源都有唯一的資源標(biāo)識(shí)(resource identifier),對(duì)資源的操作不會(huì)改變這些標(biāo)識(shí) 3.所有的操作都是無狀態(tài)的 使用簡單的HTTP協(xié)議來實(shí)現(xiàn)調(diào)用,而不是CORBA, RPC 或者 SOAP等負(fù)責(zé)的機(jī)制。
三、RESTful API 設(shè)計(jì)指南
1、概述
網(wǎng)絡(luò)應(yīng)用程序,分為前端和后端兩個(gè)部分。當(dāng)前的發(fā)展趨勢,就是前端設(shè)備層出不窮(手機(jī)、平板、桌面電腦、其他專用設(shè)備......)。 因此,必須有一種統(tǒng)一的機(jī)制,方便不同的前端設(shè)備與后端進(jìn)行通信。 1.協(xié)議 API與用戶的通信協(xié)議,總是使用HTTPs協(xié)議。 2.域名 應(yīng)該盡量將API部署在專用域名之下。 https://api.example.com 如果確定API很簡單,不會(huì)有進(jìn)一步擴(kuò)展,可以考慮放在主域名下。 https://example.org/api/ 3.版本(Versioning) 應(yīng)該將API的版本號(hào)放入U(xiǎn)RL。 https://api.example.com/v1/ 另一種做法是,將版本號(hào)放在HTTP頭信息中,但不如放入U(xiǎn)RL方便和直觀。Github采用這種做法。 4.路徑(Endpoint) 路徑又稱"終點(diǎn)"(endpoint),表示API的具體網(wǎng)址。 在RESTful架構(gòu)中,每個(gè)網(wǎng)址代表一種資源(resource),所以網(wǎng)址中不能有動(dòng)詞,只能有名詞, 而且所用的名詞往往與數(shù)據(jù)庫的表格名對(duì)應(yīng)。 一般來說,數(shù)據(jù)庫中的表都是同種記錄的"集合"(collection),所以API中的名詞也應(yīng)該使用復(fù)數(shù)。 5.HTTP動(dòng)詞 對(duì)于資源的具體操作類型,由HTTP動(dòng)詞表示。 常用的HTTP動(dòng)詞 GET(SELECT):從服務(wù)器取出資源(一項(xiàng)或多項(xiàng))。 POST(CREATE):在服務(wù)器新建一個(gè)資源。 PUT(UPDATE):在服務(wù)器更新資源(客戶端提供改變后的完整資源)。 PATCH(UPDATE):在服務(wù)器更新資源(客戶端提供改變的屬性)。 DELETE(DELETE):從服務(wù)器刪除資源。 動(dòng)詞舉例 GET /zoos:列出所有動(dòng)物園 POST /zoos:新建一個(gè)動(dòng)物園 GET /zoos/ID:獲取某個(gè)指定動(dòng)物園的信息 PUT /zoos/ID:更新某個(gè)指定動(dòng)物園的信息(提供該動(dòng)物園的全部信息) PATCH /zoos/ID:更新某個(gè)指定動(dòng)物園的信息(提供該動(dòng)物園的部分信息) DELETE /zoos/ID:刪除某個(gè)動(dòng)物園 GET /zoos/ID/animals:列出某個(gè)指定動(dòng)物園的所有動(dòng)物 DELETE /zoos/ID/animals/ID:刪除某個(gè)指定動(dòng)物園的指定動(dòng)物 6.過濾信息(Filtering) 如果記錄數(shù)量很多,服務(wù)器不可能都將它們返回給用戶。API應(yīng)該提供參數(shù),過濾返回結(jié)果。 常見的參數(shù) ?limit=10:指定返回記錄的數(shù)量 ?offset=10:指定返回記錄的開始位置。 ?page=2&per_page=100:指定第幾頁,以及每頁的記錄數(shù)。 ?sortby=name&order=asc:指定返回結(jié)果按照哪個(gè)屬性排序,以及排序順序。 ?animal_type_id=1:指定篩選條件 參數(shù)的設(shè)計(jì)允許存在冗余,即允許API路徑和URL參數(shù)偶爾有重復(fù)。 比如,GET /zoo/ID/animals 與 GET /animals?zoo_id=ID 的含義是相同的。 7.狀態(tài)碼(Status Codes) 服務(wù)器向用戶返回的狀態(tài)碼和提示信息 常見的狀態(tài)碼 200 OK - [GET]:服務(wù)器成功返回用戶請(qǐng)求的數(shù)據(jù),該操作是冪等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數(shù)據(jù)成功。 202 Accepted - []:表示一個(gè)請(qǐng)求已經(jīng)進(jìn)入后臺(tái)排隊(duì)(異步任務(wù)) 204 NO CONTENT - [DELETE]:用戶刪除數(shù)據(jù)成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發(fā)出的請(qǐng)求有錯(cuò)誤,服務(wù)器沒有進(jìn)行新建或修改數(shù)據(jù)的操作,該操作是冪等的。 401 Unauthorized - []:表示用戶沒有權(quán)限(令牌、用戶名、密碼錯(cuò)誤)。 403 Forbidden - [] 表示用戶得到授權(quán)(與401錯(cuò)誤相對(duì)),但是訪問是被禁止的。 404 NOT FOUND - []:用戶發(fā)出的請(qǐng)求針對(duì)的是不存在的記錄,服務(wù)器沒有進(jìn)行操作,該操作是冪等的。 406 Not Acceptable - [GET]:用戶請(qǐng)求的格式不可得(比如用戶請(qǐng)求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用戶請(qǐng)求的資源被永久刪除,且不會(huì)再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),發(fā)生一個(gè)驗(yàn)證錯(cuò)誤。 500 INTERNAL SERVER ERROR - [*]:服務(wù)器發(fā)生錯(cuò)誤,用戶將無法判斷發(fā)出的請(qǐng)求是否成功。 8.錯(cuò)誤處理(Error handling) 如果狀態(tài)碼是4xx,就應(yīng)該向用戶返回出錯(cuò)信息。一般來說,返回的信息中將error作為鍵名,出錯(cuò)信息作為鍵值即可。 eg: { error: "Invalid API key" } 9.返回結(jié)果 針對(duì)不同操作,服務(wù)器向用戶返回的結(jié)果應(yīng)該符合以下規(guī)范。 GET /collection:返回資源對(duì)象的列表(數(shù)組) GET /collection/resource:返回單個(gè)資源對(duì)象 POST /collection:返回新生成的資源對(duì)象 PUT /collection/resource:返回完整的資源對(duì)象 PATCH /collection/resource:返回完整的資源對(duì)象 DELETE /collection/resource:返回一個(gè)空文檔 10.Hypermedia API RESTful API最好做到Hypermedia,即返回結(jié)果中提供鏈接,連向其他API方法,使得用戶不查文檔,也知道下一步應(yīng)該做什么。 11.其他 (1)API的身份認(rèn)證應(yīng)該使用OAuth 2.0框架。 (2)服務(wù)器返回的數(shù)據(jù)格式,應(yīng)該盡量使用JSON,避免使用XML。
2、返回?cái)?shù)據(jù)類型
盡管沒有限制必須返回的類型,但是一般基于Web services的Rest返回JSON或者XML作為響應(yīng)。 客戶端可以指定(使用HTTP Accept header)他們想要的資源類型嗎,服務(wù)器返回需要的資源。 指明資源的Content-Type。
3、REST API
GET 方式請(qǐng)求 /api/user/ 返回用戶列表 GET 方式請(qǐng)求 /api/user/1返回id為1的用戶 POST 方式請(qǐng)求 /api/user/ 通過user對(duì)象的JSON 參數(shù)創(chuàng)建新的user對(duì)象 PUT 方式請(qǐng)求 /api/user/3 更新id為3的發(fā)送json格式的用戶對(duì)象 DELETE 方式請(qǐng)求/api/user/4刪除 ID為 4的user對(duì)象 DELETE 方式請(qǐng)求/api/user/刪除所有user
4、Spring4 Rest 注解
@RestController 此注解避免了每個(gè)方法都要加上@ResponseBody注解。也就是說@RestController 自己戴上了 @ResponseBody注解,看以看作是 @Controller 和 @ResponseBody的結(jié)合體。 @RestController,表明該類的每個(gè)方法返回對(duì)象而不是視圖。 它實(shí)際就是@Controller和@ResponseBody混合使用的簡寫方法。 @RequestBody 如果方法參數(shù)被 @RequestBody注解,Spring將綁定HTTP請(qǐng)求體到那個(gè)參數(shù)上。 如果那樣做,Spring將根據(jù)請(qǐng)求中的ACCEPT或者 Content-Type header(私下)使用 HTTP Message converters 來將http請(qǐng)求體轉(zhuǎn)化為domain對(duì)象。 @ResponseBody 如果方法加上了@ResponseBody注解,Spring返回值到響應(yīng)體。如果這樣做的話,Spring將根據(jù)請(qǐng)求中的 Content-Type header(私下)使用 HTTP Message converters 來將domain對(duì)象轉(zhuǎn)換為響應(yīng)體。 @ResponseBody的作用是將返回的對(duì)象放入響應(yīng)消息體中 ResponseEntity 是一個(gè)真實(shí)數(shù)據(jù).它代表了整個(gè) HTTP 響應(yīng)(response). 它的好處是你可以控制任何對(duì)象放到它內(nèi)部。 可以指定狀態(tài)碼、頭信息和響應(yīng)體。它包含你想要構(gòu)建HTTP Response 的信息。 @PathVariable 此注解意味著一個(gè)方法參數(shù)應(yīng)該綁定到一個(gè)url模板變量[在'{}'里的一個(gè)]中 MediaType 帶著 @RequestMapping 注解,通過特殊的控制器方法你可以額外指定,MediaType來生產(chǎn)或者消耗。
5、REST測試
POSTMAN測試 使用RestTemplate編寫測試用例 HTTP GET : getForObject, getForEntity HTTP PUT : put(String url, Object request, String…urlVariables) HTTP DELETE : delete HTTP POST : postForLocation(String url, Object request, String… urlVariables), postForObject(String url, Object request, ClassresponseType, String… uriVariables) HTTP HEAD : headForHeaders(String url, String… urlVariables) HTTP OPTIONS : optionsForAllow(String url, String… urlVariables) HTTP PATCH and others : exchange execute
6、others
REST的優(yōu)勢 由于REST強(qiáng)制所有的操作都必須是stateless的,這就沒有上下文的約束,如果做分布式,集群都不需要考慮上下文和會(huì)話保持的問題。極大的提高系統(tǒng)的可伸縮性 Webservice選擇 SOAP偏向于面向活動(dòng),有嚴(yán)格的規(guī)范和標(biāo)準(zhǔn),包括安全,事務(wù)等各個(gè)方面的內(nèi)容, 同時(shí)SOAP強(qiáng)調(diào)操作方法和操作對(duì)象的分離,有WSDL文件規(guī)范和XSD文件分別對(duì)其定義。 REST強(qiáng)調(diào)面向資源 只要我們要操作的對(duì)象可以抽象為資源即可以使用REST架構(gòu)風(fēng)格。 REST ful 應(yīng)用問題 是否使用REST就需要考慮資源本身的抽象和識(shí)別是否困難,如果本身就是簡單的類似增刪改查的業(yè)務(wù)操作,那么抽象資源就比較容易,而對(duì)于復(fù)雜的業(yè)務(wù)活動(dòng)抽象資源并不是一個(gè)簡單的事情。比如校驗(yàn)用戶等級(jí),轉(zhuǎn)賬,事務(wù)處理等,這些往往并不容易簡單的抽象為資源。 其次如果有嚴(yán)格的規(guī)范和標(biāo)準(zhǔn)定義要求,而且前期規(guī)范標(biāo)準(zhǔn)需要指導(dǎo)多個(gè)業(yè)務(wù)系統(tǒng)集成和開發(fā)的時(shí)候,SOAP風(fēng)格由于有清晰的規(guī)范標(biāo)準(zhǔn)定義是明顯有優(yōu)勢的。我們可以在開始和實(shí)現(xiàn)之前就嚴(yán)格定義相關(guān)的接口方法和接口傳輸數(shù)據(jù)。
四、示例
package com.qianfeng.controller;
import com.qianfeng.bean.User;
import com.qianfeng.service.IUserSevice;
import com.qianfeng.service.impl.UserServiceImpl;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class UserController {
private IUserSevice userSevice = new UserServiceImpl();
@GetMapping("/users")
public List<User> getAllUsers(){
return userSevice.getAllUsers();
}
@GetMapping("/users/{uid}")
public User getUserById(@PathVariable int uid){
return userSevice.getUserById(uid);
}
@DeleteMapping("/users/{uid}")
public boolean deleteUserById(@PathVariable int uid){
return userSevice.deleteUser(uid);
}
@PostMapping("/users")
public boolean addUser(User user){
return userSevice.addUser(user);
}
@PutMapping("/users")
public boolean updateUserById(User user){
return userSevice.updateUser(user);
}
}