工程化 : JavaScript工程項目的一系列最佳實踐策略

JavaScript工程項目的一系列最佳實踐策略

當您在青蔥的田野裡翻滾一般歡樂(而不受約束)地開發一個新項目,對其他人而言維護這樣一個項目簡直就是一個潛在的可怕的噩夢。以下列出的指南是我們在elsewhen的大多數JavaScript項目中發現,撰寫和收集的最佳實踐(至少我們是這樣認為的)。如果您想分享其他最佳實踐,或者認為其中一些指南應該刪除。

目錄

  • Git
  • 一些git規則
  • Git工作流
  • 編寫良好的提交備註信息
  • 文檔
  • 環境
  • 一致的開發環境
  • 一致性的依賴配置
  • 依賴
  • 測試
  • 結構與命名規則
  • 代碼風格
  • 一些代碼風格指南
  • 強制性的代碼風格規範
  • 日誌
  • API
  • API 設計
  • API 安全
  • API 文檔
  • 許可

1. Git

工程化 : JavaScript工程項目的一系列最佳實踐策略

1.1 一些Git規則

這裡有一套規則要牢記:

在功能分支中執行開發工作。

為什麼:

因為這樣,所有的工作都是在專用的分支而不是在主分支上隔離完成的。它允許您提交多個 pull request 而不會導致混亂。您可以持續迭代提交,而不會使得那些很可能還不穩定而且還未完成的代碼汙染 master 分支。

從 develop 獨立出分支。

為什麼:

這樣,您可以保持 master 分支中的代碼穩定性,這樣就不會導致構建問題,並且幾乎可以直接用於發佈(當然,這可能對某些項目來說要求會比較高)。

永遠也不要將分支(直接)推送到 develop 或者 master ,請使用合併請求(Pull Request)。

為什麼:

通過這種方式,它可以通知整個團隊他們已經完成了某個功能的開發。這樣開發夥伴就可以更容易對代碼進行 code review,同時還可以互相討論所提交的需求功能。

在推送所開發的功能並且發起合併請求前,請更新您本地的develop分支並且完成交互式變基操作(interactive rebase)。

為什麼:

rebase 操作會將(本地開發分支)合併到被請求合併的分支( master 或 develop )中,並將您本地進行的提交應用於所有歷史提交的最頂端,而不會去創建額外的合併提交(假設沒有衝突的話),從而可以保持一個漂亮而乾淨的歷史提交記錄。

請確保在變基併發起合併請求之前解決完潛在的衝突。

合併分支後刪除本地和遠程功能分支。

為什麼:

如果不刪除需求分支,大量殭屍分支的存在會導致分支列表的混亂。而且該操作還能確保有且僅有一次合併到master 或 develop。只有當這個功能還在開發中時對應的功能分支才存在。

在進行合併請求之前,請確保您的功能分支可以成功構建,並已經通過了所有的測試(包括代碼規則檢查)。

為什麼:

因為您即將將代碼提交到這個穩定的分支。而如果您的功能分支測試未通過,那您的目標分支的構建有很大的概率也會失敗。此外,確保在進行合併請求之前應用代碼規則檢查。因為它有助於我們代碼的可讀性,並減少格式化的代碼與實際業務代碼更改混合在一起導致的混亂問題。

使用 這個 .gitignore 文件。

為什麼:

此文件已經囊括了不應該和您開發的代碼一起推送至遠程倉庫(remote repository)的系統文件列表。另外,此文件還排除了大多數編輯器的設置文件夾和文件,以及最常見的(工程開發)依賴目錄。

保護您的 develop 和 master 分支。

為什麼:

這樣可以保護您的生產分支免受意外情況和不可回退的變更。

1.2 Git 工作流

基於以上原因, 我們將 功能分支工作流 , 交互式變基的使用方法 結合一些 Gitflow中的基礎 (比如,命名和使用一個develop branch)一起使用。 主要步驟如下:

針對一個新項目, 在您的項目目錄初始化您的項目。 如果是(已有項目)隨後的功能開發/代碼變動,這一步請忽略。

