1. 程式人生 > >SpringCloud工作筆記048---RESTful API 中 HTTP 狀態碼的定義_以及把RESTFul版本號_放到http協議header中_以及RestFul設計時的兩個誤區

SpringCloud工作筆記048---RESTful API 中 HTTP 狀態碼的定義_以及把RESTFul版本號_放到http協議header中_以及RestFul設計時的兩個誤區

-------------------------

RESTful架構有一些典型的設計誤區。

最常見的一種設計錯誤,就是URI包含動詞。因為"資源"表示一種實體,所以應該是名詞,URI不應該有動詞,動詞應該放在HTTP協議中。

舉例來說,某個URI是/posts/show/1,其中show是動詞,這個URI就設計錯了,正確的寫法應該是/posts/1,然後用GET方法表示show。

如果某些動作是HTTP動詞表示不了的,你就應該把動作做成一種資源。比如網上匯款,從賬戶1向賬戶2匯款500元,錯誤的URI是:

  POST /accounts/1/transfer/500/to/2

正確的寫法是把動詞transfer改成名詞transaction,資源不能是動詞,但是可以是一種服務:

  POST /transaction HTTP/1.1   Host: 127.0.0.1      from=1&to=2&amount=500.00

另一個設計誤區,就是在URI中加入版本號

  http://www.example.com/app/1.0/foo

  http://www.example.com/app/1.1/foo

  http://www.example.com/app/2.0/foo

因為不同的版本,可以理解成同一種資源的不同表現形式,所以應該採用同一個URI。版本號可以在HTTP請求頭資訊的Accept欄位中進行區分(參見Versioning REST Services

):

  Accept: vnd.example-com.foo+json; version=1.0

  Accept: vnd.example-com.foo+json; version=1.1

  Accept: vnd.example-com.foo+json; version=2.0

-------------------------

懶人直接看 200、400、401、403、404、500 就可以了。

其中 2XX/3XX 其實都是請求成功,但是結果不同。4XX 是請求出錯,5XX 是伺服器處理出現錯誤。

200 這個最容易理解,就是正確的請求返回正確的結果,如果不想細分正確的請求結果都可以直接返回200。

201 表示資源被正確的建立。比如說,我們 POST 使用者名稱、密碼正確建立了一個使用者就可以返回 201。

202 請求是正確的,但是結果正在處理中,沒法返回對應的結果。比如說,我們請求一個需要大量計算的結果,但是並沒有計算結束時,可以返回這個,這時候客戶端可以通過輪詢等機制繼續請求。

203 請求的代理伺服器修改了源伺服器返回的 200 中的內容,一般用不到。比如說,我們通過代理伺服器向伺服器 A 請求使用者資訊,伺服器 A 正常響應,但代理伺服器命中了快取並返回了自己的快取內容,這時候它返回 203 告訴我們這部分資訊不一定是最新的,我們可以自行判斷並處理。

204 請求正確,但是沒有需要返回的內容。比如說,我們請求刪除某個使用者,刪除成功可以返回 204。

205 類似 204,但是要求請求者重置檢視,一般也用不到。比如說,我們請求刪除某個使用者,伺服器返回 205 的話,我們就重新整理現在的使用者列表。

206 請求成功,但根據請求頭只返回了部分內容。比如說,我們下載一部片,共有 10 部分,我們把請求也分成了 10 次(防止一次請求過大),這時候伺服器就可以返回 206 並在其頭部告訴我們這是哪一部分,然後再根據這個資訊進行拼裝。

300 請求成功,但結果有多種選擇。比如說,我們下載一部片,伺服器有 avi、mp4 等格式,這時候可以返回 300,並在 body 裡告知有哪些格式,然後使用者可以根據這些格式再次請求。

301 請求成功,但是資源被永久轉移。比如說,我們要下載葫蘆娃,但是由於舊的儲存服務商漲價了,現在要使用新的儲存服務了,要去新地址下載,這時候可以返回 301,並在 header 的 Location 中告知新的地址,以後也應當到這個地址下載。

