RESTful API 編寫規范

基于一些不錯的RESTful開發組件,可以快速的開發出不錯的RESTful API,但如果不了解開發規范的、健壯的RESTful API的基本面,即便優秀的RESTful開發組件擺在面前,也無法很好的理解和使用。下文Gevin結合自己的實踐經驗,整理了從零開始開發RESTful API的核心要點,完善的RESTful開發組件基本都會包含全部或大部分要點,對于支持不夠到位的要點,我們也可以自己寫代碼實現。

Outline

  • Request 和 Response
  • Serialization 和 Deserialization
  • Validation
  • Authentication 和 Permission
  • CORS
  • URL Rules

1. Request 和 Response

RESTful API的開發和使用,無非是客戶端向服務器發請求(request),以及服務器對客戶端請求的響應(response)。本真RESTful架構風格具有統一接口的特點,即,使用不同的http方法表達不同的行為:

  • GET(SELECT):從服務器取出資源(一項或多項)
  • POST(CREATE):在服務器新建一個資源
  • PUT(UPDATE):在服務器更新資源(客戶端提供完整資源數據)
  • PATCH(UPDATE):在服務器更新資源(客戶端提供需要修改的資源數據)
  • DELETE(DELETE):從服務器刪除資源

客戶端會基于GET方法向服務器發送獲取數據的請求,基于PUTPATCH方法向服務器發送更新數據的請求等,服務端在設計API時,也要按照相應規范來處理對應的請求,這點現在應該已經成為所有RESTful API的開發者的共識了,而且各web框架的request類和response類都很強大,具有合理的默認設置和靈活的定制性,Gevin在這里僅準備強調一下響應這些request時,常用的Response要包含的數據和狀態碼(status code),不完善的內容,歡迎大家補充:

  • GET,?PUTPATCH請求成功時,要返回對應的數據,及狀態碼200,即SUCCESS
  • POST創建數據成功時,要返回創建的數據,及狀態碼201,即CREATED
  • DELETE刪除數據成功時,不返回數據,狀態碼要返回204,即NO CONTENT
  • GET?不到數據時,狀態碼要返回404,即NOT FOUND
  • 任何時候,如果請求有問題,如校驗請求數據時發現錯誤,要返回狀態碼?400,即BAD REQUEST
  • 當API 請求需要用戶認證時,如果request中的認證信息不正確,要返回狀態碼?401,即NOT AUTHORIZED
  • 當API 請求需要驗證用戶權限時,如果當前用戶無相應權限,要返回狀態碼?403,即FORBIDDEN

最后,關于Request 和 Response,不要忽略了http header中的Content-Type。以json為例,如果API要求客戶端發送request時要傳入json數據,則服務器端僅做好json數據的獲取和解析即可,但如果服務端支持多種類型數據的傳入,如同時支持json和form-data,則要根據客戶端發送請求時header中的Content-Type,對不同類型是數據分別實現獲取和解析;如果API響應客戶端請求后,需要返回json數據,需要在header中添加Content-Type=application/json

2. Serialization 和 Deserialization

Serialization 和 Deserialization即序列化和反序列化。RESTful API以規范統一的格式作為數據的載體,常用的格式為jsonxml,以json格式為例,當客戶端向服務器發請求時,或者服務器相應客戶端的請求,向客戶端返回數據時,都是傳輸json格式的文本,而在服務器內部,數據處理時基本不用json格式的字符串,而是native類型的數據,最典型的如類的實例,即對象(object),json僅為服務器和客戶端通信時,在網絡上傳輸的數據的格式,服務器和客戶端內部,均存在將json轉為native類型數據和將native類型數據轉為json的需求,其中,將native類型數據轉為json即為序列化,將json轉為native類型數據即為反序列化。雖然某些開發語言,如Python,其原生數據類型listdict能輕易實現序列化和反序列化,但對于復雜的API,內部實現時總會以對象作為數據的載體,因此,確保序列化和反序列化方法的實現,是開發RESTful API最重要的一步準備工作

題外話,序列化和反序列化的便捷,造就了RESTful API跨平臺的特點,使得REST取代RPC成為Web Service的主流

