日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

REST API 是當今可用的最常見的 Web 服務類型之一。它們允許包括瀏覽器應用程序在內的各種客戶端通過 REST API 與服務器通信。因此,正確設計 REST API 非常重要,這樣我們以后就不會遇到問題。我們必須考慮 API 使用者的安全性、性能和易用性。

否則,我們會為使用我們 API 的客戶制造問題,這不愉快并且會影響人們使用我們的 API。如果我們不遵循普遍接受的約定,那么我們就會混淆 API 的維護者和使用它們的客戶端,因為它與每個人的期望不同。

在本文中,我們將研究如何設計 REST API,使其對任何使用它們的人都易于理解、面向未來、安全且快速,因為它們向可能是機密的客戶端提供數據。

  • 接受并使用 JSON 響應
  • 在端點路徑中使用名詞而不是動詞
  • 用復數名詞命名集合
  • 分層對象的嵌套資源
  • 優雅地處理錯誤并返回標準錯誤代碼
  • 允許過濾、排序和分頁
  • 保持良好的安全實踐
  • 緩存數據以提高性能
  • 版本控制我們的 API

什么是 REST API?

REST API 是符合特定架構約束的應用程序編程接口,例如無狀態通信和可緩存數據。它不是協議或標準。雖然可以通過多種通信協議訪問 REST API,但最常見的是通過 HTTPS 調用它們,因此以下指南適用于將通過 Internet 調用的 REST API 端點。

注意:對于通過 Internet 調用的 REST API,您需要遵循REST API 身份驗證的最佳實踐。

接受并使用 JSON 響應

REST API 應該接受 JSON 作為請求負載,并發送響應到 JSON。JSON 是傳輸數據的標準。幾乎所有聯網技術都可以使用它:JAVAScript 具有通過 Fetch API 或其他 HTTP 客戶端對 JSON 進行編碼和解碼的內置方法。服務器端技術具有無需做太多工作即可解碼 JSON 的庫。

還有其他方法可以傳輸數據。如果不將數據自己轉換為可以使用的東西(通常是 JSON),框架就不會廣泛支持 XML。我們無法在客戶端輕松操作這些數據,尤其是在瀏覽器中。只是為了進行正常的數據傳輸,最終需要做很多額外的工作。

表單數據有利于發送數據,特別是如果我們要發送文件。但是對于文本和數字,我們不需要表單數據來傳輸它們,因為對于大多數框架,我們可以通過直接在客戶端獲取數據來傳輸 JSON。這是迄今為止最直接的做法。

為確保當我們的 REST API 應用程序使用 JSON 響應時客戶端將其解釋為這樣,我們應該Content-Type在響應標頭中設置為Application/json在發出請求之后。許多服務器端應用程序框架會自動設置響應標頭。一些 HTTP 客戶端查看Content-Type響應標頭并根據該格式解析數據。

唯一的例外是如果我們嘗試在客戶端和服務器之間發送和接收文件。然后我們需要處理文件響應并將表單數據從客戶端發送到服務器。但這是另一個話題。

我們還應該確保我們的端點返回 JSON 作為響應。許多服務器端框架將此作為內置功能。

讓我們看一個接受 JSON 有效負載的示例 API。此示例將使用Node.js的Express后端框架。我們可以使用body-parser中間件來解析 JSON 請求體,然后我們可以res.json使用我們想要作為 JSON 響應返回的對象調用該方法,如下所示:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());

app.post('/', (req, res) => {
  res.json(req.body);
});

app.listen(3000, () => console.log('server started'));

bodyParser.json()將 JSON 請求正文字符串解析為 JavaScript 對象,然后將其分配給該req.body對象。

將Content-Type響應中的標頭設置為application/json; charset=utf-8不做任何更改。上述方法適用于大多數其他后端框架。

在端點路徑中使用名詞而不是動詞

我們不應該在端點路徑中使用動詞。相反,我們應該使用代表我們正在檢索或操作的端點的實體的名詞作為路徑名。

這是因為我們的 HTTP 請求方法已經有了動詞。在我們的 API 端點路徑中包含動詞沒有用處,而且由于它沒有傳達任何新信息,它會導致不必要的冗長。選擇的動詞可能會因開發人員的突發奇想而有所不同。例如,有些喜歡“get”,有些喜歡“retrieve”,所以最好讓 HTTP GET 動詞告訴我們端點做什么。

該操作應由我們正在制作的 HTTP 請求方法指示。最常見的方法包括 GET、POST、PUT 和 DELETE。

  • GET 檢索資源。
  • POST 向服務器提交新數據。
  • PUT 更新現有數據。
  • DELETE 刪除數據。

動詞映射到CRUD操作。

考慮到我們上面討論的兩個原則,我們應該創建像 GET 這樣的路由/articles/來獲取新聞文章。同樣,POST/articles/用于添加新文章,PUT/articles/:id用于使用給定的更新文章id。DELETE/articles/:id用于刪除具有給定 ID 的現有文章。

/articles表示 REST API 資源。例如,我們可以使用 Express 添加以下端點來操作文章,如下所示:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());

app.get('/articles', (req, res) => {
  const articles = [];
  // code to retrieve an article...
  res.json(articles);
});

app.post('/articles', (req, res) => {
  // code to add a new article...
  res.json(req.body);
});

app.put('/articles/:id', (req, res) => {
  const { id } = req.params;
  // code to update an article...
  res.json(req.body);
});

app.delete('/articles/:id', (req, res) => {
  const { id } = req.params;
  // code to delete an article...
  res.json({ deleted: id });
});

app.listen(3000, () => console.log('server started'));

在上面的代碼中,我們定義了操作文章的端點。正如我們所見,路徑名中沒有任何動詞。我們只有名詞。動詞在 HTTP 動詞中。

POST、PUT 和 DELETE 端點都將 JSON 作為請求體,它們都返回 JSON 作為響應,包括 GET 端點。

在端點上使用邏輯嵌套

在設計端點時,將包含相關信息的端點分組是有意義的。也就是說,如果一個對象可以包含另一個對象,您應該設計端點來反映這一點。無論您的數據在數據庫中的結構是否如此,這都是一種很好的做法。事實上,避免在端點中鏡像數據庫結構以避免給攻擊者提供不必要的信息可能是明智的。

例如,如果我們希望端點獲取新聞文章的評論,我們應該將/comments路徑附加到路徑的末尾/articles。我們可以在 Express 中使用以下代碼來做到這一點:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());

app.get('/articles/:articleId/comments', (req, res) => {
  const { articleId } = req.params;
  const comments = [];
  // code to get comments by articleId
  res.json(comments);
});


app.listen(3000, () => console.log('server started'));

在上面的代碼中,我們可以在 path 上使用 GET 方法'
/articles/:articleId/comments'。我們獲取comments由標識的文章articleId,然后在響應中返回它。我們'comments'在'/articles/:articleId'路徑段之后添加以表明它是 的子資源/articles。

這是有道理的,因為comments是 的子對象articles,假設每篇文章都有自己的評論。否則,用戶會感到困惑,因為這種結構通常被認為是用于訪問子對象。同樣的原則也適用于 POST、PUT 和 DELETE 端點。它們都可以對路徑名使用相同類型的嵌套結構。

但是,嵌套可能會走得太遠。在大約第二或第三級之后,嵌套端點可能會變得笨拙。相反,請考慮將 URL 返回到這些資源,特別是如果該數據不一定包含在頂級對象中。

例如,假設您想返回特定評論的作者。你可以使用
/articles/:articleId/comments/:commentId/author. 但這已經失控了。而是在 JSON 響應中返回該特定用戶的 URI:

"author": "/users/:userId"

優雅地處理錯誤并返回標準錯誤代碼

為了在錯誤發生時消除 API 用戶的困惑,我們應該優雅地處理錯誤并返回指示發生了哪種錯誤的 HTTP 響應代碼。這為 API 的維護者提供了足夠的信息來了解發生的問題。我們不希望錯誤導致我們的系統崩潰,所以我們可以不處理它們,這意味著 API 使用者必須處理它們。

常見的錯誤 HTTP 狀態代碼包括:

  • 400 Bad Request – 這意味著客戶端輸入驗證失敗。
  • 401 Unauthorized – 這意味著用戶無權訪問資源。它通常在用戶未通過身份驗證時返回。
  • 403 Forbidden - 這意味著用戶已通過身份驗證,但不允許訪問資源。
  • 404 Not Found – 這表示未找到資源。
  • 500 內部服務器錯誤 – 這是一般的服務器錯誤。它可能不應該明確拋出。
  • 502 Bad Gateway - 這表示來自上游服務器的無效響應。
  • 503 Service Unavailable - 這表明服務器端發生了意外(可能是服務器過載,系統某些部分發生故障等)。