302 請求成功,但是資源被臨時轉移了。和 301 不同的是,除非是 HEAD 請求,否則新地址的資訊應當在 body 中返回,並且資源只是臨時轉移,以後不應當通過新地址來下載。

303 類似 302,但要求使用 GET 來訪問新的地址來獲取資源。

304 請求的資源並沒有被修改過。比如說,我們傳送請求想看看 5.20 後的情侶資訊,伺服器查詢沒有新的情侶資訊產生,這時候可以返回 304,然後客戶端可以繼續用舊的資料。

305 請求的資源必須通過代理訪問。比如說,我們想請求伺服器 A 上新的 iPhone 的資訊,但是需要通過代理伺服器才能訪問,如果直接請求了伺服器 A,沒有經過代理伺服器,這時候伺服器 A 就可以返回 305 從而告訴我們應當訪問代理伺服器。 306 不用了。

307 類似 302,但要求使用原有的請求方式來通過新地址獲取資源。

308 類似 301,但要求使用原有的請求方式來通過新地址獲取資源。

400 請求出現錯誤,比如請求頭不對等,所有不想明確區分的客戶端請求出錯都可以返回 400。

401 沒有提供認證資訊。比如說,請求的時候沒有帶上 Token 等。

402 為將來的需要所保留的狀態碼。

403 請求的資源不允許訪問。比如說,你使用普通使用者的 Token 去請求管理員才能訪問的資源。

404 請求的內容不存在。

405 請求的方法不允許使用。比如說,伺服器只實現了 PATCH 了局部更新資源,並沒有實現 PUT 來替換資源,而我們使用了 PUT,這時候伺服器可以返回 405 來告知並沒有實現對 PUT 的相關處理。

406 請求的資源並不符合要求。比如說,我們 header 裡請求 JSON 格式的資料,但是伺服器只有 XML 格式的資料,這時候可以返回 406 告知。

407 類似 401,但是要求必須去同代理伺服器進行認證。

408 客戶端請求超時。我們想 POST 建立一個使用者,雖然建立了連線,但是網路不好,伺服器在規定時間內沒有得到我們的請求資訊,這時候伺服器可以返回 408 告訴我們超時了。然後我們可以重新發送請求。

409 請求衝突。比如說,伺服器要求不同使用者不能重名,伺服器已經有了一個名叫小偉的使用者,這時候我們又想建立一個名叫小偉的使用者,伺服器可以返回 409,告訴我們衝突了,也可以在 body 中明確告知是什麼衝突了。

410 請求資源曾經存在,但現在不存在了。比如說,我們下載葫蘆娃,但是因為版權被刪了,下載不了了,這時候伺服器返回 410,告訴我們洗洗早點睡。

411 沒有提供請求資源的長度。比如說,我們下載葫蘆娃,伺服器只允許我們分部分下載,我們如果不告訴伺服器我們要下載哪部分,伺服器就返回 411 警告我們。

412 請求的資源不符合請求頭中的 IF-* 的某些條件。比如說,我們下載葫蘆娃,然後在請求頭告知伺服器要 5.20 後更新過的,伺服器沒有,於是返回了 412。

413 請求體過大。比如說,伺服器要求上傳檔案不能超過 5M,但是我們 POST 了 10M,這時候就返回 413。

414 請求的 URI 太長了。比如說,我們提供了太多的 Query 引數,以至於超過了伺服器的限制,這時候可以返回 414。

415 不支援的媒體型別。比如說,我們上傳了一張七娃的 GIF 動圖,而伺服器只允許你上傳 PNG 圖片,這時候就返回 415。 416 請求的區間無效。比如說,我們分部分下載時請求葫蘆娃的 10 分鐘到 12 分鐘的內容,但是這部葫蘆娃只有 1 分鐘的內容,這時候就返回 416。

417 預期錯誤。指伺服器沒法滿足我們在請求頭裡的 Expect 相關的資訊。

