1. 程式人生 > >使用C#開發HTTP伺服器系列之構建RESTful API

使用C#開發HTTP伺服器系列之構建RESTful API

  各位朋友大家好,我是秦元培,歡迎大家關注我的部落格,我的部落格地址是http://qinyuanpei.com。到目前為止,“使用C#開發HTTP伺服器”這個系列系列文章目前已經接近尾聲了,雖然我們在伺服器功能的完整性(如支援併發、快取、非同步、Htts等)上沒有再繼續深入下去,可是我們現在已經具備了一個基本的伺服器框架啦,所以更多深層次的問題就需要大家根據自己的需要來擴充套件了,因為寫部落格更多的是一種“記錄-輸出-反饋”的一個過程,所以我更希望大家在看完我的部落格後能對我有所反饋,因為抄部落格上的程式碼實在是太無聊啦!好了,保持愉悅的心情我們下面來引出今天的話題:構建RESTful API。RESTful API,這個概念或許你曾經聽說過,可能它和我們所熟悉的各種Web息息相關,甚至在某種意義上來講它並不是一種新的技術,而這一切的一切歸根到底都是在問一個問題,即網站真的是Web的唯一形態嗎?

什麼是RESTful API

  什麼是RESTful API?首先,REST即REpresentational State Transfer,通常被翻譯為“表述性狀態傳輸”或者“表述性狀態轉移”,它最早出自Roy Fielding的《Archltectural Styles and the Design of Network-based Software Arcltechures》這篇論文,作者曾經參與HTTP協議和Apache Web Server的設計,所以REST實際上是一個和HTTP協議聯絡非常緊密的一種設計思想。而從這個題目中我們可以找到三個關鍵詞:

  • 架構樣式——(Archltectural Styles)
  • 軟體架構——(Software Arcltechures)
  • 網路為基礎——(Network-based)

所以從我個人角度來理解REST,我更傾向於將REST理解為一種以網路為基礎的設計風格,因此REST從本質上來講解決的是如何正確地使用Web標準的問題。

  以國內為例,當Google的Chrome瀏覽器選擇以Chormium這種形式開源以後,國內廠商紛紛表示跟進以雙核為主要特點進行了新一輪的網際網路入口爭奪戰,雖然從技術角度來講這讓Chorme瀏覽器更加流行,可我們更應該注意到不同的廠商紛紛建立起自己的護城河,以犧牲Web的統一性和標準性來滿足其商業競爭的需要,所以我們看到了即使在HTML5定稿以後,在不同瀏覽器對HTML5的支援區別依然非常大。微信帶動了大量可以在朋友圈內流傳的“H5”媒介,可是這個東西從來就不是HTML5,而微信內建的QQ瀏覽器核心更是以各種不相容讓開發者為此殫精竭慮,所以你問我REST是什麼的時候,我會回答它是一種風格上統一的Web API,而根據百科中的描述REST通常被這樣定義:
* REST是一組架構約束條件和原則,而滿足這些約束條件和原則的應用程式就是RESTful。
* REST的目標是構建可擴充套件的Web Service,它是一種更簡單的SOAP協議以及以WSDL為基礎的WebService的替代。
* REST採用的是HTTP協議並通過HTTP中的GET、POST、PUT、DELETE等動詞收發資料。
* REST希望通過HTTP來完成對資料的元操作,即傳統的CRUD(Create、Read、Update、Delete)分別對應GET、POST、PUT、DELETE,這樣就統一了資料操作的介面,實現在不同平臺上提供一套相同的服務。
* REST是一種面向服務的、分散式的API設計風格。

從WebService看REST

  在這裡我們提到了SOAP、WSDL、RPC等概念,這是因為從某種意義上來講,REST是這些概念的一種延伸。以我們熟悉的WebService為例,當我們需要從網路上獲取天氣預報資訊時,我們可以採取兩種思路,第一種是通過抓包分析相關天氣預報網站來獲取資訊,第二種是通過呼叫網際網路上提供的WebService來獲得資訊。雖然這兩種方法在技術上具有相似性和可行性,可是我覺得對開發者來講,除了技術層面的突破以外在道德層面的堅守更為重要,我們說”人無德不立,國無德不興”正是如此,所以我們這裡強烈推薦第二種思路。WebService能夠讓我們像呼叫一個方法一樣獲取資訊,那麼對我們來講WebService到底是什麼呢?

  WebService首先是一種服務,它不需要客戶端提供額外的軟體支援,只要客戶端支援HTTP協議和XML這樣兩個特性就可以了。而對WebService自身來講,它本身就是一種自我描述型的設計,所以服務端和客戶端可以通過它來了解響應和請求的內容及格式,因為XML是一種平臺無關、語言無關的文件結構,所以WebService是一種可以跨平臺的Web API。WebService能夠讓客戶端像呼叫原生代碼一樣呼叫服務端程式碼,所以WebService是一種分散式計算的Web應用程式元件。我們對WebService下了如此多的定義,其實核心是什麼呢?核心是WebService是一種基於HTTP協議和XML的Web API。

  好了,現在我們再來說說什麼是SOAP和WSDL。事實上,這些概念聽起來都非常地學術,可是我保證這對我們理解REST會有所幫助。首先,SOAP即簡單訪問物件協議(Simple Object Access Protocol),聽起來感覺非常高大上嗎?然而這是一個“唯一沒有發明任何新技術的技術”。因為它是一個訪問Web服務的協議,如同HTTP協議定義了訪問Web的協議一樣,SOAP在HTTP協議的基礎上,採用XML定義了訊息協議,所以SOAP本質上是使用XML進行通訊的HTTP協議,這樣聽起來是不是非常熟悉啦,因為我們熟悉的AJAX同樣是採用XML進行通訊,所不同的是AJAX是執行在瀏覽器中的且其主要目的是實現頁面的無重新整理更新。需要說明的是,雖然SOAP的基礎HTTP協議是基於TCP/IP協議的,可是SOAP是具有穿透防火牆的能力的,對此有興趣的朋友可以自行了解,我們這裡因為篇幅有限所以就不做詳細說明啦!

  接下來,WSDL即Web服務描述語言(Web Service Description Language),我對它的理解是提供了一個WebService的文件,因為從定義可以看出,它是一個基於XML的用於描述Web服務以及如何訪問Web服務的語言,Web服務提供者通過它可以告知使用者當前Web服務訪問的規範和說明,而Web使用者通過它可以在滿足平臺無關性和語言無關性的情況下快速進行開發,所以綜合下來看,WebService和REST都能為我們提供類似地服務需求,關於兩者或者說REST能為我們帶來哪些不一樣的體驗,我們將在本文的第二部分說明。

從WCF看REST

  我覺得對技術而言,我們每個人都應該試圖去發現技術背後真正美的東西,就像我們在瞭解了WebService,並發現它和REST從本質上來講都是一個東西的時候,這個時候我們應該直接去了解REST給我們帶來了哪些不一樣的東西。可是事實上因為開發者使用的平臺和語言的多樣性,讓開發者再這個過程中不得不去對平臺或者語言造成依賴,而當每個廠商都試圖建立一套自己的標準或者框架的時候,它對開發者造成的這種依賴感就越發地強烈。雖然我目前的工作是做.NET開發,可是事實上我最喜歡的只有微軟的C#語言而已。這裡我們簡單介紹下WCF,WCF即Windows Communication Foundation是由微軟發展的一組資料通訊的應用程式開發介面,它是.NET框架的一部分,從.NET Framework 3.0開始引入,其設計目標是整合不同程序的通訊、不同系統間的通訊、C/S架構通訊等等通訊目標,所以對.NET開發者而言它是一個“全家桶”般的存在,我們到底需要“小而美”還是“大而全”,這是一個問題。

  回到我們關注本身,WCF整合了Web服務、.NET Remoting、訊息佇列和Enterprise Services的功能並將其整合在Visual Studio中,顯然對我們而言,我們關注的核心依然在Web服務。首先,我們要明確的是WebService這個是行業標準,即WebService規範,這是一個和平臺、和語言無關的標準,而微軟的ASP.NET WebService是ASP.NET框架的組成部分,我不喜歡ASP.NET的一個原因就是我們常常認為網站是Web技術的核心而Web服務不是,更離譜的是我們認為開發一個Web伺服器或者一個WebService一定要採用XXX框架,雖然使用Web框架、寫業務程式碼都是技術能力的一種體現,可是不求甚解真的無法讓我安心。那麼WCF呢?其實WCF本質上是將ASP.NET WebService和微軟的相關技術如Enterprise Services(COM+)、.NET Remoting、MSMQ訊息佇列等進行了整合,為什麼要整合在一起呢?因為從巨集觀上來講,跨程序、跨機器、跨網路都屬於通訊的範疇,所以我們現在回過頭來看,這些東西玩來玩去有什麼稀奇,歸根到底還不是HTTP協議啊,我們追求新的技術並沒有錯,錯誤的是我們將希望寄託在技術本身,而不是我們自己。

讓REST理解起來簡單點

  我們從最初接觸到REST的雲裡霧裡,到翻來覆去地講述WebService,其實我的目的只有一個,那就是告訴大家,Web技術發展到今天,從本質上來將變化並沒有太大,可是為什麼我們會看到前端領域每隔一段時間就會有新的框架產生呢?回答這個問題非常簡單,所有的框架的提出都是因為某種業務的背景需要,而所有的業務無一不是因為人類增加了其複雜性,所以當你下來看待這一切的時候,你發現從WebService到REST其實變化都是非常細微的東西,與其在新技術裡疲於奔命不如靜下心來學習好HTML、CSS和JavaScript,雖然JavaScript是一個垃圾的語言,可是有時候它會讓我們這些後端程式開發者都懵逼呢,哈哈,所以現在是時候給REST一個簡單的定義:

REST是一種使用URL來定位資源,使用HTTP請求描述操作的Web服務規範。

REST的約束條件和原則

  我們說REST本質上是Web服務的一種規範,一種思想,所以單獨來說REST是沒有意義的,這意味著,如果我們要深入瞭解REST,就需要了解它的約束條件和原則,下面我們就來說說這個問題。

資源(Resources)

  在REST中資源是整個架構或者說整個網路處理的核心,那麼什麼是資源呢?在我們傳統的觀念中,資源是指伺服器上的一個檔案,而在REST裡資源則是指一個URL。URL即統一資源定位,而我們都知道通過URL可以訪問網際網路上的資源,所以在REST裡這種對資源的指向性更加強烈,並且在這裡資源的範疇會被無限放大而並非侷限在檔案本身,例如:

http://api.qc.com/v1/feed 表示獲取某人的最新Feed
http://api.qc.com/v1/friends 表示獲取某人的好友列表
http://api.qc.com/v1/profile 表示獲取某人的詳細資訊

由此我們注意到REST在形式上更加趨向API設計,而我們獲取的資源則通過一定的形式進行統一而規範化的表達,因此REST實現了讓不同的平臺共享一套API這樣的願望,這是一件非常美好的事情,這個世界上的技術陣營舉不勝數,而它們為了各自的利益建立一套封閉、臃腫的體系框架,很多時候當我們不需要這樣的“全家桶”並且希望“跨平臺”的時候,REST將會是一個不錯的選擇。

表現形式(Representational)

  在REST中表現形式作為我們對資源請求的一個結果的呈現,通過對HTTP協議的學習我們已經知道,伺服器會給客戶端返回什麼形式的資訊,這一點取決於伺服器響應報文中相關頭部欄位,而對REST來講,它通常會採用XML或者JSON來告訴請求者請求的結果,因為JSON相比XML所含的冗餘資訊較少,所以目前更加傾向於或者說流行使用JSON作為請求結果的表現形式。

狀態變化(State Transfer)

  雖然我們一再強調HTTP協議是無狀態,這主要體現在HTTP請求與請求、HTTP響應與響應的上下文無關性上。在REST中,我們所說狀態變化更多是指HTTP中的GET、POST、DELETE等動詞實現。具體來講,雖然這一點我們在前面有所提及我們來看下面的簡單示例:

GET http://someurl/tasks 表示獲取全部的tasks
POST http://someurl/tasks 表示建立一個新的task
GET http://someurl/tasks/{id} 表示獲取一個指定id的task
PET http://someurl/tasks/{id} 表示更新一個指定id的task
DELETE http://someurl/tasks/{id} 表示刪除一個指定id的task

除此之外,我們注意到REST基於HTTP協議,所以HTTP協議中的狀態碼對它來講同樣適用,例如最常用的200表示成功、500表示伺服器內部錯誤、404表示無法找到請求資源等等。

如何構建REST風格的API

  如何構建REST風格的API?這是這篇文章的最後一個問題,相信大家在閱讀這篇文章的時候會感到疲憊吧,我想說寫作者的疲憊不一定會比閱讀者的疲憊要輕,現在到了這篇文章裡最難的部分啦,這可比我們花費大量篇幅來講什麼是REST要更有意義,這是真正的說起來容易做起來難,在正式開始實踐以前,我們首先提出下面的最佳實踐:

  • URLRoot採用下面這樣的結構:
http://example.com/api/v1/
http://api.example.com/v1/
  • API版本可以放在URL或者HTTP的Header裡

  • URL使用名詞而非動詞:

http://example.com/api/v1/getProducts 這是一個糟糕的設計
GET http://example.com/api/v1/products 這是一個優雅的設計
  • 保證方法時安全的不會對資源狀態有所改變。例如:
GET http://example.com/api/v1/deleteProduct?id=1 這是一個危險的訊號
  • 資源的地址推薦使用巢狀結構
GET http://example.com/api/v1/friends/10375923/profile
  • 使用正確的HTTP狀態碼錶示訪問狀態

  • 返回含義明確的結果(這是我為什麼推薦使用JSON的理由)

  好了,這篇文章我目前能夠理解並輸出給大家的只有這些啦,關於具體在Web開發中我們如何去實現RESTful API,這個我覺得並沒有一個固定的方法吧,而且我現在編寫的這個伺服器只支援Get和Post兩種型別,如果要實現一個完整的RESTful API架構,還需要很長的時間去探索,這篇文章寫得我的確有些疲憊,所以有不周的地方希望大家諒解,後續更新關注我的專案HttpServer就好啦,謝謝大家!