我們應該拋出與我們的應用程序遇到的問題相對應的錯誤。例如,如果我們想拒絕來自請求負載的數據,那么我們應該在 Express API 中返回一個 400 響應,如下所示:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

// existing users
const users = [
  { email: '[email protected]' }
]

app.use(bodyParser.json());

app.post('/users', (req, res) => {
  const { email } = req.body;
  const userExists = users.find(u => u.email === email);
  if (userExists) {
    return res.status(400).json({ error: 'User already exists' })
  }
  res.json(req.body);
});


app.listen(3000, () => console.log('server started'));

在上面的代碼中,我們users在給定電子郵件的數組中有一個現有用戶的列表。

然后,如果我們嘗試使用email中已存在的值提交有效負載users,我們將獲得一個 400 響應狀態碼和一條'User already exists'消息,讓用戶知道該用戶已經存在。使用該信息,用戶可以通過將電子郵件更改為不存在的內容來更正操作。

錯誤代碼需要帶有消息,以便維護者有足夠的信息來解決問題,但攻擊者不能使用錯誤內容來進行我們的攻擊,如竊取信息或關閉系統。

每當我們的 API 沒有成功完成時,我們應該通過發送錯誤信息來幫助用戶做出糾正措施,從而優雅地失敗。

允許過濾、排序和分頁

REST API 背后的數據庫可能會變得非常大。有時,有太多的數據不應該一次全部返回,因為它太慢或者會導致我們的系統崩潰。因此,我們需要過濾項目的方法。

我們還需要對數據進行分頁的方法,以便一次只返回幾個結果。我們不想通過嘗試一次獲取所有請求的數據來占用資源太久。

過濾和分頁都通過減少服務器資源的使用來提高性能。隨著數據庫中積累的數據越多,這些特征就越重要。

這是一個小示例,其中 API 可以接受具有各種查詢參數的查詢字符串,以便我們按字段過濾項目:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

// employees data in a database
const employees = [
  { firstName: 'Jane', lastName: 'Smith', age: 20 },
  //...
  { firstName: 'John', lastName: 'Smith', age: 30 },
  { firstName: 'Mary', lastName: 'Green', age: 50 },
]

app.use(bodyParser.json());

app.get('/employees', (req, res) => {
  const { firstName, lastName, age } = req.query;
  let results = [...employees];
  if (firstName) {
    results = results.filter(r => r.firstName === firstName);
  }

  if (lastName) {
    results = results.filter(r => r.lastName === lastName);
  }

  if (age) {
    results = results.filter(r => +r.age === +age);
  }
  res.json(results);
});

app.listen(3000, () => console.log('server started'));

在上面的代碼中,我們有req.query獲取查詢參數的變量。然后,我們通過使用 JavaScript 解構語法將各個查詢參數解構為變量來提取屬性值。最后,我們filter繼續使用每個查詢參數值來定位我們想要返回的項目。

完成此操作后,我們將返回results作為響應。因此,當我們使用查詢字符串向以下路徑發出 GET 請求時:

/employees?lastName=Smith&age=30

我們得到:

[
    {
        "firstName": "John",
        "lastName": "Smith",
        "age": 30
    }
]

作為我們過濾后的返回響應lastName和age。

同樣,我們可以接受page查詢參數并返回位置 from (page - 1) * 20to中的一組條目page * 20。

我們還可以在查詢字符串中指定要排序的字段。例如,我們可以從帶有我們想要對其數據進行排序的字段的查詢字符串中獲取參數。然后我們可以按這些單獨的字段對它們進行排序。

例如,我們可能想從如下 URL 中提取查詢字符串:

http://example.com/articles?sort=+author,-datepublished

where+表示上升,-表示下降。所以我們按照作者姓名的字母順序,datepublished從最近到最近的排序。

保持良好的安全實踐

客戶端和服務器之間的大多數通信應該是私密的,因為我們經常發送和接收私人信息。因此,必須使用 SSL/TLS 來確保安全。