cd

git init

檢出(Checkout) 一個新的功能或故障修復(feature/bug-fix)分支。

git checkout -b

新增代碼變更。

git add

git commit -a

為什麼:

git commit -a 會獨立啟動一個編輯器用來編輯您的說明信息,這樣的好處是可以專注於寫這些註釋說明。更多請閱讀 章節 1.3。

保持與遠程(develop分支)的同步,以便(使得本地 develop 分支)拿到最新變更。

git checkout develop

git pull

為什麼:

當您進行(稍後)變基操作的時候,保持更新會給您一個在您的機器上解決衝突的機會。這比(不同步更新就進行下一步的變基操作並且)發起一個與遠程倉庫衝突的合併請求要好。

(切換至功能分支並且)通過交互式變基從您的develop分支中獲取最新的代碼提交,以更新您的功能分支。

git checkout <branchname>

git rebase -i --autosquash develop

為什麼:

您可以使用 —autosquash 將所有提交壓縮到單個提交。沒有人會願意(看到) develop 分支中的單個功能開發就佔據如此多的提交歷史。

如果沒有衝突請跳過此步驟,如果您有衝突, 就需要解決它們並且繼續變基操作。

git add <file1> <file2> .../<file2>/<file1>

git rebase --continue

推送您的(功能)分支。變基操作會改變提交歷史, 所以您必須使用 -f 強制推送到遠程(功能)分支。 如果其他人與您在該分支上進行協同開發,請使用破壞性沒那麼強的 —force-with-lease 參數。

git push -f

為什麼:

當您進行 rebase 操作時,您會改變功能分支的提交歷史。這會導致 Git 拒絕正常的 git push 。那麼,您只能使用 -f 或 —force 參數了。

提交一個合併請求(Pull Request)。

Pull Request 會被負責代碼審查的同事接受,合併和關閉。

如果您完成了開發,請記得刪除您的本地分支。

git branch -d

(使用以下代碼)刪除所有已經不在遠程倉庫維護的分支。

git fetch -p && for branch in `git branch -vv | grep ': gone]' | awk '{print $1}'`; do git branch -D $branch; done

1.3 如何寫好 Commit Message

堅持遵循關於提交的標準指南,會讓在與他人合作使用 Git 時更容易。這裡有一些經驗法則 (來源):

用新的空行將標題和主體兩者隔開。

為什麼:

Git 非常聰明,它可將您提交消息的第一行識別為摘要。實際上,如果您嘗試使用 git shortlog ,而不是 git log ,您會看到一個很長的提交消息列表,只會包含提交的 id 以及摘要(,而不會包含主體部分)。

將標題行限制為50個字符,並將主體中一行超過72個字符的部分折行顯示。

為什麼:

提交應儘可能簡潔明瞭,而不是寫一堆冗餘的描述。

標題首字母大寫。

不要用句號結束標題。

在標題中使用 祈使句 。

為什麼:

與其在寫下的信息中描述提交者做了什麼,不如將這些描述信息作為在這些提交被應用於該倉庫後將要完成的操作的一個說明。

使用主體部分去解釋 是什麼 和 為什麼 而不是 怎麼做。

2. 文檔

  • 可以使用這個 模板 作為 README.md (的一個參考), 隨時歡迎添加里面沒有的內容。
  • 對於具有多個存儲庫的項目,請在各自的 README.md 文件中提供它們的鏈接。
  • 隨項目的進展,持續地更新 README.md 。
  • 給您的代碼添加詳細的註釋,這樣就可以清楚每個主要部分的含義。
  • 如果您正在使用的某些代碼和方法,在github或stackoverflow上已經有公開討論,請在您的註釋中包含這些鏈接,
  • 不要把註釋作為壞代碼的藉口。保持您的代碼乾淨整潔。
  • 也不要把那些清晰的代碼作為不寫註釋的藉口。
  • 當代碼更新,也請確保註釋的同步更新。

3. 環境

如果需要,請分別定義 development, test 和 production 三個環境。

為什麼:

不同的環境可能需要不同的數據、token、API、端口等。您可能需要一個隔離的 development 環境,它調用 mock 的 API,mock 會返回可預測的數據,使自動和手動測試變得更加容易。或者您可能只想在 production 環境中才啟用 Google Analytics(分析)。

依據不同的環境變量加載部署的相關配置,不要將這些配置作為常量添加到代碼庫中, 看這個例子.

為什麼:

您會有令牌,密碼和其他有價值的信息。這些配置應正確地從應用程序內部分離開來,這樣代碼庫就可以隨時獨立發佈,不會包含這些敏感配置信息。 怎麼做: 使用 .env 文件來存儲環境變量,並將其添加到 .gitignore 中使得排除而不被提交(到倉庫)。另外,再提交一個 .env.example 作為開發人員的參考配置。對於生產環境,您應該依舊以標準化的方式設置環境變量。

建議您在應用程序啟動之前校驗一下環境變量。 看這個例子 ,它使用了 joi 去校驗提供的值。

為什麼:

它可能會將其他人從上小時的故障排查中解救。

3.1 一致的開發環境:

在 package.json 裡的 engines 中設置您的node版本。

為什麼:

讓其他人可以清晰的知道這個項目中用的什麼node版本。

另外,使用 nvm 並在您的項目根目錄下創建一個 .nvmrc 文件。不要忘了在文檔中標註。

為什麼:

任何使用nvm的人都可以使用 nvm use 來切換到合適的node版本。

最好設置一個檢查 node 和 npm 版本的 preinstall 腳本。

為什麼:

某些依賴項可能會在新版本的 npm 中安裝失敗。

如果可以的話最好使用 Docker 鏡像。

為什麼:

它可以在整個工作流程中為您提供一致的環境,而且不用花太多的時間來解決依賴或配置。

使用本地模塊,而不是使用全局安裝的模塊。

為什麼:

您不能指望您的同事在自己的全局環境都安裝了相應的模塊,本地模塊可以方便您分享您的工具。

3.2 依賴一致性:

確保您的團隊成員獲得與您完全相同的依賴。

為什麼:

因為您希望代碼在任何開發環境中運行都能像預期的一樣。

怎麼做:

在npm@5或者更高版本中使用 package-lock.json。

我們沒有 npm@5:

或者,您可以使用 yarn ,並確保在 README.md 中標註了使用 yarn 。您的鎖文件和package.json在每次依賴關係更新後應該具有相同的版本。

我不太喜歡 Yarn :

居然不喜歡 Yarn,太糟糕了。對於舊版本的npm,在安裝新的依賴關係時使用 -save —save-exact ,並在發佈之前創建 npm-shrinkwrap.json 。

4. 依賴

持續跟蹤您當前的可用依賴包: 舉個例子, npm ls —depth=0。

查看這些軟件包是否未使用或者與開發項目無關: depcheck。

為什麼:

您可能會在代碼中包含未使用的庫,這會增大生產包的大小。請搜索出這些未使用的依賴關係並去掉它們吧。

在使用依賴之前,請檢查他的下載統計信息,看看它是否被社區大量使用: npm-stat.

為什麼:

更多的使用量很大程度上意味著更多的貢獻者,這通常意味著擁有更好的維護,這些能確保錯誤能夠被快速地發現並修復。

在使用依賴之前,請檢查它是否具有良好而成熟的版本發佈頻率與大量的維護者:例如, npm view async。

為什麼:

如果維護者沒有足夠快地合併修補程序,那麼這些貢獻者也將會變得不積極不高效。

如果需要使用那些不太熟悉的依賴包,請在使用之前與團隊進行充分討論。

始終確保您的應用程序在最新版本的依賴包上面能正常運行,而不是無法使用:npm outdated。

為什麼:

依賴關係更新有時包含破壞性更改。當顯示需要更新時,請始終先查看其發行說明。並逐一地更新您的依賴項,如果出現任何問題,可以使故障排除更容易。可以使用類似 npm-check-updates 的酷炫工具(來解決這個問題)。

檢查包是否有已知的安全漏洞,例如: Snyk。