418 我是個茶壺。這是一個愚人節的玩笑,這個狀態碼就是用來搞笑的。

500 伺服器錯誤。沒法明確定義的伺服器錯誤都可以返回這個。

501 請求還沒有被實現。比如說,我們請求一個介面來自動拒絕專案經理的要求,但是這個介面只是美好的想象,並沒有被實現,這時候可以返回 501。

502 閘道器錯誤。比如說,我們向伺服器 A 請求下載葫蘆娃,但是 A 其實只是一個代理伺服器,他得向 B 請求葫蘆娃,但是不知道為啥 B 不理他或者給他錯誤,這時候哦可以 A 返回 502 用來表示 B 這傢伙傲嬌了。

503 服務暫時不可用。比如說,伺服器正好在更新程式碼重啟。

504 類似 502,但是這時候是 B 不理 A,超時了 。

505 請求的 HTTP 版本不支援。比如說,現在強行根據 HTTP 1000 來請求。

-------------------------------------------------

HTTP無狀態協議(超文字傳輸協議,屬於文字協議)

HTTP無狀態協議是指協議對於事務處理沒有記憶能力。缺少狀態意味著如果後續處理需要前面的資訊,則它必須重傳,這樣可能導致每次連線傳送的資料量增大。另一方面,在伺服器不需要先前資訊時它的應答就較快。



HTTP請求頭和相應頭

參考:http://tools.jb51.net/table/http_header

相應頭和請求頭對照(部分)


Request
Response
Accept:瀏覽器能夠處理的內容型別
Accept-Ranges:表明伺服器是否支援指定範圍請求及哪種型別的分段請求
Accept-Charset:瀏覽器能夠顯示的字符集
Accept-Encoding:瀏覽器能夠處理的壓縮編碼
Accept-Language:瀏覽器當前設定的語言
Connection:瀏覽器與伺服器連線型別,表示是否要持續連線
 
Cookie:把儲存在該請求域名的所有cookies傳送給伺服器
Set-Cookie:設定HTTP cookie
Host:指定請求伺服器的域名或埠號
Server:伺服器軟體名稱
Referer:先前請求的網頁
 
User-Agent:使用者代理
Vary:告訴下游代理是使用快取響應還是從原始伺服器請求
 
Location:重定向到新的資源
 
Refresh:應用於重定向或一個新的資源被創造
Date:請求傳送的時間
Date:原始伺服器發出訊息的時間
 
Expires:相應過期的時間
 
Age:從原始伺服器到代理快取形成的估算時間(以秒計,非負)
Cache-Control:指定請求和相應遵循哪種快取機制。
Cache-Control:告訴所有的快取機制是否可以快取以及用哪種方式快取



RESTful(英文:Representational State Transfer,簡稱REST表現層狀態轉換)

一種軟體架構風格,設計風格而不是標準,只是提供了一組設計原則和約束條件。它主要用於客戶端和伺服器互動類的軟體。基於這個風格設計的軟體可以更簡潔,更有層次,更易於實現快取等機制。

Web 應用程式最重要的 REST 原則是,客戶端和伺服器之間的互動在請求之間是無狀態的。從客戶端到伺服器的每個請求都必須包含理解請求所必需的資訊。如果伺服器在請求之間的任何時間點重啟,客戶端不會得到通知。此外,無狀態請求可以由任何可用伺服器回答,這十分適合雲端計算之類的環境。客戶端可以快取資料以改進效能。

HTTP狀態碼