序列化和反序列化是RESTful API開發中的一項硬需求,所以幾乎每一種常用的開發語言都會有一個或多個優秀的開源庫,來實現序列化和反序列化,因此,我們在開發RESTful API時,沒必要制造重復的輪子,選一個好用的庫即可,如python中的marshmallow,如果基于Django開發,Django REST Framework中的serializer即可。

3. Validation

Validation即數據校驗,是開發健壯RESTful API中另一個重要的一環。仍以json為例,當客戶端向服務器發出post,?putpatch請求時,通常會同時給服務器發送json格式的相關數據,服務器在做數據處理之前,先做數據校驗,是最合理和安全的前后端交互。如果客戶端發送的數據不正確或不合理,服務器端經過校驗后直接向客戶端返回400錯誤及相應的數據錯誤信息即可。常見的數據校驗包括:

  • 數據類型校驗,如字段類型如果是int,那么給字段賦字符串的值則報錯
  • 數據格式校驗,如郵箱或密碼,其賦值必須滿足相應的正則表達式,才是正確的輸入數據
  • 數據邏輯校驗,如數據包含出生日期和年齡兩個字段,如果這兩個字段的數據不一致,則數據校驗失敗

以上三種類型的校驗,數據邏輯校驗最為復雜,通常涉及到多個字段的配合,或者要結合用戶和權限做相應的校驗。Validation雖然是RESTful API 編寫中的一個可選項,但它對API的安全、服務器的開銷和交互的友好性而言,都具有重要意義,因此,Gevin建議,開發一套完善的RESTful API時,Validation的實現必不可少。

4. Authentication 和 Permission

Authentication指用戶認證,Permission指權限機制,這兩點是使RESTful API 強大、靈活和安全的基本保障。

常用的認證機制是Basic AuthOAuth,RESTful API 開發中,除非API非常簡單,且沒有潛在的安全性問題,否則,認證機制是必須實現的,并應用到API中去。Basic Auth非常簡單,很多框架都集成了Basic Auth的實現,自己寫一個也能很快搞定,OAuth目前已經成為企業級服務的標配,其相關的開源實現方案非常豐富(更多)。

我在《RESTful 架構風格概述》中,對認證機制做了更加詳細的描述,有興趣的同學不妨閱讀相關章節。

權限機制是對API請求更近一步的限制,只有通過認證的用戶符合權限要求,才能訪問API。權限機制的具體實現通常依賴于系統的業務邏輯和應用場景,generally speaking,常用的權限機制主要包含全局型的和對象型的,全局型的權限機制,主要指通過為用戶賦予權限,或者為用戶賦予角色或劃分到用戶組,然后為角色或用戶組賦予權限的方式來實現權限控制,對象型的權限機制,主要指權限控制的顆粒度在object上,用戶對某個具體對象的訪問、修改、刪除或其行為,要單獨在該對象上為用戶賦予相關權限來實現權限控制。

全局型的權限機制容易理解,實現也簡單,有很多開源庫可做備選方案,不少完善的web開發框架,也會集成相關的權限邏輯,object permission 相對難復雜一點,但也有很多典型的應用場景,如多人博客系統中,作者對自己文章的編輯權限即為object permission,其對應的開源庫也有很多。

注: 我寫過一篇《Django權限機制的實現》,Django 開發者可做延伸閱讀。

開發一套完整的RESTful API,權限機制必須納入考慮范圍,雖然權限機制的具體實現依賴于業務,權限機制本身,是有典型的模式存在的,需要開發者掌握基本的權限機制實現方案,以便隨時應用到API中去。

5. CORS

CORS即Cross-origin resource sharing,在RESTful API開發中,主要是為js服務的,解決javascript 調用 RESTful API時的跨域問題。

由于固有的安全機制,js的跨域請求時是無法被服務器成功響應的。現在前后端分離日益成為web開發主流方式的大趨勢下,后臺逐漸趨向指提供API服務,為各客戶端提供數據及相關操作,而網站的開發全部交給前端搞定,網站和API服務很少部署在同一臺服務器上并使用相同的端口,js的跨域請求時普遍存在的,開發RESTful API時,通常都要考慮到CORS功能的實現,以便js能正常使用API。