5. 測試

如果需要,請構建一個 test 環境.

為什麼:

雖然有時在 production 模式下端到端測試可能看起來已經足夠了,但有一些例外:比如您可能不想在生產環境下啟用數據分析功能,只能用測試數據來填充(汙染)某人的儀表板。另一個例子是,您的API可能在 production 中才具有速率限制,並在請求達到一定量級後會阻止您的測試請求。

將測試文件放在使用 .test.js 或 .spec.js 命名約定的測試模塊,比如 moduleName.spec.js

為什麼:

您肯定不想進入一個層次很深的文件夾結構來查找裡面的單元測試。

將其他測試文件放入獨立的測試文件夾中以避免混淆。

為什麼:

一些測試文件與任何特定的文件實現沒有特別的關係。您只需將它放在最有可能被其他開發人員找到的文件夾中:test 文件夾。這個名字:test也是現在的標準,被大多數JavaScript測試框架所接受。

編寫可測試代碼,避免副作用(side effects),提取副作用,編寫純函數。

為什麼:

您想要將業務邏輯拆分為單獨的測試單元。您必須“儘量減少不可預測性和非確定性過程對代碼可靠性的影響”。純函數是一種總是為相同的輸入返回相同輸出的函數。相反地,不純的函數是一種可能會有副作用,或者取決於來自外部的條件來決定產生對應的輸出值的函數。這使得它不那麼可預測。

使用靜態類型檢查器

為什麼:

有時您可能需要一個靜態類型檢查器。它為您的代碼帶來一定程度的可靠性。

先在本地 develop 分支運行測試,待測試通過後,再進行pull請求。

為什麼:

您不想成為一個導致生產分支構建失敗的人吧。在您的rebase之後運行測試,然後再將您改動的功能分支推送到遠程倉庫。

記錄您的測試,包括在 README.md 文件中的相關說明部分。

為什麼:

這是您為其他開發者或者 DevOps 專家或者 QA 或者其他如此幸運能和您一起協作的人留下的便捷筆記。

6. 結構佈局與命名

請圍繞產品功能/頁面/組件,而不是圍繞角色來組織文件。此外,請將測試文件放在他們對應實現的旁邊。

不規範

.

├── controllers

| ├── product.js

| └── user.js

├── models

| ├── product.js

| └── user.js

規範

.

├── product

| ├── index.js

| ├── product.js

| └── product.test.js

├── user

| ├── index.js

| ├── user.js

| └── user.test.js

為什麼:

比起一個冗長的列表文件,創建一個單一責權封裝的小模塊,並在其中包括測試文件。將會更容易瀏覽,更一目瞭然。

將其他測試文件放在單獨的測試文件夾中以避免混淆。

為什麼:

這樣可以節約您的團隊中的其他開發人員或DevOps專家的時間。

使用 ./config 文件夾,不要為不同的環境製作不同的配置文件。

為什麼:

當您為不同的目的(數據庫,API等)分解不同的配置文件;將它們放在具有容易識別名稱(如 config )的文件夾中才是有意義的。請記住不要為不同的環境製作不同的配置文件。這樣並不是具有擴展性的做法,如果這樣,就會導致隨著更多應用程序部署被創建出來,新的環境名稱也會不斷被創建,非常混亂。 配置文件中使用的值應通過環境變量提供。

將腳本文件放在./scripts文件夾中。包括 bash 腳本和 node 腳本。

為什麼:

很可能最終會出現很多腳本文件,比如生產構建,開發構建,數據庫feeders,數據庫同步等。

將構建輸出結果放在./build文件夾中。將build/添加到.gitignore中以便忽略此文件夾。

為什麼:

命名為您最喜歡的就行,dist看起來也蠻酷的。但請確保與您的團隊保持一致性。放置在該文件夾下的東西應該是已經生成(打包、編譯、轉換)或者被移到這裡的。您產生什麼編譯結果,您的隊友也可以生成同樣的結果,所以沒有必要將這些結果提交到遠程倉庫中。除非您故意希望提交上去。