HTTP狀態碼分類 
1** 資訊,伺服器收到請求,需要請求者繼續執行操作
2** 成功,操作被成功接收並處理
3** 重定向,需要進一步的操作以完成請求
4** 客戶端錯誤,請求包含語法錯誤或無法完成請求
5** 伺服器錯誤,伺服器在處理請求的過程中發生了錯
HTTP狀態碼列表
100 Continue    繼續。客戶端應繼續其請求
101 Switching Protocols 切換協議。伺服器根據客戶端的請求切換協議。只能切換到更高階的協議,例如,切換到HTTP的新版本協議
200 OK  請求成功。一般用於GET與POST請求
201 Created 已建立。成功請求並建立了新的資源
202 Accepted    已接受。已經接受請求,但未處理完成
203 Non-Authoritative Information   非授權資訊。請求成功。但返回的meta資訊不在原始的伺服器,而是一個副本
204 No Content  無內容。伺服器成功處理,但未返回內容。在未更新網頁的情況下,可確保瀏覽器繼續顯示當前文件
205 Reset Content   重置內容。伺服器處理成功,使用者終端(例如:瀏覽器)應重置文件檢視。可通過此返回碼清除瀏覽器的表單域
206 Partial Content 部分內容。伺服器成功處理了部分GET請求
300 Multiple Choices    多種選擇。請求的資源可包括多個位置,相應可返回一個資源特徵與地址的列表用於使用者終端(例如:瀏覽器)選擇
301 Moved Permanently   永久移動。請求的資源已被永久的移動到新URI,返回資訊會包括新的URI,瀏覽器會自動定向到新URI。今後任何新的請求都應使用新的URI代替
302 Found   臨時移動。與301類似。但資源只是臨時被移動。客戶端應繼續使用原有URI
303 See Other   檢視其它地址。與301類似。使用GET和POST請求檢視
304 Not Modified    未修改。所請求的資源未修改,伺服器返回此狀態碼時,不會返回任何資源。客戶端通常會快取訪問過的資源,通過提供一個頭資訊指出客戶端希望只返回在指定日期之後修改的資源
305 Use Proxy   使用代理。所請求的資源必須通過代理訪問
306 Unused  已經被廢棄的HTTP狀態碼
307 Temporary Redirect  臨時重定向。與302類似。使用GET請求重定向
400 Bad Request 客戶端請求的語法錯誤,伺服器無法理解
401 Unauthorized    請求要求使用者的身份認證
402 Payment Required    保留,將來使用
403 Forbidden   伺服器理解請求客戶端的請求,但是拒絕執行此請求
404 Not Found   伺服器無法根據客戶端的請求找到資源(網頁)。通過此程式碼,網站設計人員可設定"您所請求的資源無法找到"的個性頁面
405 Method Not Allowed  客戶端請求中的方法被禁止
406 Not Acceptable  伺服器無法根據客戶端請求的內容特性完成請求
407 Proxy Authentication Required   請求要求代理的身份認證,與401類似,但請求者應當使用代理進行授權
408 Request Time-out    伺服器等待客戶端傳送的請求時間過長,超時
409 Conflict    伺服器完成客戶端的PUT請求是可能返回此程式碼,伺服器處理請求時發生了衝突
410 Gone    客戶端請求的資源已經不存在。410不同於404,如果資源以前有現在被永久刪除了可使用410程式碼,網站設計人員可通過301程式碼指定資源的新位置
411 Length Required 伺服器無法處理客戶端傳送的不帶Content-Length的請求資訊
412 Precondition Failed 客戶端請求資訊的先決條件錯誤
413 Request Entity Too Large    由於請求的實體過大,伺服器無法處理,因此拒絕請求。為防止客戶端的連續請求,伺服器可能會關閉連線。如果只是伺服器暫時無法處理,則會包含一個Retry-After的響應資訊
414 Request-URI Too Large   請求的URI過長(URI通常為網址),伺服器無法處理
415 Unsupported Media Type  伺服器無法處理請求附帶的媒體格式
416 Requested range not satisfiable 客戶端請求的範圍無效
417 Expectation Failed  伺服器無法滿足Expect的請求頭資訊
500 Internal Server Error   伺服器內部錯誤,無法完成請求
501 Not Implemented 伺服器不支援請求的功能,無法完成請求
502 Bad Gateway 充當閘道器或代理的伺服器,從遠端伺服器接收到了一個無效的請求
503 Service Unavailable 由於超載或系統維護,伺服器暫時的無法處理客戶端的請求。延時的長度可包含在伺服器的Retry-After頭資訊中
504 Gateway Time-out    充當閘道器或代理的伺服器,未及時從遠端伺服器獲取請求
505 HTTP Version not supported  伺服器不支援請求的HTTP協議的版本,無法完成處理