目前各主流web開發語言都有很多優秀的實現CORS的開源庫,我們在開發RESTful API時,要注意CORS功能的實現,直接拿現有的輪子來用即可。

更多關于CORS的介紹,有興趣的同學可以查看阮一峰老師的跨域資源共享 CORS 詳解

6. URL Rules

RESTful API 是寫給開發者來消費的,其命名和結構需要有意義。因此,在設計和編寫URL時,要符合一些規范。Url rules 可以單獨寫一篇博客來詳細闡述,本文只列出一些關鍵點。

6.1 Version your API

規范的API應該包含版本信息,在RESTful API中,最簡單的包含版本的方法是將版本信息放到url中,如:

/api/v1/posts/
/api/v1/drafts//api/v2/posts/
/api/v2/drafts/

另一種優雅的做法是,使用HTTP header中的accept來傳遞版本信息,這也是GitHub API 采取的策略。

6.2 Use nouns, not verbs

RESTful API 中的url是指向資源的,而不是描述行為的,因此設計API時,應使用名詞而非動詞來描述語義,否則會引起混淆和語義不清。即:

# Bad APIs
/api/getArticle/1/
/api/updateArticle/1/
/api/deleteArticle/1/

上面四個url都是指向同一個資源的,雖然一個資源允許多個url指向它,但不同的url應該表達不同的語義,上面的API可以優化為:

# Good APIs
/api/Article/1/

article 資源的獲取、更新和刪除分別通過?GET,?PUT?和?DELETE方法請求API即可。試想,如果url以動詞來描述,用PUT方法請求?/api/deleteArticle/1/?會感覺多么不舒服。

6.3?GET?and?HEAD?should always be safe

RFC2616已經明確指出,GETHEAD方法必須始終是安全的。例如,有這樣一個不規范的API:


# The following api is used to delete articles
# [GET]
/api/deleteArticle?id=1

試想,如果搜索引擎訪問了上面url會如何?

6.4 Nested resources routing

如果要獲取一個資源子集,采用?nested routing?是一個優雅的方式,如,列出所有文章中屬于Gevin編寫的文章:

# List Gevin's articles
/api/authors/gevin/articles/

獲取資源子集的另一種方式是基于filter(見下面章節),這兩種方式都符合規范,但語義不同:如果語義上將資源子集看作一個獨立的資源集合,則使用?nested routing?感覺更恰當,如果資源子集的獲取是出于過濾的目的,則使用filter更恰當。

至于編寫RESTful API時到底應采用哪種方式,則仁者見仁,智者見智,語義上說的通即可。

6.5 Filter

對于資源集合,可以通過url參數對資源進行過濾,如:

# List Gevin's articles
/api/articles?author=gevin

分頁就是一種最典型的資源過濾。

6.6 Pagination

對于資源集合,分頁獲取是一種比較合理的方式。如果基于開發框架(如Django REST Framework),直接使用開發框架中的分頁機制即可,如果是自己實現分頁機制,Gevin的策略是:

返回資源集合是,包含與分頁有關的數據如下:

{"page": 1,            # 當前是第幾頁"pages": 3,           # 總共多少頁"per_page": 10,       # 每頁多少數據"has_next": true,     # 是否有下一頁數據"has_prev": false,    # 是否有前一頁數據"total": 27           # 總共多少數據
}

當想API請求資源集合時,可選的分頁參數為:

參數 含義
page 當前是第幾頁,默認為1
per_page 每頁多少條記錄,默認為系統默認值

另外,系統內還設置一個per_page_max字段,用于標記系統允許的每頁最大記錄數,當per_page值大于?per_page_max?值時,每頁記錄條數為?per_page_max

6.7 Url design tricks

(1)Url是區分大小寫的,這點經常被忽略,即:

  • /Posts
  • /posts

上面這兩個url是不同的兩個url,可以指向不同的資源

(2)Back forward Slash (/)

目前比較流行的API設計方案,通常建議url以/作為結尾,如果API?GET請求中,url不以/結尾,則重定向到以/結尾的API上去(這點現在的web框架基本都支持),因為有沒有?/,也是兩個url,即:

  • /posts/
  • /posts

這也是兩個不同的url,可以對應不同的行為和資源

(3)連接符?-?和 下劃線?_

RESTful API 應具備良好的可讀性,當url中某一個片段(segment)由多個單詞組成時,建議使用?-?來隔斷單詞,而不是使用?_,即:

# Good
/api/featured-post/# Bad
/api/featured_post/

這主要是因為,瀏覽器中超鏈接顯示的默認效果是,文字并附帶下劃線,如果API以_隔斷單詞,二者會重疊,影響可讀性。

總結

編寫本文的初衷,是為了整理一套從零開始編寫規范、安全的RESTful API的基本思路。本文介紹了開發RESTful API時,要考慮的基本內容,對于類似Flask這種天生支持RESTful風格的web框架,不依賴其他RESTful第三方庫開發RESTful 服務時,可以從本文內容入手;不少強大的RESTful 庫,雖然其相關接口基本涵蓋了本文的全部或大部分內容,但本文的總結,相信對這些庫的理解和使用也是有幫助的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247731.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247731.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247731.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python2與Python3的區別

Python2與Python3的區別 1) 核心類差異 Python3 對 Unicode 字符的原生支持。 Python2 中使用 ASCII 碼作為默認編碼方式導致 string 有兩種類型 str 和 unicode,Python3 只 支持 unicode 的 string。Python2 和 Python3 字節和字符對應關系為: python2p…

JavaScript-內存空間

深入了解js這門語言后,才發現它有著諸多眾所周知的難點(例如:閉包、原型鏈、內存空間等)。有的是因為js的設計缺陷導致的,而有的則是js的優點。不管如何,總需要去學會它們,在學習過程中我覺得只…

java類的結構1: 屬性 —(11)

類的設計中,兩個重要結構之一:屬性 對比:屬性 vs 局部變量 1.相同點: 1.1 定義變量的格式:數據類型 變量名 變量值1.2 先聲明,后使用1.3 變量都其對應的作用域 2.不同點: 2.1 在類中聲明的…

GXU - 7D - 區間求和 - 前綴和

https://oj.gxu.edu.cn/contest/7/problem/D 描述 有一個所有元素皆為0的數組A,有兩種操作: 1 l r x表示將A區間[l,r]內所有數加上x; 2 l r表示將A區間[l,r]內從左往右數第i個數加上i; 給出m個操作,請輸出操作結束后A中…

javascript-排序算法

插入排序 算法描述: 1. 從第一個元素開始,該元素可以認為已經被排序 2. 取出下一個元素,在已經排序的元素序列中從后向前掃描 3. 如果該元素(已排序)大于新元素,將該元素移到下一位置 4. 重復步驟 3&am…

DPDK并行計算

參考文獻: 《深入淺出DPDK》 https://www.cnblogs.com/LubinLew/p/cpu_affinity.html ...................................................................... 前言: 處理器提高性能主要是通過兩個途徑,一個是提高IPC(CPU每一時…

Highcharts圖表-ajax-獲取json數據生成圖表

重點說明此代碼是針對一個報表顯示多個項對比顯示。 直接貼代碼&#xff1a;web端 <script type"text/JavaScript" src"js/jQuery/jquery-1.7.2.js"></script> <script type"text/javascript" src"j…

關于RGBDSLAMV2學習、安裝、調試過程

Step&#xff11;&#xff1a;https://github.com/felixendres/rgbdslam_v2/wiki/Instructions-for-Compiling-Rgbdslam-(V2)-on-a-Fresh-Ubuntu-16.04-Install-(Ros-Kinetic)-in-Virtualbox 照著這個instructions安裝好 rgbdslamv2&#xff0c;并且在安裝的過程中&#xff0c;…

Java—List的用法與實例詳解

List特點和常用方法 List是有序、可重復的容器。 有序指的是&#xff1a;List中每個元素都有索引標記。可以根據元素的索引標記&#xff08;在List中的位置&#xff09;訪問元素&#xff0c;從而精確控制這些元素。 可重復指的是&#xff1a;List允許加入重復的元素。更確切地講…

Java—遍歷集合的N種方式總結Collections工具類