文件名和目錄名請使用 PascalCase camelCase 風格。組件請使用 PascalCase 風格。

CheckBox/index.js 應該代表 CheckBox 組件,也可以寫成 CheckBox.js ,但是不能寫成冗長的 CheckBox/CheckBox.js 或 checkbox/CheckBox.js 。

理想情況下,目錄名稱應該和 index.js 的默認導出名稱相匹配。

為什麼:

這樣您就可以通過簡單地導入其父文件夾直接使用您預期的組件或模塊。

7. 代碼風格

7.1 若干個代碼風格指導

對新項目請使用 Stage2 和更高版本的 JavaScript(現代化)語法。對於老項目,保持與老的語法一致,除非您打算把老的項目也更新為現代化風格。

為什麼:

這完全取決於您的選擇。我們使用轉換器來使用新的語法糖。Stage2更有可能最終成為規範的一部分,而且僅僅只需經過小版本的迭代就會成為規範。

在構建過程中包含代碼風格檢查。

為什麼:

在構建時中斷下一步操作是一種強制執行代碼風格檢查的方法。強制您認真對待代碼。請確保在客戶端和服務器端代碼都執行代碼檢查。

使用 ESLint - Pluggable JavaScript linter 去強制執行代碼檢查。

為什麼:

我們個人很喜歡 eslint ,不強制您也喜歡。它擁有支持更多的規則,配置規則的能力和添加自定義規則的能力。

針對 JavaScript 我們使用Airbnb JavaScript Style Guide , 更多請閱讀。 請依據您的項目和您的團隊選擇使用所需的JavaScript 代碼風格。

當使用FlowType的時候,我們使用 ESLint的Flow樣式檢查規則。。

為什麼:

Flow 引入了很少的語法,而這些語法仍然需要遵循代碼風格並進行檢查。

使用 .eslintignore 將某些文件或文件夾從代碼風格檢查中排除。

為什麼:

當您需要從風格檢查中排除幾個文件時,就再也不需要通過 eslint-disable 註釋來汙染您的代碼了。

在Pull Request之前,請刪除任何 eslint 的禁用註釋。

為什麼:

在處理代碼塊時禁用風格檢查是正常現象,這樣就可以關注在業務邏輯。請記住把那些 eslint-disable 註釋刪除並遵循風格規則。

根據任務的大小使用 //TODO: 註釋或做一個標籤(ticket)。

為什麼:

這樣您就可以提醒自己和他人有這樣一個小的任務需要處理(如重構一個函數或更新一個註釋)。對於較大的任務,可以使用由一個lint規則(no-warning-comments)強制要求其完成(並移除註釋)的//TODO(#3456),其中的#3456號碼是一個標籤(ticket),方便查找且防止相似的註釋堆積導致混亂。

隨著代碼的變化,始終保持註釋的相關性。刪除那些註釋掉的代碼塊。

為什麼:

代碼應該儘可能的可讀,您應該擺脫任何分心的事情。如果您在重構一個函數,就不要註釋那些舊代碼,直接把要註釋的代碼刪除吧。

避免不相關的和搞笑的的註釋,日誌或命名。

為什麼:

雖然您的構建過程中可能(應該)移除它們,但有可能您的源代碼會被移交給另一個公司/客戶,您的這些笑話應該無法逗樂您的客戶。

請使用有意義容易搜索的命名,避免縮寫名稱。對於函數使用長描述性命名。功能命名應該是一個動詞或動詞短語,需要能清楚傳達意圖的命名。

為什麼:

它使讀取源代碼變得更加自然。

依據《代碼整潔之道》的step-down規則,對您的源代碼文件中的函數(的聲明)進行組織。高抽象級別的函數(調用了低級別函數的函數)在上,低抽象級別函數在下,(保證了閱讀代碼時遇到未出現的函數仍然是從上往下的順序,而不會打斷閱讀順序地往前查找並且函數的抽象層次依次遞減)。

為什麼:

它使源代碼的可讀性更好。

7.2 強制的代碼風格標準