---------------------

本文來自 晞晞 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/leledexixi/article/details/59109160?utm_source=copy 

-----------------------------------------------

而且 github API,也是 URI 中帶有版本號的,難道說 github 的 API 設計的不好?

所以我懷疑 阮一峰這個問題上搞錯了。

能分享一些實際的設計 API 的經驗嗎?謝謝

---------------------------

REST API新版本上線後,舊版本要繼續線上,所以要做多版本並行。

伺服器程式碼目錄

api.example.com/

                          0.1/

                               controller

                               model

                               htdocs/index.php

                          0.2/

                               controller

                               model

                               htdocs/index.php

之前做的URI是這樣的:

curl http://api.example.com/0.2/users/1

web server需要做rewrite,把各個版本的請求路由到 {v}/www/index.php。

版本號的格式為:11.11.11,即([0-9]+\.)+[0-9]+

這時候apache這麼配:

    DocumentRoot "/api.example.com/"
    ServerName api.example.com
    RewriteEngine On
    RewriteRule ^/(([0-9]+\.)+[0-9]+)/ /$1/www/index.php

更多的瞭解REST以後,覺得把版本號、access_token放在header中更符合資源的概念。

URI改成這樣:

curl -H 'Accept:application/json; version=0.2' http://api.example.com/users/1

這個時候需要web server從header中解析到版本號,然後路由。

這個時候apache這麼配:

    DocumentRoot "/api.example.com/"
    ServerName api.example.com
    RewriteEngine On
    RewriteCond  %{HTTP_ACCEPT}  version=(([0-9]+\.)+[0-9]+) 
    RewriteRule ^(.+)$ - [env=v:%1]
    RewriteCond  %{HTTP_ACCEPT}  version=(([0-9]+\.)+[0-9]+)
    RewriteRule  .*   /%{ENV:v}/htdocs/index.php
    #RewriteLogLevel 9
    #RewriteLog logs/api.example.com-rewrite_log

在網上查了半天,才試出來apache rewrite從header中取變數。

nginx 這麼配:

複製程式碼

server {
    listen       8080;
    server_name  api.example.com;
    root html/api;
    #access_log   logs/api.example.com/access.log combined buffer=32k;
    access_log   logs/api.example.com/trunk/access.log combined;
    error_log    logs/api.example.com/trunk/error.log;

    location = /robots.txt {
        expires 1d;
    }
    location = /favicon.ico {
        expires 1d;
    }

    location / {
        rewrite ^/$ /docs.php last;
        set $api_version "enabled";
        if ($uri ~ "((([0-9]+\.)+[0-9]+)|trunk)/docs/.*") {
            set $api_version $1;
            rewrite ^/((([0-9]+\.)+[0-9]+)|trunk)/docs/$ /$api_version/htdocs/docs/index.html break;
            rewrite ^/((([0-9]+\.)+[0-9]+)|trunk)/docs/(.*)$ /$api_version/htdocs/docs/$4 break;
        }
        if ($http_accept ~ "application/json; version=((([0-9]+\.)+[0-9]+)|trunk)") {
            set $api_version $1;
        }
        rewrite .* /$api_version/htdocs/index.php last;
    }

    location ~ \.php$ {
        fastcgi_pass   unix:/home/lnmp/php/var/run/php-fpm.sock;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    error_page  404              /404.html;
    location = /404.html {
        root   html;
    }

    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

複製程式碼

注意:nginx if中的rewrite不能直接使用$1,而要先set。

參考資料:

how to use a variable inside a nginx “if” regular expression

apache [env=ps:http] %{ENV:ps}

apache rewrite 變數 %{}

apache rewrite %N $N

apache [E=VAR:VAL] [env

截圖: