超文本傳輸協定 (HTTP) 是網路的生命。每次傳輸文件或發出 AJAX 請求時都會使用它。但令人驚訝的是,HTTP 在一些 Web 開發人員中相對陌生。 本介紹將示範 REST 設計原則集如何支撐 HTTP。您將學習如何透過建立幾乎可以在任何裝置或作業系統上使用的介面來發揮其全部功能。 Envato Market 還擁有數千個有用的程式碼腳本、外掛程式和應用程式來幫助您進行Web 開發,例如Premium URL Shortener,這是一個PHP 腳本,您可以將其安裝在伺服器上以建立自訂縮短的URL。 #為什麼要休息? REST 是一種組織獨立系統之間互動的簡單方法。自 2005 年以來,它越來越受歡迎,並激發了 Twitter API 等服務的設計。這是因為 REST 允許您以最小的開銷與手機和其他網站等各種客戶進行互動。理論上,REST 並不依賴 Web,但它幾乎總是這樣實現,並且受到 HTTP 的啟發。因此,REST 可以用在任何可以使用 HTTP 的地方。 另一種選擇是在 HTTP 之上建立相對複雜的約定。通常,這會表現為全新的語言。最傑出的例子是 SOAP 和 GraphQL。您必須學習一套全新的約定,但您永遠不會充分利用 HTTP。由於 REST 受到 HTTP 的啟發並發揮了其優勢,因此它是了解 HTTP 運作原理的最佳方式。 初步概述後,我們將檢查每個 HTTP 建構塊:URL、HTTP 動詞和回應碼。我們也將回顧如何以 RESTful 方式使用它們。在此過程中,我們將透過範例應用程式來說明該理論,該應用程式模擬透過 Web 介面追蹤與公司客戶相關的資料的過程。 HTTP HTTP 是允許在網路上來回傳送文件的協定。協議是一組規則,用於確定可以交換哪些訊息以及哪些訊息是對其他訊息的適當回應。另一個常見的協定是 POP3,您可以使用它來取得硬碟上的電子郵件。 在HTTP中,有兩種不同的角色:伺服器和客戶端。一般來說,談話總是由客戶發起;伺服器回覆。 HTTP 是基於文字的;也就是說,訊息本質上是文字位,儘管訊息正文也可以包含其他媒體。使用文字可以輕鬆監控 HTTP 交換。 HTTP 訊息由標頭和正文組成。身體常常是空的;它包含您想要透過網路傳輸的數據,以便根據標頭中的說明使用它。 header包含元數據,例如編碼資訊;但是,對於請求,它還包含重要的 HTTP 方法。在 REST 風格中,您會發現標頭資料通常比正文更重要。 在工作中監視 HTTP 如果您使用 Chrome 或 Firefox 開發者工具,請點擊頂部欄上的網頁以查看您目前所在網站中的 HTTP 請求。您可能需要在開啟的網頁開發人員工具的情況下刷新頁面才能查看日誌。例如: #熟悉 HTTP 的另一個有用方法是使用專用客戶端,例如 cURL。 cURL 是一個命令列工具,可在所有主要作業系統上使用。 安裝 cURL 後,輸入: curl -v google.com 登入後複製 這將顯示完整的 HTTP 對話。請求前面是 >,而回應前面是 <<。 網址 URL 是您識別要操作的內容的方式。我們說每個 URL 標識一個資源。這些 URL 與指派給網頁的 URL 完全相同。事實上,網頁是一種資源。 讓我們舉一個更奇特的例子,考慮我們的範例應用程序,它管理公司的客戶清單。 /clients 將識別所有客戶端,而 /clients/jim 將識別名為「Jim」的客戶端,假設他是唯一具有該名稱的客戶端。 在這些範例中,我們通常不會在 URL 中包含主機名,因為從介面組織方式的角度來看,它無關緊要。儘管如此,主機名稱對於確保資源識別碼在整個網路上是唯一的很重要。我們經常說您將資源請求傳送到主機。主機與資源路徑分開包含在標頭中,資源路徑位於請求標頭的正上方: GET /clients/jim HTTP/1.1 Host: example.com 登入後複製 資源最好被視為名詞。例如,以下內容不是 RESTful: /clients/add 登入後複製 這是因為它使用 URL 來描述操作。這是區分 RESTful 和非 RESTful 系統的一個相當基本的點。 最後,URL 應根據需要盡可能精確;唯一標識資源所需的所有內容都應包含在 URL 中。您不需要在請求中包含標識資源的資料。這樣,URL 就可以充當應用程式處理的所有資料的完整映射。 但是如何指定操作呢?例如,您怎么说您希望创建而不是检索新的客户记录?这就是 HTTP 动词发挥作用的地方。 HTTP 动词 每个请求在请求标头中指定特定的 HTTP 动词或方法。这是请求标头中的第一个全大写单词。例如,GET / HTTP/1.1 表示正在使用 GET 方法,而 DELETE /clients/anne HTTP/1.1 表示正在使用 DELETE 方法。 HTTP 动词告诉服务器如何处理 URL 标识的数据。请求可以选择在其正文中包含执行操作可能需要的其他信息,例如您想要与资源一起存储的数据。您可以使用 -d 选项在 cURL 中提供此数据。 如果您曾经创建过 HTML 表单,您就会熟悉两个最重要的 HTTP 动词:GET 和 POST。但可用的 HTTP 动词要多得多。构建 RESTful API 最重要的是 GET、POST、PUT 和 DELETE。还有其他方法,例如 HEAD 和 OPTIONS,但比较少见。如果您想了解所有其他 HTTP 方法,官方来源是 IETF。 获取 GET 是最简单的 HTTP 请求方法,浏览器每次单击链接或在地址栏中输入 URL 时都会使用该方法。它指示服务器将 URL 标识的数据传输到客户端。切勿因 GET 请求而在服务器端修改数据。从这个意义上说,GET 请求是只读的,但是当然,一旦客户端收到数据,它就可以自由地在自己这边对其进行任何操作 - 例如,将其格式化以供显示。 放置 当您希望创建或更新由 URL 标识的资源时,将使用 PUT 请求。例如, PUT /clients/robin 可能会在服务器上创建一个名为 Robin 的客户端。您会注意到 REST 完全与后端无关;请求中没有任何内容告诉服务器应该如何创建数据——只是告诉服务器应该如何创建数据。这使您可以在需要时轻松更换后端技术。 PUT 请求包含在主体中更新或创建资源时使用的数据。在 cURL 中,您可以使用 -d 开关将数据添加到请求: curl -v -X PUT -d "some text" 登入後複製 删除 DELETE 应该执行与 PUT 相反的操作;当您想要删除由请求的 URL 标识的资源时,应该使用它。 curl -v -X DELETE /clients/anne 登入後複製 这将删除与该资源关联的所有数据,由 /clients/anne 标识。 发布 POST 当您希望在服务器上进行的处理应该重复时使用,如果 POST 请求被重复(也就是说,它们不是幂等;更多内容请参见下文) )。此外,POST 请求应导致将请求正文作为您要发布到的 URL 的下属进行处理。 简单来说,POST /clients/ 不应导致 /clients/ 本身的资源被修改,而是 URL 以 /clients/ 开头的资源被修改。例如,它可以将一个新客户端附加到列表中,并使用服务器生成的 id: /clients/some-unique-id 登入後複製 PUT 请求很容易使用,而不是 POST 请求,反之亦然。有些系统仅使用一个,有些系统使用 POST 进行创建操作,使用 PUT 进行更新操作(因为 PUT 要求您始终提供完整的 URL),有些系统甚至使用 POST 进行更新,并使用 PUT用于创建。 通常,POST 请求用于触发服务器上不符合 Create/Update/Delete 范例的操作,但这超出了 REST 的范围。在我们的示例中,我们将始终使用 PUT。 HTTP 方法分类 安全和不安全的方法 安全方法是那些从不修改资源的方法。从上面列出的四种方法中,唯一安全的方法是 GET。其他的不安全,因为它们可能会导致资源的修改。 幂等方法 无论重复请求多少次,这些方法都会获得相同的结果:它们是 GET、PUT 和 DELETE。唯一的非幂等方法是 POST。 PUT 和 DELETE 被认为是幂等的可能会令人惊讶,但这很容易解释。使用相同的主体重复 PUT 方法应该以与上一个 PUT 请求中描述的方式相同的方式修改资源:不会有任何改变!同样,删除一个资源两次也是没有意义的。因此,无论重复 PUT 或 DELETE 请求多少次,结果都应该与只执行一次相同。 记住:最终决定使用某种 HTTP 方法时会发生什么情况的是程序员。 HTTP 实现中没有任何固有的东西会自动导致资源被创建、列出、删除或更新。您必须小心地正确应用 HTTP 协议并自行强制执行这些语义。 表示 我们可以通过以下方式总结到目前为止所学到的知识:HTTP 客户端和 HTTP 服务器交换有关由 URL 标识的资源的信息。 我们说请求和响应包含资源的表示。通过表示,我们指的是某种格式的有关资源状态或该状态未来应如何的信息。标头和正文都是表示的一部分。 包含元数据的 HTTP 标头由 HTTP 规范严格定义;它们只能包含纯文本,并且必须以某种方式格式化。 正文可以包含任何格式的数据,这就是 HTTP 真正发挥作用的地方。您知道可以用任何人类语言发送纯文本、图片、HTML 和 XML。通过请求元数据或不同的 URL,您可以选择同一资源的不同表示形式。例如,您可以将网页发送到浏览器,并将 JSON 发送到应用程序。 HTTP 响应应指定正文的内容类型。这是在标头的 Content-Type 字段中完成的。例如: Content-Type: application/json 登入後複製 为简单起见,我们的示例应用程序仅来回发送 JSON,但应用程序的设计方式应使您可以轻松更改数据格式,以适应不同的客户端或用户偏好。 HTTP 客户端库 要尝试不同的请求方法,您需要一个客户端,它允许您指定要使用的方法。不幸的是,HTML 表单不符合要求,因为它们只允许您发出 GET 和 POST 请求。在现实生活中,API 是通过单独的客户端应用程序或通过浏览器中的 JavaScript 以编程方式访问的。 这就是为什么除了服务器之外,在您选择的编程语言中拥有良好的 HTTP 客户端功能也很重要。 一个非常流行的 HTTP 客户端库是 cURL。您已经在本教程前面熟悉了 cURL 命令。 cURL 包括一个独立的命令行程序和一个可供各种编程语言使用的库。特别是,cURL 通常是 PHP 开发人员选择的 HTTP 客户端解决方案。其他语言(例如 Python)提供更多本机 HTTP 客户端库。 设置示例应用程序 现在我们将构建一个准系统示例应用程序。您可以按照相应部分并使用代码附件中的相应文件夹,在 Node.js 或 PHP 中构建示例应用程序。两个应用程序的工作方式相同。如果您不确定选择哪个,Node.js 可能是更好的选择,因为它现在更常用。 Node.js 和 Express 为了运行示例应用程序,您需要安装 Node.js。完成后,打开源代码附件中的 node.js 目录并运行 npm install。 PHP 要运行示例应用程序,您需要安装 PHP 5 和具有某种运行 PHP 机制的 Web 服务器。当前版本必须至少为 5.2 版本才能访问 json_encode() 和 json_decode() 函数。 对于服务器,最常见的选择仍然是 Apache 和 mod_php,但您可以自由地使用您喜欢的任何替代方案。有一个 Apache 配置示例,其中包含重写规则,可帮助您快速设置应用程序。对任何以 /clients/ 开头的 URL 的所有请求都必须路由到我们的 server.php 文件。 在 Apache 中,您需要启用 mod_rewrite 并将提供的 mod_rewrite 配置放在您的 Apache 配置或 .htacess 中的某个位置文件。这样,server.php 将响应来自服务器的所有请求。必须使用 Nginx 或您决定使用的任何替代服务器来实现同样的效果。 示例应用程序如何工作 Node.js 和 Express 如果你查看代码,你会看到一些不同的方法,例如 app.get 或 app.put。这些是不同的路线。每个路由都匹配特定的 URL 和 HTTP 方法。 app.get("/clients", (_, res) => { ... }); app.get("/clients/:client", (req, res) => { ... }); app.put("/clients/:client", (req, res) => { ... }); app.delete("/clients/:client", (req, res) => { ... }); 登入後複製 您可能已经注意到 URL 中的 :client 。这是一个参数,这意味着 URL 该部分中的任何内容都将与该路由匹配,并且 URL 该部分将作为参数传递。在路由处理函数内部,您可以看到描述逻辑的注释。最后是app.listen。 app.listen(port, () => { console.log(`Example app listening on port ${port}`); }); 登入後複製 这将在 port 指定的端口启动服务器。回调函数在服务器启动后执行。 PHP 以 REST 方式处理请求有两个关键。第一个关键是根据 HTTP 方法启动不同的处理 - 即使 URL 相同。在 PHP 中,$_SERVER 全局数组中有一个变量,用于确定使用哪种方法发出请求: $_SERVER['REQUEST_METHOD'] 登入後複製 此变量包含字符串形式的方法名称,例如 'GET'、'PUT' 等。 另一个关键是知道请求的是哪个 URL。为此,我们使用另一个标准 PHP 变量: $_SERVER['REQUEST_URI'] 登入後複製 此变量包含从第一个正斜杠开始的 URL。例如,如果主机名是 example.com,则 'https://example.com/' 将返回 '/',而 'http://example.com/test/ ' 将返回 '/test/'。 我们首先尝试确定调用了哪个 URL。我们只考虑以 'clients' 开头的 URL。其他均无效。 $resource = array_shift($paths); if ($resource == 'clients') { $name = array_shift($paths); if (empty($name)) { $this->handle_base($method); } else { $this->handle_name($method, $name); } } else { // We only handle resources under 'clients' header('HTTP/1.1 404 Not Found'); } 登入後複製 我们有两种可能的结果: 资源就是客户,在这种情况下,我们会返回完整的列表。 还有一个进一步的标识符。 如果有其他标识符,我们假设它是客户端的名称,并再次将其转发到不同的函数,具体取决于 方法 。我们使用 switch 语句,在实际应用程序中应该避免这种情况: switch($method) { case 'PUT': $this->create_contact($name); break; case 'DELETE': $this->delete_contact($name); break; case 'GET': $this->display_contact($name); break; default: header('HTTP/1.1 405 Method Not Allowed'); header('Allow: GET, PUT, DELETE'); break; } 登入後複製 响应代码 您可能已经注意到,示例应用程序使用 PHP header(),传递一些看起来奇怪的字符串作为参数。 header() 函数打印 HTTP headers 并确保它们的格式正确。标头应该是响应中的第一件事,因此在完成标头之前不应输出任何其他内容。有时,除了您在代码中指定的标头之外,您的 HTTP 服务器可能会配置为添加其他标头。 标头包含各种元信息,例如消息正文中使用的文本编码或正文内容的 MIME 类型。在本例中,我们显式指定 HTTP 响应代码。 HTTP 响应代码标准化了一种通知客户端请求结果的方法。默认情况下,PHP返回200响应码,表示响应成功。 服务器应该返回最合适的HTTP响应代码;这样,客户端可以尝试修复其错误(假设有任何错误)。大多数人都熟悉常见的 404 Not Found 响应代码,但还有更多可用的代码可以适应各种情况。 请记住,HTTP 响应代码的含义并不是非常精确;这是 HTTP 本身相当通用的结果。您应该尝试使用与当前情况最匹配的响应代码。话虽如此,如果您找不到完全合适的产品,也不必太担心。 以下是一些经常与 REST 一起使用的 HTTP 响应代码: 200 好的 此响应代码表明请求成功。 201已创建 这表明请求成功并且资源已创建。用于确认 PUT 或 POST 请求是否成功。 400 错误请求 请求格式错误。当数据未通过验证或格式错误时,尤其是在 POST 和 PUT 请求中,会发生这种情况。 404 未找到 此响应表明无法找到所需的资源。这通常会返回给所有指向没有相应资源的 URL 的请求。 401 未经授权 此错误表明您需要在访问资源之前执行身份验证。 405 方法不允许 此资源不支持所使用的 HTTP 方法。 409 冲突 这表明存在冲突。例如,您使用 PUT 请求两次创建相同的资源。 500 内部服务器错误 当一切都失败时;一般情况下,500响应是由于服务器端出现意外情况导致处理失败,导致服务器出错时使用的。 练习示例应用程序 我们首先从应用程序中获取信息。我们需要客户端 'jim' 的详细信息,因此让我们向此资源的 URL 发送一个简单的 GET 请求: curl -v http://localhost:80/clients/jim 登入後複製 这将显示完整的消息标题。响应中的最后一行将是消息正文;在本例中,它将是包含 Jim 地址的 JSON(请记住,省略方法名称将导致 GET 请求;同时将 localhost:80 替换为您正在使用的服务器名称和端口)。 接下来,我们可以一次性获取所有客户的信息: curl -v http://localhost:80/clients/ 登入後複製 然后我们创建一个新客户端,名为 Paul: curl -v -X "PUT" http://localhost:80/clients/paul -d '{"address":"Sunset Boulevard" }' -H 'content-type: application/json' 登入後複製 现在您将收到包含 Paul 的所有客户的列表作为确认。 最后,删除客户端: curl -v -X "DELETE" http://localhost:80/clients/anne 登入後複製 您会发现返回的JSON不再包含有关Anne的任何数据。 如果您尝试检索不存在的客户端,例如: curl -v http://localhost:80/clients/jerry 登入後複製 您将收到 404 错误,而如果您尝试创建已存在的客户端: curl -v -X "PUT" http://localhost:80/clients/anne 登入後複製 您将收到 409 错误。 结论 重要的是要记住,HTTP 被设计为在除了对协议的理解之外不共享任何内容的系统之间进行通信。一般来说,您对 HTTP 之外的假设越少越好:这允许最广泛的程序和设备访问您的 API。 我在本教學中使用了 PHP,因為它很可能是 Envato Tuts 讀者最熟悉的語言。也就是說,PHP 雖然是為Web 設計的,但在以REST 方式工作時可能不是最好的語言,因為它處理PUT 請求的方式與GET 和POST 完全不同。 除了 PHP 和 Node.js,您還可以考慮以下內容: 尋求更高效能的網路伺服器。 各種 Ruby 框架(Rails 和 Sinatra)。 Python,因為它具有良好的 REST 支援。普通 Django 和 WebOb 或 Werkzeug 應該可以工作。 在嘗試遵守 REST 原則的應用程式中,典型的例子是 Atom 發布協議,儘管老實說它在實踐中使用得併不頻繁。基於充分使用 HTTP 理念建構的現代應用程序,請參閱 Apache CouchDB。 玩得開心! 本文已根據雅各傑克森 (Jacob Jackson) 的貢獻進行了更新。 Jacob 是一位 Web 開發人員、技術作家和頻繁的開源貢獻者。 #