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