讓您的編輯器提示您關於代碼風格方面的錯誤。 請將 eslint-plugin-prettier 與 eslint-config-prettier 和您目前的ESLint配置一起搭配使用。

考慮使用Git鉤子。

為什麼:

Git的鉤子能大幅度地提升開發者的生產力。在做出改變、提交、推送至暫存區或者生產環境的過程中(充分檢驗代碼),再也不需要擔心(推送的代碼會導致)構建失敗。

將Git的precommit鉤子與Prettier結合使用。

為什麼:

雖然prettier自身已經非常強大,但是每次將其作為單獨的一個npm任務去格式化代碼,並不是那麼地高效。 這正是lint-staged(還有husky)可以解決的地方。關於如何配置 lint-staged 請閱讀這裡 以及如何配置 husky 請閱讀這裡。

8. 日誌

避免在生產環境中使用客戶端的控制檯日誌。

為什麼:

您在構建過程可以把(應該)它們去掉,但是請確保您在代碼風格檢查中提供了有關控制檯日誌的警告信息。

產出生產環境的可讀生產日誌記錄。一般使用在生產模式下所使用的日誌記錄庫 (比如 winston 或者 node-bunyan)。

為什麼:

它通過添加著色、時間戳、log到控制檯或者文件中,甚至是夜以繼日地輪流log到文件,來減少故障排除中那些令人不愉快的事情。

9. API

9.1 API 設計

為什麼:

因為我們試圖實施開發出結構穩健的 Restful 接口,讓團隊成員和客戶可以簡單而一致地使用它們。

為什麼:

缺乏一致性和簡單性會大大增加集成和維護的成本。這就是為什麼API設計這部分會包含在這個文檔中的原因

我們主要遵循資源導向的設計方式。它有三個主要要素:資源,集合和 URLs。

  • 資源具有數據,嵌套,和一些操作方法。
  • 一組資源稱為一個集合。
  • URL標識資源或集合的線上位置。

為什麼:

這是針對開發人員(您的主要API使用者)非常著名的設計方式。除了可讀性和易用性之外,它還允許我們在無需瞭解API細節的情況下編寫通用庫和一些連接器。

使用kebab-case(短橫線分割)的URL。

在查詢字符串或資源字段中使用camelCase模式。

在URL中使用多個kebab-case作為資源名稱。

總是使用複數名詞來命名指向一個集合的url:/users.

為什麼:

基本上,它可讀性會更好,並可以保持URL的一致性。 更多請閱讀…

在源代碼中,將複數轉換為具有列表後綴名描述的變量和屬性。

為什麼:

複數形式的URL非常好,但在源代碼中使用它卻很微妙而且容易出錯,所以要小心謹慎。

堅持這樣一個概念:始終以集合名起始並以標識符結束。

/students/245743

/airports/kjfk

避免這樣的網址:

GET /blogs/:blogId/posts/:postId/summary

為什麼:

這不是在指向資源,而是在指向屬性。您完全可以將屬性作為參數傳遞,以減少響應。

URLs裡面請儘量少用動詞

為什麼:

因為如果您為每個資源操作使用一個動詞,您很快就會維護一個很大的URL列表,而且沒有一致的使用模式,這會使開發人員難以學習。此外,我們還要使用動詞做別的事情。

為非資源型請求使用動詞。在這種情況下,您的API並不需要返回任何資源。而是去執行一個操作並返回執行結果。這些不是 CRUD(創建,查詢,更新和刪除)操作:

/translate?text=Hallo

為什麼:

因為對於 CRUD,我們在資源或集合URL上使用 HTTP 自己帶的方法。我們所說的動詞實際上是指Controllers。您通常不會開發這些東西。

請求體或響應類型如果是JSON,那麼請遵循camelCase規範為JSON屬性命名來保持一致性。

為什麼:

這是一個 JavaScript 項目指南,其中用於生成JSON的編程語言以及用於解析JSON的編程語言被假定為 JavaScript。

即使資源類似於對象實例或數據庫記錄這樣的單一概念,您也不應該將table_name用作資源名稱或將column_name作為資源屬性。

