REST簡介
最近幾年,Web程序有種趨勢,就是業務邏輯被越來越多地移到了客戶端一側,開創出一種稱爲富互聯網應用(Rich Internet Application, RIA)的架構,在RIA中,服務器的主要功能(有時是唯一功能)是爲客戶端提供數據存取服務,在這種模式中,服務器變成了Web服務或應用編程接口(Application Programming Interface,API)
RIA可採用多種協議與Web服務通信,遠程過程調用(Romete Procedure Call,RPC)協議,例如XML-RPC,及由其衍生的簡單對象訪問協議,(Simplified Object Access Protocol, SOAP),在幾年前比較受歡迎,最近,表現層狀態轉移(Representational State Transfer,REST)架構嶄露頭角,成爲Web程序的新寵,因爲這種架構建立在大家熟知的萬維網基礎之上
Flask是開發REST架構Web服務的理想框架,因爲Flask天生輕量
Roy Fielding在其博士論文中介紹了Web服務的REST架構方式,並列出6個符合這一架構定義的特徵:
客戶端-服務器
客戶端和服務器之間必須有明確的界限無狀態
客戶端發出的請求中必須包含所有必要的信息,服務器不能在兩次請求之間保存客戶端的任何狀態緩存
服務器發出的響應可以標記爲可緩存或不可緩存,這樣出於優化目的,客戶端(或客戶端和服務器之間的中間服務)可以使用緩存接口統一
客戶端訪問服務器資源時使用的協議必須一致,定義良好,且已經標準化,REST Web服務最常使用的統一接口是HTTP協議系統分層
在客戶端和服務器之間可以按需插入代理服務器,緩存或網關,以提高性能、穩定性和伸縮性按需代碼
客戶端可以選擇從服務器上下載代碼,在客戶端的環境中執行
資源就是一切
資源是REST架構方式的核心概念,在REST架構中,資源時程序中你要着重關注的事務
例如在博客程序中,用戶、博客文章和評論都是資源
每個資源都要使用唯一的URL表示,還是以博客程序爲例,一篇博客文章可以使用URL/api/posts/12345
表示,其中12345是這篇文章的唯一標識符,使用文章在數據庫中的主鍵表示,URL的格式或內容無關緊要,只要資源的URL只表示唯一的一個資源即可
某一類資源的集合也要有一個URL,博客文章集合的URL可以是/api/posts/
,評論集合的URL可以是/api/comments/
API還可以爲某一類資源的邏輯子集定義集合URL,例如,編號爲12345的博客文章,其中的所有評論可以使用URL/api/posts/12345/comments/
表示,表示資源集合的URL習慣在末尾加上一個斜線,代表一種“文件夾”結構
注意,Flask會特殊對待末尾帶有斜線的路由,如果客戶端請求的URL的末尾沒有斜線,而唯一匹配的路由末端有斜線,Flask會自動響應一個重定向,轉向末端帶斜線的URL,反之則不會重定向
請求方法
客戶端程序在建立起的資源URL上發送請求,使用請求方法表示期望的操作,若要從博客API中獲取現有博客文章的列表,客戶端可以向http://www.exam-ple.com/api/posts
發送GET請求,若要插入一篇新博客文章,客戶端可以向同一地址發送POST請求,而且請求主體中要包含博客的內容,若要獲取編號爲12345的博客文章,客戶端可以向http://www.exam-ple.com/api/posts/12345
發送GET請求,下表列出了REST架構API中常用的HTTP請求方法以及含義:
請求方法 | 目標 | 說明 | HTTP狀態碼 |
---|---|---|---|
GET | 單個資源的URL | 獲取目標資源 | 200 |
GET | 資源集合的URL | 獲取資源的集合(若有分頁,則是一頁中的資源) | 200 |
POST | 資源集合的URL | 創建新資源,並將其加入目標集合,服務器爲新資源指派URL,並在相應的Location首部中返回 | 201 |
PUT | 單個資源的URL | 修改一個現有資源,如果客戶端能爲資源指派URL,還可用來創建新資源 | 200 |
DELETE | 單個資源的URL | 刪除一個資源 | 200 |
DELETE | 資源集合的URL | 刪除目標集合中的所有資源 | 200 |
REST架構不要求必須爲一個資源實現所有的請求方法,如果資源不支持客戶端使用的請求方法,響應的狀態碼爲405,返回“不允許使用的方法”,Flask會自動處理這種錯誤
請求和響應主體
在請求和響應的主體中,資源在客戶端和服務器之間來回傳送,但REST沒有指定編碼資源的方式,請求和響應中的Content-Type首部用於指明主體中資源的編碼方式,使用HTTP協議中的內容協商機制,可以找到一種客戶端和服務器都支持的編碼方式
REST Web服務常用的兩種編碼方式是JavaScript對象表示法(JavaScript Object Notation,JSON)和可擴展標記語言(Extensible Markup Language),對基於Web的RIA來說,JSON更具吸引力,因爲JSON和JavaScript聯繫緊密,而JavaScript是Web瀏覽器使用的客戶端腳本語言,繼續以博客API爲例,一篇博客文章對應的資源可以使用如下的JSON表示:
{
"url": "http://www.example/com/api/posts/12345"
"title": "Writing RESTful APIs in Python"
"author": "http://www.example.com/api/users/2"
"body": "... text of the article here ..."
"comments": "http://www.example.com/api/posts/12345/comments"
}
在這篇博客文章中,url、author和comments字段都是完整的資源URL,這是很重要的表示方式,因爲客戶端可以通過這些URL發掘新資源
在設計良好的REST API中,客戶端只需知道幾個頂級資源的URL,其他資源的URL則從響應中包含的鏈接上發掘,這就好比瀏覽網絡時,你在自己知道的網頁中點擊鏈接發掘新網頁
版本
在傳統的以服務器爲中心的Web程序中,服務器完全掌握程序,更新程序時,只需在服務器上部署新版本就可以更新所有的用戶,因爲運行在用戶Web瀏覽器中的那部分程序也是從服務器上下載的
但升級RIA和Web服務要複雜的多,因爲客戶端程序和服務器上的程序是獨立開發的有時甚至由不同的人進行開發,可以考慮一下這種情況,即一個程序的REST Web服務器 被很多客戶端使用,其中包括Web瀏覽器和智能手機原生應用,服務器可以隨時更新Web瀏覽器中的客戶端,但無法強制更新智能手機中的應用,更新前先要獲得機主的許可,即便機主想進行更新,也不能保證新版應用上傳到所有應用商店的時機都完全吻合新服務器端版本的部署
基於以上原因,Web服務的容錯能力要比一般的Web程序強,而且還要保證舊版客戶端能繼續使用,這一問題的常見解決辦法是使用版本區分Web服務所處理的URL,例如,首次發佈的博客Web服務可以通過/api/v1.0/posts
提供博客文章的集合
在URL中加入Web服務的版本有助於條理化管理新舊功能,讓服務器能爲新客戶端提供新功能,同時繼續支持舊版客戶端,博客服務可能會修改博客文章使用的JSON格式,同時通過/api/v1.1/posts
提供修改後的博客文章,而客戶端仍能通過/api/v1.0/posts
獲取舊的JSON格式,在一段時間內,服務器要同時處理v1.1和v1.0這兩個版本的URL
提供多版本支持會增加服務器的維護負擔,但在某些情況下,這是不破壞現有部署且能讓程序不斷髮展的唯一方式