遍歷集合的N種方式總結 【示例1】遍歷List方法1&#xff0c;使用普通for循環 for(int i0;i<list.size();i){ //list為集合的對象名 String temp (String)list.get(i); System.out.println(temp); } 【示例2】遍歷List方法2&#xff0c;使用增強for循環(使用泛型定義…

java類的結構2: 方法—(12)

面向對象的特征一&#xff1a;封裝與隱藏 1.為什么要引入封裝性&#xff1f; 我們程序設計追求“高內聚&#xff0c;低耦合”。 高內聚 &#xff1a;類的內部數據操作細節自己完成&#xff0c;不允許外部干涉&#xff1b; 低耦合 &#xff1a;僅對外暴露少量的方法用于使用。…

Docker 環境下部署 redash

環境&#xff1a; centos7 官網&#xff1a;https://redash.io/help/open-source/dev-guide/docker 一、安裝步驟 1、虛擬機安裝 安裝vmware&#xff0c;并安裝centos7 2、安裝docker docker安裝手冊 3、安裝nodejs centos下安裝Nodejs 4、redash安裝 1)、clone git repostory …

List接口常用實現類的特點和底層實現

List接口常用的實現類有3個&#xff1a;ArrayList、LinkedList、Vector。 那么它們的特點和底層實現有哪些呢&#xff1f; ArrayList特點和底層實現 ArrayList底層是用數組實現的存儲。 特點&#xff1a;查詢效率高&#xff0c;增刪效率低&#xff0c;線程不安全。我們一般使用…

java面向對象的特征 —(13)

面向對象的特征一&#xff1a;封裝與隱藏 1.為什么要引入封裝性&#xff1f; 我們程序設計追求“高內聚&#xff0c;低耦合”。 高內聚 &#xff1a;類的內部數據操作細節自己完成&#xff0c;不允許外部干涉&#xff1b; 低耦合 &#xff1a;僅對外暴露少量的方法用于使用。…

null指針

做了一個關于花卉花木的管理操作&#xff0c;后期因為花卉的類型需要顯示在花卉詳情頁面&#xff0c;所以需要兩張表連接。在不寫sql語句的前提下&#xff0c;用了外鍵連接。因為在先前的操作過程中&#xff0c;沒有將外鍵所在字段設置為必填項&#xff0c;導致有一個外鍵字段為…

jquery Ajax請求本地json

1-1-1 json文件內容(item.json) [{"name":"張國立","sex":"男","email":"zhangguoli123.com","url":"./img/1.jpg"},{"name":"張鐵林","sex":"男"…

論文《learning to link with wikipedia》

learning to link with wikipedia 一、本文目標&#xff1a; 如何自動識別非結構化文本中提到的主題&#xff0c;并將其鏈接到適當的Wikipedia文章中進行解釋。 二、主要借鑒論文&#xff1a; Mihalcea and Csomai----Wikify!: linking documents to encyclopedic knowledge 第…

java類的結構:構造器 —(13)

1.構造器&#xff08;或構造方法&#xff09;&#xff1a;Constructor 構造器的作用&#xff1a; 1.創建對象2.初始化對象的信息 2.使用說明&#xff1a; 1.如果沒顯式的定義類的構造器的話&#xff0c;則系統默認提供一個空參的構造器2.定義構造器的格式&#xff1a;權限修…

java面向對象的特征二:繼承性 —(14)

1.為什么要有類的繼承性&#xff1f;(繼承性的好處&#xff09; ① 減少了代碼的冗余&#xff0c;提高了代碼的復用性② 便于功能的擴展③ 為之后多態性的使用&#xff0c;提供了前提 圖示&#xff1a; 2.繼承性的格式&#xff1a; class A extends B{} A:子類、派生類、s…

vuejs怎么在服務器上發布部署

首先VUE 是一個javascript的前端框架&#xff0c;注定了它是運行在瀏覽器里的&#xff0c;對服務器本地沒有任何要求&#xff0c;只要一個靜態文件服務器能通過http訪問到其資源文件就足矣&#xff01;無論你是用apache ,ngnix 就算你要用node 自己實現一個靜態文件服務器&…