為什麼:

因為您的目的是分析資源,而不是分析數據庫模式。

再次,只有在您的URL上面命名資源時才使用名詞,不要嘗試解釋其功能。

為什麼:

只能在資源URL中使用名詞,避免像/addNewUser或/updateUser這樣的結束點。也避免使用參數作為發送資源的操作。

如何使用HTTP方法來操作CRUD功能

怎麼做:

  • GET: 查詢資源的表示法
  • POST: 創建一些新的資源或者子資源
  • PUT: 更新一個存在的資源
  • PATCH: 更新現有資源。它只更新所提供的字段,不管其他字段
  • DELETE: 刪除一個存在的資源

對於嵌套資源,請在URL中把他們的關係表現出來。例如,使用id將員工與公司聯繫起來。

為什麼:

這是一種自然的方式,方便資源的認知。

怎麼做:

  • GET /schools/2/students , 應該從學校2得到所有學生的名單
  • GET /schools/2/students/31 , 應該得到學生31的詳細信息,且此學生屬於學校2
  • DELETE /schools/2/students/31 , 應刪除屬於學校2的學生31
  • PUT /schools/2/students/31 , 應該更新學生31的信息,僅在資源URL上使用PUT方式,而不要用收集
  • POST /schools , 應該創建一所新學校,並返回創建的新學校的細節。在集合URL上使用POST

對於具有v前綴(v1,v2)的版本,使用簡單的序數。並將其移到URL的左側,使其具有最高的範圍表述:

http://api.domain.com/v1/schools/3/students

為什麼:

當您的 API 為第三方公開時,升級API會導致發生一些意料之外的影響,也可能導致使用您API的人無法使用您的服務和產品。而這時使用URL中版本化可以防止這種情況的發生。

響應消息必須是自我描述的。一個很好的錯誤消息響應可能如下所示:

{

"code": 1234,

"message" : "Something bad happened",

"description" : "More details"

}

或驗證錯誤:

{

"code" : 2314,

"message" : "Validation Failed",

"errors" : [

{

"code" : 1233,

"field" : "email",

"message" : "Invalid email"

},

{

"code" : 1234,

"field" : "password",

"message" : "No password provided"

}

]

}

為什麼:

開發人員在使用這些由API構建的應用程序時,難免會需要在故障排除和解決問題的關鍵時刻使用到這些精心設計的錯誤消息。好的錯誤消息設計能節約大量的問題排查時間。

_注意:儘可能保持安全異常消息的通用性。例如,別說不正確的密碼,您可以換成無效的用戶名或密碼,以免我們不知不覺地通知用戶他的用戶名確實是正確的,只有密碼不正確。這會讓用戶很懵逼。

只使用這8個狀態代碼,並配合您自定義的響應描述來表述程序工作一切是否正常,客戶端應用程序發生了什麼錯誤或API發生錯誤。

選誰呢:

  • 200 OK GET, PUT 或 POST 請求響應成功.
  • 201 Created 標識一個新實例創建成功。當創建一個新的實例,請使用POST方法並返回201狀態碼。
  • 304 Not Modified 發現資源已經緩存在本地,瀏覽器會自動減少請求次數。
  • 400 Bad Request 請求未被處理,因為服務器不能理解客戶端是要什麼。
  • 401 Unauthorized 因為請求缺少有效的憑據,應該使用所需的憑據重新發起請求。
  • 403 Forbidden 意味著服務器理解本次請求,但拒絕授權。
  • 404 Not Found 表示未找到請求的資源。
  • 500 Internal Server Error 表示請求本身是有效,但由於某些意外情況,服務器無法實現,服務器發生了故障。

為什麼:

大多數 API 提供程序僅僅只使用一小部分 HTTP 狀態代碼而已。例如,Google GData API 僅使用了10個狀態代碼,Netflix 使用了9個,而 Digg 只使用了8個。當然,這些響應作為響應主體的附加信息。一共有超過 70 個 HTTP 狀態代碼。然而,大多數開發者不可能全部記住這 70 個狀態碼。因此,如果您選擇不常用的狀態代碼,您將使應用程序開發人員厭煩構建應用程序,然後您還要跑到維基百科上面找出您要告訴他們的內容,多累啊。

