<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    paulwong

    RESTful 最佳實(shí)踐

    除了傳統(tǒng)對于遠(yuǎn)程調(diào)用的需求,近來移動開發(fā)對于api的規(guī)范化需要,restful作為一個(gè)流行的接口調(diào)用方式,值得深入了解。

    聲明 本文屬于轉(zhuǎn)載:原文

    此文為實(shí)踐總結(jié),是自己在實(shí)踐過程中積累的經(jīng)驗(yàn)和"哲學(xué)"。部分內(nèi)容參考相關(guān)資料,參考內(nèi)容請看尾頁。建議對RESTful有一定了解者閱讀!

    "哲學(xué)"

    • 不要為了RESTful而RESTful

    • 在能表達(dá)清楚的情況下,簡單就是美

    接口路徑設(shè)計(jì)

    接口設(shè)計(jì)原則

    URI指向的是唯一的資源對象
    示例: 指向ID為cloud.mario的Account對象

    GET http://~/$version/accounts/cloud.mario

    URI可以隱式指向唯一的集合列表

    示例: 隱式地指向trades list 集合

    GET http://~/$version/trades/(list)

    等同于

    GET http://~/$version/trades

    聚合資源必須通過父級資源操作

    示例ProfileUser的聚合資源,User有一個(gè)唯一且私有的Profile資源,只能通過User操作Profile。

    更新user_id為123456的Profile資源 PUT http://~/$version/users/123456/profiles Request Body:{       "full_name": "cloud.mario",       "state": "Beijing",       "title": "一個(gè)開發(fā)者"   }

    Http Methods

    HTTP OperationDescription
    GET獲取,查找
    POST新增創(chuàng)建
    PUT更新
    PATCH部分更新
    DELETE刪除

    URL組成

    1. 網(wǎng)絡(luò)協(xié)議(HTTP, HTTPS)

    2. 服務(wù)器地址

    3. 版本

    4. 接口名稱

    5. ?參數(shù)列表

    GET https://github.com/v1/trades

    為什么需要版本?

    當(dāng)服務(wù)被更多其他系統(tǒng)使用的時(shí)候,服務(wù)的可用性和上下兼容變得至關(guān)重要。被外部系統(tǒng)依賴的服務(wù)在升級時(shí)是一個(gè)非常麻煩的事情,既要發(fā)布新的接口,又要保留舊的接口留出時(shí)間讓調(diào)用者去升級。在URL中加入Version標(biāo)示能很好地解決上下兼容(新老版本共存)問題。

    示例1: URL中新增了Path parameter

    v1版本

    GET http://~/v1/trades?user_id=123456

    v2版本

    GET http://~/v2/:user_id/trades

    示例1中的user_id參數(shù)在v2版本被加入到path parameter中,使用$version保證了v1v2接口的共存。

    示例2: 數(shù)據(jù)接口發(fā)生變化

    v1版本

    GET http://~/v1/accounts/cloud.mario Response Body: {       "user_name": "cloud.mario",       "e_mail": "cloud.mario@gmail.com",       "state": "Beijign",       "title": "一個(gè)開發(fā)者" }

    v2版本

    GET http://~/v2/accounts/cloud.mario Response Body: {           "user_name": "cloud.mario",           "e_mail": "cloud.mario@gmail.com",           "profile":      {                   "state": "Beijign",                   "title": "一個(gè)開發(fā)者"              } }

    示例2中的接口返回?cái)?shù)據(jù)結(jié)構(gòu)已經(jīng)發(fā)生了變化。使用$version保證了v1v2接口的共存。

    URL定義限制

    1. 不使用大寫字母

    2. 使用中線-代替下劃線_

    3. 參數(shù)列表應(yīng)該被encode過

    接口分類

    資源對象的CURD操作

    GET http://~/$version/trades                 #獲取trades列表 GET http://~/$version/trades/:id            #根據(jù)id獲取單個(gè)trade POST http://~/$version/trades               #創(chuàng)建trade PUT http://~/$version/trades/:id             #根據(jù)id更新trade PATCH http://~/$version/trades/:id        #根據(jù)id部分更新trade DELETE http://~/$version/trades/:id      #根據(jù)id刪除trade

    系統(tǒng)設(shè)置

    使用settings標(biāo)識,根據(jù)服務(wù)的屬性選擇http方法。

    http://~/settings/$version/server-name

    示例1: 搜索

    GET http://~/services/$version/search?q=filter&category=file

    示例2: 任務(wù)隊(duì)列操作

    PUT http://~/services/$version/queued/jobs          往任務(wù)隊(duì)列里面添加一個(gè)新的任務(wù) DELETE http://~/services/$version/queued/jobs/:id   根據(jù)id刪除任務(wù)

    示例3: 更改界面語言環(huán)境

    PUT http://~/settings/$version/gui/lang {    "lang": "zh-CN"}

    為什么需要區(qū)分?

    1. Microservices

    Microservices是一個(gè)全新的概念,它主要的觀點(diǎn)是將一個(gè)大型的服務(wù)系統(tǒng)分解成多個(gè)微型系統(tǒng)。每個(gè)微型系統(tǒng)都能獨(dú)立工作,并且提供各種不同的服務(wù)。獨(dú)立運(yùn)行的特點(diǎn)使微型系統(tǒng)之間不會產(chǎn)生相互影響,其中的一個(gè)微型系統(tǒng)宕機(jī)并不會牽連到其他的微型系統(tǒng)。這種架構(gòu)使分布式系統(tǒng)的節(jié)點(diǎn)數(shù)量大大提升。因?yàn)镽ESTful服務(wù)是無狀態(tài)的,所以這種分解并不會帶來狀態(tài)共享的問題。

    1. 路由規(guī)則(邏輯)

    當(dāng)我們需要對不同屬性的接口做路由規(guī)則的時(shí)候,按功能劃分接口是一個(gè)很好的方案。例如:我們要對系統(tǒng)設(shè)置接口設(shè)置增加更嚴(yán)格的調(diào)用限制。

    緩存

    網(wǎng)絡(luò)接口相對于堆棧接口來說數(shù)據(jù)傳輸極其不穩(wěn)定,盡可能地減少數(shù)據(jù)傳輸不僅能控制這種風(fēng)險(xiǎn)還能減少流量。使用緩存還能有效地提高后臺的吞吐量。

    后臺在響應(yīng)請求時(shí)使用響應(yīng)頭E-TagLast-Modified來標(biāo)記數(shù)據(jù)的版本,前臺在發(fā)送請求時(shí)將數(shù)據(jù)版本通過請求頭If-Match幫助后臺判斷緩存的使用。

    Request Header

     If-Match: 2390239059405940

    Response Header

    E-Tag: 2390239059405940Last-Modified: 1403183502701

    Bookmarker

    在實(shí)際的環(huán)境中,有大量的查詢需求是相同的。將這些搜索需求標(biāo)簽化能降低使用難度也可以達(dá)到重用的目的。

    示例: 查找狀態(tài)為關(guān)閉的訂單

    普通方式
    GET http://~/$version/trades?status=closed&sorting=-created_at

    Bookmarker
    GET http://~/$version/trades#recently_closed

    GET http://~/$version/trades/recently_closed

    HATEOAS

    HATEOAS通過Web Linking的方式來描述程序的狀態(tài)信息

    Link 主要包含以下屬性:

    PropertyDescription
    rel關(guān)聯(lián)內(nèi)容
    hrefURL
    type媒體類型
    methodHttp Method
    title標(biāo)題
    arguments參數(shù)列表
    value返回值

    Rel 可能為以下值:

    ValueDescription
    next下一步
    prev上一步
    first第一步,最前
    last最后一步,最后
    source來源
    self資源自身,相對于this

    Web Linking 可以通過兩種方式傳遞至客戶端:

    Http Header

    Link: <http://~/$version/trades?page_no=10>; rel="next", <http://~/$version/trades?page_no=19>; rel="last"

    Http JSON Body

    {     "links": [         {             "rel": "next",             "href": "http://~/$version/trades?page_no=1"         },         {             "rel": "last",             "href": "http://~/$version/trades?page_no=19"         }     ] }

    示例1: 用戶注冊業(yè)務(wù)

    1. 用戶填寫E-Mail與密碼

    2. 完善用戶資料

    Register Request

    POST http://~/$version/accountsHeaders:     Accept: application/json         Content-Type: application/json;charset=utf-8Body:     {             "username": "cloud.mario@gmail.com",             "e_mail": "cloud.mario@gmail.com",             "password": "balabala"     }

    Register Response

    Headers:     Content-Type: application/json;charset=utf-8 Status: 201 Created Body:     {              "uri": "http://~/$version/accounts/cloud.mario",             "identity": "cloud.mario",             "created_at": 1403535668653,             "links": [             {                             "rel": "next",                             "href": "http://~/$version/accounts/cloud.mario/profiles",                             "method": "POST",                             "title": "Editing Profiles",                             "arguments": "status=editing"             }         ]     }

    Profile Request

    POST http://~/$version/accounts/cloud.mario/profilesHeaders:     Accept: application/json         Content-Type: application/json;charset=utf-8Body:     {             "full_name": "cloud.mario",             "state": "Beijing",             "title": "一個(gè)開發(fā)者"     }

    Profile Response

    Headers:     Content-Type: application/json;charset=utf-8Status: 201 Created Body:     {             "uri": "http://~/$version/accounts/cloud.mario/profiles",             "identity": "cloud.mario",            "created_at": 1403535668653     }

    示例2: 請看下節(jié)<分頁>

    HATEOAS在解決什么問題?

    HATEOAS是Hypermedia as the Engine of Application State的縮寫形式,中文意思為:超媒體應(yīng)用狀態(tài)引擎。它的核心思想是使用超媒體表達(dá)應(yīng)用狀態(tài),與hypertext-driven思想是一致的。在此之前,我們大多數(shù)的程序業(yè)務(wù)控制在前臺完成。例如:我們會在前臺做注冊流程,我們在前臺判定下一步應(yīng)該做什么,可以做什么。當(dāng)使用HATEOAS時(shí),這些狀態(tài)流程控制都在應(yīng)用程序的后臺完成。我們使用超媒體來表達(dá)前臺做完某一步驟之后可以做哪些? 這樣一來,前臺的任務(wù)就變得相當(dāng)簡單了,前臺需要處理的是理解狀態(tài)表述,數(shù)據(jù)收集和結(jié)果顯示。

    思考

    HATEOAS會帶來怎樣的改變? 使用它的意義在哪?

    分頁

    Request

    GET http://~/$version/trades?page=10&pre_page=100

    Response

    Link Header

    Link: <http://~/$version/trades?page=11&pre_page=100>; rel="next", <http://~/$version/trades?page=19&pre_page=100>; rel="last"

    JSON Body

    {     "links": [         {             "rel": "next",             "href": "http://~/$version/trades?page=11&pre_page=100"         },         {             "rel": "last",             "href": "http://~/$version/trades?page=19&pre_page=100"         }     ] }

    安全

    調(diào)用限制

    為保證服務(wù)的可用性應(yīng)對服務(wù)進(jìn)行調(diào)用過載保護(hù)

    Response Headers

    X-RateLimit-Limit: 3000             調(diào)用量的最大限制 X-RateLimit-Reset: 1403162176516    調(diào)用限制重置時(shí)間 X-RateLimit-Remaining: 299          剩余的調(diào)用量

    安全驗(yàn)證

    RESTful服務(wù)使用Oauth2的方式進(jìn)行調(diào)用授權(quán),使用http請求頭Authorization設(shè)置授權(quán)碼; 必須使用User-Agent設(shè)置客戶端信息, 無User-Agent請求頭的請求應(yīng)該被拒絕訪問。

    Request Header

    User-Agent: Data-Server-Client Authorzation: Bearer 383w9JKJLJFw4ewpie2wefmjdlJLDJF

    為什么建議使用Oauth2授權(quán)?

    Oauth2的參與者為:客戶端,資源所有者,授權(quán)服務(wù)器,資源服務(wù)器。客戶端先從資源所有者得到授權(quán)碼之后使用授權(quán)碼從授權(quán)服務(wù)器得到token,再使用token調(diào)用資源服務(wù)器獲取經(jīng)過資源所有者授權(quán)使用的資源。這種授權(quán)方式的特點(diǎn)有:

    1. 資源所有者可以隨時(shí)撤銷授權(quán)許可

    2. 可以通過撤銷token拒絕客戶端的調(diào)用

    3. 資源服務(wù)器可以拒絕客戶端的調(diào)用

    通過這三種方式可以做到對資源的嚴(yán)格保護(hù)。資源的訪問權(quán)限也把握在資源所有者的手中,而不是資源服務(wù)器。
    當(dāng)然,Oauth2授權(quán)框架也允許受信任的客戶端直接使用token調(diào)用資源服務(wù)器獲取資源。這種靈活性完全取決于客戶端類型和對資源的保護(hù)程度。

    為什么授權(quán)碼要放在Http Header中?

    1. WEB服務(wù)器對訪問做記錄已經(jīng)成為了行業(yè)的一個(gè)標(biāo)準(zhǔn),訪問記錄不僅可以用來做訪問量統(tǒng)計(jì)還能用來做訪問特征分析?;ヂ?lián)網(wǎng)廣告平臺就是利用訪問記錄來做精準(zhǔn)營銷的。如果token(授權(quán)碼)包含在URL中就有很大的安全風(fēng)險(xiǎn)。

    2. 包含在URL中的token串可能被進(jìn)行重定向傳遞。通過這兩種方式入侵者可以不通過授權(quán)而使用泄漏的授權(quán)碼訪問那些受保護(hù)的數(shù)據(jù),會造成數(shù)據(jù)泄漏的風(fēng)險(xiǎn)。

    以Apache為例,訪問日志為:

    127.0.0.1 - - [24/Jun/2014:14:38:04 +0800] "GET /v1/accounts/cloud.mario?token=dgdreLJLJLER798989erJKJK HTTPS/1.1" 200 343

    通過對訪問日志的提取,很容易得到token信息。

    數(shù)據(jù)設(shè)計(jì)

    交互原則

    1. 查詢,過濾條件使用query string。

    2. 用來描述數(shù)據(jù)或者請求的元數(shù)據(jù)放Header中,例如 X-Result-Fields

    3. Content body 僅僅用來傳輸數(shù)據(jù)。

    4. 數(shù)據(jù)要做到拿來就可用的原則,不需要“拆箱”的過程。

    結(jié)構(gòu)

    使用JSON格式傳輸數(shù)據(jù),在http請求頭和響應(yīng)頭申明Content-Type。返回的數(shù)據(jù)結(jié)構(gòu)應(yīng)該做到盡可能簡單,不要過于包裝。響應(yīng)狀態(tài)應(yīng)該包含在響應(yīng)頭中!

    Request

    Accept: application/json Content-Type: application/json;charset=UTF-8

    Response

    Content-Type: application/json;charset=UTF-8

    錯(cuò)誤的做法

    {     "status": 200,     "data": {         "trade_id": 1234,         "trade_name": "Bala bala"     } }

    正確的做法

    Response Headers:     Status: 200     Response Body:     {             "trade_id": 1234,             "trade_name": "Bala bala"     }

    示例1: 創(chuàng)建User對象

    POST http://~/$version/users Request     headers:         Accept: application/json                Content-Type: application/json;charset=UTF-8     body:         {                     "user_name": "Cloud Mario"         } Response     status: 201 Created     headers:         Content-Type: application/json;charset=UTF-8     body:         {                     "uri": "http://~/$version/users/1234",                     "identity": 1234,                     "created_on": "Date()",                     "links": [                 {                                     "rel": "next",                                     "href": "http://~/gui/users/1234"                 }             ]         }

    為什么是JSON?

    JSON 是一種可以跨平臺高擴(kuò)展的輕量級的數(shù)據(jù)交換格式。易于人閱讀和編寫,同時(shí)也易于機(jī)器解析和生成。

    屬性定義限制

    1. 不能使用大寫(大小寫友好)

    2. 使用下劃線_命名(連接兩個(gè)單詞)

    3. 屬性和字符串值必須使用雙引號""

    提取部分字段

    無狀態(tài)服務(wù)器應(yīng)該允許客戶端對數(shù)據(jù)按需提取。在請求頭使用X-Result-Fields指定數(shù)據(jù)返回的字段集合。

    例如:trade 有trade_id, trade_namecreated_at 三個(gè)屬性,客戶端只需其中的trade_idtrade_name屬性。

    Request Header

    X-Result-Fields: trade_id,trade_name

    子對象描述

    數(shù)據(jù)里面的子對象使用URI描述不應(yīng)該被提取,除非用戶指定需要提取子對象

    示例trade里面的order對象

    錯(cuò)誤的做法

    {     "trade_id": "123456789",     "full_path": null,     "order": {         "order_id": "987654321"     } }

    正確的做法

    {     "trade_id": "123456789",     "order": "http://~/$version/orders/987654321" }

    應(yīng)用指定提取子對象,需要在請求頭聲明X-Expansion-Fields

    Request

    X-Expansion-Fields: true

    為什么要客戶端指定提取子對象時(shí)才提取?

    懶模式服務(wù)能夠最大程度地節(jié)省運(yùn)算資源。雖然與客戶端交互的次數(shù)有所增加,但是能做到按需提取,按需響應(yīng),這也是響應(yīng)式設(shè)計(jì)的一大特點(diǎn)??蛻舳说挠脩粜袨槟J綗o法真實(shí)地模擬,也就無法確定哪些資源需要做到一次性推送,讓客戶端按需使用是一個(gè)不錯(cuò)的方式。

    關(guān)于空字段

    應(yīng)該在返回結(jié)果里面剔除空字段,因?yàn)閚ull值傳輸?shù)娇蛻舳瞬]有實(shí)際的含義,反而增加了占用空間。

    Tips

    使用HTTP Header時(shí),優(yōu)先使用合適的標(biāo)準(zhǔn)頭屬性。用X-作為前綴自定義一個(gè)頭屬性,例如: X-Result-Fields。

    狀態(tài)碼&錯(cuò)誤處理

    應(yīng)用狀態(tài)碼

    CodeHTTP OperationBody ContentsDescription
    200GET,PUT資源操作成功
    201POST資源,元數(shù)據(jù)對象創(chuàng)建成功
    202POST,PUT,DELETE,PATCHN/A請求已經(jīng)被接受
    204DELETE,PUT,PATCHN/A操作已經(jīng)執(zhí)行成功,但是沒有返回?cái)?shù)據(jù)
    301GETlink資源已被移除
    303GETlink重定向
    304GETN/A資源沒有被修改
    400GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)參數(shù)列表錯(cuò)誤(缺少,格式不匹配)
    401GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)未授權(quán)
    403GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)訪問受限,授權(quán)過期
    404GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)資源,服務(wù)未找到
    405GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)不允許的http方法
    409GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)資源沖突,或者資源被鎖定
    415GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)不支持的數(shù)據(jù)(媒體)類型
    429GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)請求過多被限制
    500GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)系統(tǒng)內(nèi)部錯(cuò)誤
    501GET,PSOT,PUT,DELETE,PATCH錯(cuò)誤提示(消息)接口未實(shí)現(xiàn)

    容器狀態(tài)碼

    容器狀態(tài)碼是指http容器的狀態(tài)碼,應(yīng)用不應(yīng)該使用或限制使用

    CodeHTTP OperationBody ContentsDescription
    303GETlink靜態(tài)資源被移除,應(yīng)用限制使用
    503GET,PSOT,PUT,DELETE,PATCHtext body服務(wù)器宕機(jī)

    Tips

    4開頭的錯(cuò)誤用來表達(dá)來自于客戶端的錯(cuò)誤,例如: 未授權(quán),參數(shù)缺失。5開頭的錯(cuò)誤用來表達(dá)服務(wù)端的錯(cuò)誤,例如: 在連接外部系統(tǒng)(DB)發(fā)生的IO錯(cuò)誤。

    錯(cuò)誤信息格式

    錯(cuò)誤信息應(yīng)該包含下列內(nèi)容:

    1. 錯(cuò)誤標(biāo)題 message, 必須

    2. 錯(cuò)誤代碼 error code, 必須

    3. 錯(cuò)誤信息 error message, 必須

    4. 資源 resource, 可選

    5. 屬性 field, 可選

    6. 文檔地址 document, 可選

    Tips

    Error Code 盡可能做到簡潔明了,提取異常的關(guān)鍵字并且使用下劃線_把它們連接起來。

    示例: 調(diào)用頻率超過限制,Response:

        Headers:         Content-Type: application/json;charset=UTF-8         X-RateLimit-Limit: 3000         X-RateLimit-Reset: 1403162176516         X-RateLimit-Remaining: 0     {             "message": "Message title",              "errors": [             {                              "code": "rate_limit_exceeded",                             "message": "Too Many Requests. API rate limit exceeded",                             "document": "https://developer.github.com/v3/gists/"             }         ]     }

    錦上添花

    1. 格式化(Pettyprint)JSON數(shù)據(jù)(返回結(jié)果)并且使用gzip壓縮,Pettyprint易于閱讀,多余的空格在經(jīng)過gzip壓縮之后占用空間比壓縮之前更小。

    2. 重寫Server

    3. 返回X-Powered-By

    Response Headers

    X-Pretty-Print: true Content-Encoding: gzip Server: cloud.mario@sina.com X-Powered-By: cloud.mario;email=cloud.mario@gmail.com

    資源福利

    框架&工具

    參考資料

    posted on 2014-08-18 08:47 paulwong 閱讀(6475) 評論(0)  編輯  收藏 所屬分類: RESTFUL-API


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 精品免费久久久久国产一区| yy6080亚洲一级理论| 日韩欧美一区二区三区免费观看| 日本亚洲成高清一区二区三区| 一本久久A久久免费精品不卡| 四虎永久在线精品视频免费观看| 亚洲中文字幕久久精品无码A| 亚洲国产精品免费观看| 亚洲黄色片在线观看| 久久精品私人影院免费看| 亚洲色偷偷综合亚洲AVYP| 特黄特色的大片观看免费视频| 国产一区二区三区免费视频| 日韩欧美亚洲中文乱码| 午夜免费福利在线| 亚洲AV无码成人网站久久精品大 | 久9热免费精品视频在线观看| 日韩激情淫片免费看| 国产精品亚洲精品青青青| 四虎在线成人免费网站| 亚洲熟妇av一区| 1000部拍拍拍18免费网站| 亚洲毛片一级带毛片基地| 91免费国产精品| 亚洲视频一区在线观看| 亚洲一区二区免费视频| 亚洲天堂一区二区三区四区| 免费专区丝袜脚调教视频| 亚洲成年人免费网站| 国产在线观看片a免费观看 | 国产区图片区小说区亚洲区| 国产午夜鲁丝片AV无码免费| 免费人成视频在线播放| 成人看的午夜免费毛片| 亚洲熟女乱色一区二区三区| 日韩一级免费视频| 精品久久久久久亚洲综合网| 内射无码专区久久亚洲| 国产免费人成视频尤勿视频| 精品国产_亚洲人成在线高清| 一级毛片免费观看|