將 SSL 證書加載到服務器上并不難,而且成本免費或非常低。沒有理由不讓我們的 REST API 通過安全通道而不是公開方式進行通信。

人們不應該能夠訪問他們請求的更多信息。例如,普通用戶不應該能夠訪問其他用戶的信息。他們也不應該能夠訪問管理員的數據。

為了執行最小權限原則,我們需要為單個角色添加角色檢查,或者為每個用戶添加更細化的角色。

如果我們選擇將用戶分組為幾個角色,那么這些角色應該具有涵蓋他們需要的所有權限,僅此而已。如果我們對用戶可以訪問的每個功能都擁有更精細的權限,那么我們必須確保管理員可以相應地從每個用戶添加和刪除這些功能。此外,我們需要添加一些可以應用于組用戶的預設角色,這樣我們就不必手動為每個用戶執行此操作。

緩存數據以提高性能

我們可以添加緩存以從本地內存緩存返回數據,而不是每次想要檢索用戶請求的一些數據時都查詢數據庫來獲取數據。緩存的好處是用戶可以更快地獲取數據。但是,用戶獲得的數據可能已經過時。當我們不斷看到舊數據時出現問題時,這也可能導致在生產環境中調試時出現問題。

緩存解決方案有很多種,比如redis、內存緩存等等。我們可以隨著需求的變化而改變緩存數據的方式。

例如,Express 具有apicache無需太多配置即可將緩存添加到我們的應用程序的中間件。我們可以像這樣在我們的服務器中添加一個簡單的內存緩存:

const express = require('express');
const bodyParser = require('body-parser');
const apicache = require('apicache');
const app = express();
let cache = apicache.middleware;
app.use(cache('5 minutes'));

// employees data in a database
const employees = [
  { firstName: 'Jane', lastName: 'Smith', age: 20 },
  //...
  { firstName: 'John', lastName: 'Smith', age: 30 },
  { firstName: 'Mary', lastName: 'Green', age: 50 },
]

app.use(bodyParser.json());

app.get('/employees', (req, res) => {
  res.json(employees);
});

app.listen(3000, () => console.log('server started'));

上面的代碼只是引用了apicache中間件,apicache.middleware然后我們有:

app.use(cache('5 minutes'))

將緩存應用于整個應用程序。例如,我們將結果緩存五分鐘。我們可以根據需要進行調整。

如果您使用緩存,您還應該Cache-Control在標題中包含信息。這將幫助用戶有效地使用您的緩存系統。

版本控制我們的 API

如果我們對它們進行任何可能破壞客戶端的更改,我們應該有不同版本的 API。可以像現在大多數應用程序一樣,根據語義版本(例如,2.0.6 表示主要版本 2 和第六個補丁)進行版本控制。

這樣,我們可以逐步淘汰舊的端點,而不是強迫每個人同時遷移到新的 API。v1 端點可以為不想改變的人保持活躍,而 v2 具有閃亮的新功能,可以為那些準備升級的人提供服務。如果我們的 API 是公開的,這一點尤其重要。我們應該對它們進行版本控制,這樣我們就不會破壞使用我們 API 的第三方應用程序。

版本通常有做/v1/,/v2/等在API路徑的開始增加。

例如,我們可以使用 Express 執行此操作,如下所示:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());

app.get('/v1/employees', (req, res) => {
  const employees = [];
  // code to get employees
  res.json(employees);
});

app.get('/v2/employees', (req, res) => {
  const employees = [];
  // different code to get employees
  res.json(employees);
});

app.listen(3000, () => console.log('server started'));

我們只需將版本號添加到端點 URL 路徑的開頭即可對其進行版本控制。

結論

設計高質量 REST API 最重要的一點是通過遵循 Web 標準和約定來保持一致性。JSON、SSL/TLS 和 HTTP 狀態代碼都是現代 Web 的標準構建塊。

性能也是一個重要的考慮因素。我們可以通過不一次返回太多數據來增加它。此外,我們可以使用緩存,這樣我們就不必一直查詢數據。

端點的路徑應該是一致的,我們只使用名詞,因為 HTTP 方法表明我們想要采取的行動。嵌套資源的路徑應該在父資源的路徑之后。他們應該告訴我們我們正在獲取或操作什么,而無需閱讀額外的文檔來了解它在做什么。

分享到:
標簽:API
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定