在您的響應中提供資源的總數

接受limit和offset參數

還應考慮資源暴露的數據量。 API消費者並不總是需要資源的完整表述。可以使用一個字段查詢參數,該參數用逗號分隔的字段列表來包括:

GET /student?fields=id,name,age,class

分頁,過濾和排序功能並不需要從所有資源一開始就要得到支持。記錄下那些提供過濾和排序的資源。

9.2 API 安全

這些是一些基本的安全最佳實踐:

除非通過安全的連接(HTTPS),否則不要只使用基本認證。不要在URL中傳輸驗證令牌:GET /users/123?token=asdf….

為什麼:

因為令牌、用戶ID和密碼通過網絡是明文傳遞的(它是base64編碼,而base64是可逆編碼),所以基本認證方案是不安全的。

必須使用授權請求頭在每個請求上發送令牌:Authorization: Bearer xxxxxx, Extra yyyyy

通過不響應任何HTTP請求來拒絕任何非TLS請求,以避免任何不安全的數據交換。響應403 Forbidden的HTTP請求。

考慮使用速率限制

為什麼:

保護您的API免受每小時數千次的機器人掃描威脅。您應該在早期就考慮實施流控。

適當地設置HTTP請求頭可以幫助鎖定和保護您的Web應用程序。更多請閱讀…

您的API應將收到的數據轉換為規範形式,或直接拒絕響應,並返回400錯誤請求(400 Bad Request)的錯誤,並在其中包含有關錯誤或丟失數據的詳細信息。

所有通過Rest API交換的數據必須由API來校驗。

序列化JSON

為什麼:

JSON編碼器的一個關鍵問題是阻止任意的可執行代碼在瀏覽器或在服務器中(如果您用nodejs的話)執行。您必須使用適當的JSON序列化程序對用戶輸入的數據進行正確編碼,以防止在瀏覽器上執行用戶提供的輸入,這些輸入可能會包含惡意代碼,而不是正常的用戶數據。

驗證內容類型,主要使用application/*.json(Content-Type 頭字段).

為什麼:

例如,接受application/x-www-form-urlencodedMIME類型可以允許攻擊者創建一個表單並觸發一個簡單的POST請求。服務器不應該假定Content-Type。缺少Content-Type請求頭或異常的Content-Type請求頭,應該讓服務器直接以4XX響應內容去拒絕請求。

9.3 API 文檔

在README.md模板為 API 填寫 API Reference 段落。

儘量使用示例代碼來描述 API 授權方法

解釋 URL 的結構(僅 path,不包括根 URL),包括請求類型(方法)

對於每個端點(endpoint)說明:

如果存在 URL 參數就使用 URL 參數,並根據URL中使用到的名稱來指定它們:

Required: id=[integer]

Optional: photo_id=[alphanumeric]

如果請求類型為 POST,請提供如何使用的示例。上述的URL參數規則在這也可以適用。分為可選和必需。

響應成功,應該對應什麼樣的狀態代碼,返回了哪些數據?當人們需要知道他們的回調應該是期望的樣子,這很有用:

Code: 200

Content: { id : 12 }

錯誤響應,大多數端點都存在許多失敗的可能。從未經授權的訪問到錯誤參數等。所有的(錯誤描述信息)都應該列在這裡。雖然有可能會重複,但它卻有助於防止別人的猜想(,減少使用時的排錯時間)。例如

{

"code": 403,

"message" : "Authentication failed",

"description" : "Invalid username or password"

}

使用API設計工具,有很多開源工具可用於提供良好的文檔,例如 API Blueprint and Swagger.

10. 證書

確保您有權使用的這些資源。如果您使用其中的軟件庫,請記住先查詢MIT,Apache或BSD(以更好地瞭解您所能夠擁有的權限),但如果您打算修改它們,請查看許可證詳細信息。圖像和視頻的版權可能會導致法律問題。


分享到:


相關文章: