摘要:狀態碼的正確使用。解析請求獲取隨機數范圍并將生產的結果以格式返回。在代碼的最后,我們會在合法的參數返回內生成隨機數并將結果返回給客戶端。雖然示例很簡單,但是它已經包含了使用構建的基本流程解析請求,設置狀態碼,返回響應數據。
在介紹了那么多 Express 核心概念之后,接下來的文章將會把注意力放在如何構建一個真實的應用上。這里我們先從構建應用 API 接口開始。從某種程度上來說幾乎所有的軟件應用其背后都是由一組強大的 API 驅動。
其實 API 就是一種代碼之間交互的一種方式,它既可以是在程序內部也可以是通過網絡的跨機器進行。例如,Express 中的 app.use 和 app.get 就屬于在內部使用 API 。而通過 HTTP 或者 FTP 等協議發送 JSON、XML 數據的方式則屬于后者。對于后一種方式需要注意的是,API 的提供者和使用者必須對數據格式做出約定。在本文示例中,我們將會討論如何使用 Express 構建后一類型的 API 接口,同時所有 HTTP 接口返回的數據格式都將使用 JSON。
另外,本章還會討論如何設計一個優雅的 API 用于提升使用者的體驗和效率,讓 API 的含義一目了然而不用去閱讀又臭又長的說明文檔。就像“好代碼”與“壞代碼”一樣,API 是否優雅其實更多的取決于實際情形。盲目遵循 API 設計的最佳實踐有時會顯得很迂腐,因為它有可能與使用者的期望不一致。
接下來的內容包括:
什么是 API 。
Express 中構建 API 的基礎內容。
HTTP 方法與應用邏輯的關聯。
多版本 API 的實現和管理。
HTTP 狀態碼的正確使用。
簡單的 JSON 格式 API 示例首先,我們需要明確該示例的功能以及 API 的使用方式,后面再寫代碼。
假設,現在程序需要在接受到 America/Los_Angeles 或 Europe/London 等代表時區的字符串后,返回該時區的當前時間信息(例如:2015-04-07T20:09:58-07:00 )。該返回信息與現實中易懂的時間格式是不一樣的,因為它是為計算機設計的。
通過類似下面格式的 URL 的 HTTP 請求來調用應用 API:
/timezone?tz=America+Los_Angeles
而服務端 API 返回的 JSON 的數據格式,如下:
{ "time": "2015-06-09T16:20:00+01:00", "zone": "America/Los_Angeles" }
只要能調用 API 并對 JSON 數據進行解析,你就可以在任意平臺構建任意應用程序。如下圖,你可以通過 AJAX 請求該 API 實現一個展示時區信息的單頁應用。
你也可以利用該接口實現下圖所示的移動應用。
你甚至可以利用該 API 實現下圖一樣的終端命令行工具:在終端中打印服務端 API 接口返回的數據。
像前一章的天氣應用一樣,我們可以利用這些 API 返回的冰冷數據構建更具表達力的 UI 。
Express 驅動的 JSON API 服務了解 API 概念之后,下面我們就動手實現一個 Express 驅動的 API 服務。實現的原理非常簡單:通過中間件和內置函數解析網絡請求并將 JSON 數據和 HTTP 狀態碼封裝到響應對象并返回給客戶端。
從技術角度上說,API 服務除了使用 JSON 格式外,你還可以是使用 XML 或者純文本。但是 Express 和 JavaScript 對 JSON 的支持是最好的,同時它也是當前最流行的格式,所以后面會一直使用 JSON 作為默認數據格式。
下面我們編寫一個為多平臺提供隨機數生成的服務,該 API 將擁有如下特性:
在請求 API 時必須附帶隨機數最小值和最大值。
解析請求獲取隨機數范圍并將生產的結果以 JSON 格式返回。
你可能認為這里完全可以使用純文本來替換 JSON 格式。但是發送 JSON 數據是開發者的必備技能,而且 JSON 格式極易拓展。
該工程的構建步驟如下:
新建 package.json 。
創建工程主入口文件 app.js 。
在 app.js 中創建應用和路由中間件。
首先,在新建的 package.json 中,復制下面的內容并按照依賴項:
{ "name": "random-number-api", "private": true, "scripts": { "start": "node app" }, "dependencies": { "express": "^5.0.0" } }
接下來,將下面的代碼復制到入口文件 app.js 中:
var express = require("express"); var app = express(); app.get("/random/:min/:max", function(req, res) { var min = parseInt(req.params.min); var max = parseInt(req.params.max); if (isNaN(min) || isNaN(max)) { res.status(400); res.json({ error: "Bad request." }); return; } var result = Math.round((Math.random() * (max - min)) + min); res.json({ result: result }); }); app.listen(3000, function() { console.log("App started on port 3000"); });
現在啟動應用并訪問 http://localhost:3000/random/... 的話,你將看到一個附帶 10 ~ 100 范圍內隨機數的 JSON 數據。
接下來,我們來分析上面的代碼。
與之前一樣,前兩行代碼引入了 Express 并創建了一個 Express 應用實例。
然后,我們創建了一個路由中間件用于處理類似 /random/10/100 這樣的 API 請求。當然,這里還存在一些 bug ,例如,沒有過濾掉 /random/foo/bar 請求。所以,在調用 API 的時候請確保使用的參數是整型變量。
在然后,我們使用內置的 parseInt 解析范圍參數,而該函數的返回值只可能是整形數字或者 NaN。如果傳入的參數有一個為 NaN 的話就會給客戶端返回一個錯誤信息。下面這部分代碼對于整個程序來說是非常重要的:
if (isNaN(min) || isNaN(max)) { res.status(400); res.json({ error: "Bad request." }); return; }
如果上面的參數檢查的結果是最少有一個為 NaN ,程序就會進行如下處理:
設置 HTTP 狀態碼為 400。常見的 404 錯誤就是它的一個具體變種,表示的含義是:用戶請求的出現了問題。
發送包含錯誤信息的 JSON 數據。
結束請求處理并跳出中間件執行。
在代碼的最后,我們會在合法的參數返回內生成隨機數并將結果返回給客戶端。
雖然示例很簡單,但是它已經包含了使用 Express 構建 API 的基本流程:解析請求,設置 HTTP 狀態碼,返回響應數據。你可以在這個基礎之上構建更為復雜優雅的 API 。
CURD 操作 APICURD 是對程序中 Create、Read、Update、Delete 四種業務動作的一個簡稱。
大多數的應用都會涉及到 CURD 操作。例如,對于一個圖片分享應用來說,其中涉及圖片的所有操作就是典型的 CRUD:
用戶上傳照片的行為對應就是 create 操作。
用戶瀏覽照片的行為就是 read 操作。
用戶更新照片的行為就是 update 操作。
用戶刪除照片的行為就是 delete 操作。
無論是分享照片的社交應用還是文件存儲服務,你生活中的使用的很多服務中都使用了這種模式。不過在開始討論構建 CRUD 功能的 API 之前,我們先來看看被稱為 HTTP 方法的內容。
HTTP 方法HTTP 的規范中是這樣定義其方法的:
HTTP 方法明確了對請求 URI 所標識資源進行的操作,而且方法是區分大小寫的。
一個更易理解的解釋是:客戶端在發送 HTTP 請求時需要指定一個 HTTP 方法,然后服務端回依據不同的 HTTP 方法做出不同的響應。雖然,可用的 HTTP 方法有很多,但是常用的其實并不多。其中在 Web 應用中常用是下面 4 個:
GET 是最常用的一個 HTTP 方法,它表示請求服務端資源。例如,加載網站首頁、請求圖片資源都使用的是 GET。雖然服務端的響應可能不同,但是GET 請求并不會改變服務器的資源。例如,對某圖片資源的一次或者多次請求并不會導致圖片本身出現任何差別。
POST 是另一個常用的 HTTP 方法。例如,創建新博客、上傳照片、注冊用戶、清空購物車等業務都是使用 POST 。與 GET 不同的是:每次 POST 請求都會導致服務端發生修改。
PUT 方法用于對已有記錄的修改,所有我覺得它應該被稱為 "UPDATE" 更為合適。例如,修改博客標題、修改用戶昵稱等操作都是 PUT 操作。另外,PUT 還具備 POST 的功能:就是當要修改的記錄不存在時可以進行新建操作(非必需)。其次 PUT 還具有 GET 方法的特點:對同一 URL 的一次或多次 PUT 請求后的結果是一致的。
DELETE 方法用于記錄刪除。例如,刪除用戶文章、刪除網絡照片。另外,與 PUT 一樣同一刪除請求無論是執行一次還是多次最終結果是一致的。
雖然 HTTP 還有很多其他的方法,但是它們在現實開發過程中并不常見。理論上你甚至可以只使用 GET 和 POST 請求完成所有業務,但是這是錯誤實踐畢竟它違反了 HTTP 規范也會給開發者造成困惑。另外,很多瀏覽器也是根據 HTTP 方法來明確所執行的操作類型。所以,即使并沒有強制你也應該參照該規范來約束自己的行為。
前面你已經見過 Express 中對部分方法的處理,不過下面的代碼將一次涵蓋上面所有的四個方法:
var express = express("express"); var app = express(); app.get("/", function(req, res) { res.send("you just sent a GET request, friend"); }); app.post("/", function(req, res) { res.send("a POST request? nice"); }); app.put("/", function(req, res) { res.send("i don"t see a lot of PUT requests anymore"); }); app.delete("/", function(req, res) { res.send("oh my, a DELETE??"); }); app.listen(3000, function() { console.log("App is listening on port 3000"); });
將代碼復制到入口文件 app.js 中并啟動服務,然后你就可以使用 cURL 命令測試不同的 HTTP 方法了。默認情況下 cURL 使用 GET 發送請求,但是你可以使用 -X 選項來指定其他的方法。例如,curl -X PUT http://localhost:3000 。
通過 HTTP 方法構建 CRUD 接口回想以下之前的照片分享應用,下面是其中可能的 CRUD 操作:
用戶上傳圖片,此為 Create。
用戶瀏覽圖片,此為 Read。
用戶更新圖片備注等信息,此為 Update。
用戶從站點刪除圖片,此為 Delete。
不難看出 CRUD 操作與之前四種 HTTP 方法存在對應關系:
Create = POST
Read = GET
Update = PUT
Delete = DELETE
因此通過這四個 HTTP 方法我們可以很好的實現最常見 CRUD 風格的 web 應用程序。
API 版本控制實際上對于更新和創建動作與 HTTP 方法的對應關系,一些人有著自己的看法。它們認為 PUT 更應該對應創建動作而非 POST。另外,新的 PATCH 方法則對應更新操作。雖然本文將會使用上面那種更規范的對應關系,但是你完全可以按照自己的意愿選擇。
為了應對未來可能的 API 更新,對 API 進行版本控制是一件非常高效的方法。例如,前面獲取指定時區當前時間的 API 在推出后就被很多的廠商和開發者使用。但是,幾年幾后由于某些原因必須對該 API 進行更新而與此同時你又不能影響之前的使用者。此時,我們就可以通過添加新版本來解決這個問題。其中原有的 API 請求可以通過:
/v1/timezone
而新版本 API 請求則可以使用:
/v2/timezone
這樣不僅在進行 API 更新時防止了代碼的破壞性更改。而且接口使用者也有了更靈活的選擇,他們可以在必要的時候進行 API 切換。
在 Express 中可以使用 Router 中間件來實現 API 版本管理。拷貝下面代碼到文件 app1.js 中,并講其作為第一個版本 API 的實現:
var express = require("express"); var api = express.Router(); api.get("/timezone", function(req, res) { res.send("Sample response for /timezone"); }); api.get("/all_timezones", function(req, res) { res.send("Sample response for /all_timezones"); }); module.exports = api;
請注意,上面的中間件代碼在處理的 URL 并沒有包含 /v1 。下面在入口文件中引入這個 Router 中間件并進行路由映射。
var express = require("express"); var apiVersion1 = require("./api1.js"); var app = express(); app.use("/v1", apiVersion1); app.listen(3000, function() { console.log("App started on port 3000"); });
然后,你將最新版本的 API 實現放在 api2.js 文件中:
var express = require("express"); var api = express.Router(); api.get("/timezone", function(req, res) { res.send("API 2: super cool new response for /timezone"); }); module.exports = api;
最后,通過 Router 將這兩個版本的 API 同時添加到主入口中:
var express = require("express"); var apiVersion1 = require("./api1.js"); var apiVersion2 = require("./api2.js"); var app = express(); app.use("/v1", apiVersion1); app.use("/v2", apiVersion2); app.listen(3000, function() { console.log("App started on port 3000"); });
你可以通過瀏覽器驗證這些版本化后的 API 是否正確工作,另外你也可以使用 cURL 命令進行測試。
就像前面章節介紹的那樣,Router 可以讓你將不同的路由存放在不同文件中進行管理。而版本化 API 就是最典型的應用實例。
設置 HTTP 狀態碼每一個 HTTP 響應都應該附帶一個 HTTP 狀態碼,其中最有名的就是 404 Not Found 。
雖然 404 是最出名的,但是 200 狀態碼確是最常見的。與 404 不同的是,雖然當網頁成功加載或 JSON 數據成功返回后都會包含狀態碼 200,但它并不會被展示出來。
當然,除了 404 和 200 之外,HTTP 中還定義了很多其他的狀態碼,包括 100、200、300、400 以及 500 系列。需要注意的是并不是每個系列中所有 100 個數字都有明確定義,例如,100 系列只有 100,101,102 三個有效碼,緊跟其后就是 200 。
每個狀態碼系列其實都有特定的含義和主題,總結就是:
1xx: 成功接收到請求。
2xx: 成功
3xx: 重定向
4xx: 客戶端錯誤
5xx: 服務端錯誤
規范中只定義的大約 60 個狀態碼。你可以在此基礎上拓展自己的狀態碼,但是通常并不會這么做。因為優秀的 API 的首要設計原則就是確保不會對使用者造成任何歧義,所以應該最大程度遵循官方規范的指導。后面我們會對上面的每個區間的狀態碼進行講解,但是在此之前先來看看如何在 Express 中設置狀態碼。
設置 HTTP 狀態碼少部分應用還在使用 HTTP 1.0 版本的協議,而大部分以及切換到了 1.1 版本。作為下一個版本的 HTTP 2.0 標準現在也逐漸在推廣過程中。幸運的是,2.0 版本的協議大部分更新都在底層所以切換時并不會涉及太大的工作量。另外,2.0 版本還新增了一個 421 的狀態碼。
默認情況下,HTTP 狀態碼是 200。如果用戶訪問的 URL 對應資源不存在的話,Express 會發送 404 錯誤。如果訪問的服務器出現問題的話,Express 就會發送 500 錯誤。
但是這些都是 Express 的默認行為,某些情形下可能會需要自行設置狀態碼。為此,Express 的 response 對象提供了一個 status 方法,你需要在調用是傳入對應狀態碼就能完成設置。
// ... res.status(404); // ...
該方法可以進行鏈式調用,所以你可以緊跟其后使用 json 設置返回的數據。
res.status(404).json({ error: "Resource not found!" }); // 它等價于: res.status(404); res.json({ error: "Resource not found!" });
雖然 Express 對原生 Node 的 response 對象進行了拓展,并且在使用 Express 時也應遵循 Express 風格,但是你依舊可以使用原生方法來完成設置。
res.statusCode = 404;100 區間
100 區間的官方狀態碼只有兩個:100(繼續) 和 101 (切換協議),而且它們很少會被用到。如果你必須處理的話,可以去官網或者維基上查看。
200 區間200 區間狀態碼表示請求成功。雖然該區間狀態碼不少,但是常用的也就下面 4 個:
200:作為最常見的狀態碼,它也被稱為 "OK"。這意味著請求和響應都正確執行期間并沒有出現任何錯誤或者重定向操作。
201:與 200 十分類似,但是使用情形略有不同。它通常用于 POST 或者 PUT 請求成功創建記錄后。例如,創建博文、上傳圖片等操作成功后就會發送 201。
202:202 是 201 的一個變種。因為,資源的創建大多是異步進行的,而這些操作也是費時的。所以,你可以在此時給客戶端響應 202 。它表示已經成功接收數據正在等待創建。
204:它表示用戶刪除請求所對應的資源并不存在已經被刪除過了。
300區間同樣,在 300 區間,我們只介紹其中常用的三個,并且它們全都涉及重定向。
301:它表示所訪問資源位置已經發生修改,請訪問最新的 URL 。通常它還會附帶一個 Location 的頭部信息指明重定向的位置。
303:它表示請求的資源已經創建完成,現在你就會被重定位到一個新頁面。
307:與 301 類似都是提示當前 URL 不存在。不過區別是,301 的重定向是永久的而 307 可能重定向的只是一個臨時性 URL 。
400 區間400 區間的狀態碼是最多的,而它通常都是表示由于客戶端的錯誤導致請求失敗。
401 和 403:這兩個狀態碼分別表示“未授權”和“禁止”。字面上看兩者很類似,但是前者可能表示用戶未登錄而后者則可能是用戶登錄了但是權限不夠。
404:它表示用戶 URL 請求的資源并不存在。
至于該區間其他狀態碼,讀者可以去維基上自行查看,這里就不一一介紹了。另外,當你不確定應該使用哪種客戶端錯誤狀態碼時,你可以直接使用 400 。
500 區間作為 HTTP 規范里的最后一個區間,500 區間狀態碼表示的是服務內部出現錯誤。例如,請求過載或者數據庫連接中斷。另外,理論上該區間的錯誤只能有服務內部自己觸發。最后,為了防止黑客窺探太多內部信息,你可以對所有的內部錯誤僅僅返回一個抽象的“內部服務器錯誤”這樣的信息。
總結本章包含的內容有:
使用 Express 構建 API 服務。
HTTP 方法以及與 CRUD 操作之間的關系。
如果對 API 進行版本控制,提示服務的兼容性和穩定性。
HTTP 狀態碼的使用和其意義。
原文地址
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84849.html
摘要:最終代碼省略其他輸入類型用標識查詢類型需要至少定義一個不要會不顯示查詢這里需要轉成數組因為前面定義了返回值是類型相當于數據庫的添加操作相當于數據庫的更新操作省略其他現在我們可以啟動服務器,在上測試下效果了。 showImg(https://segmentfault.com/img/remote/1460000019142304?w=893&h=438); 看完復聯四,我整理了這份 Gr...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請點擊集前端最近很火的框架資源定時更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語。葉上初陽乾宿雨,水面清圓,一一風荷舉。家住吳門,久作長安旅。五月漁郎相憶否。小楫輕舟,夢入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請::點擊::集web前端最近很火的vue2框架資源;定時更新,歡迎 Star 一下。 蘇...
閱讀 1267·2021-09-22 15:18
閱讀 2595·2021-09-22 15:17
閱讀 2222·2019-08-30 15:55
閱讀 1572·2019-08-30 15:54
閱讀 1036·2019-08-30 13:12
閱讀 622·2019-08-30 13:12
閱讀 1675·2019-08-29 11:33
閱讀 1434·2019-08-26 17:04