成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Restful API 中的錯(cuò)誤處理

NotFound / 1293人閱讀

簡(jiǎn)介

隨著移動(dòng)開(kāi)發(fā)和前端開(kāi)發(fā)的崛起,越來(lái)越多的 Web 后端應(yīng)用都傾向于實(shí)現(xiàn) Restful API。
Restful API 是一個(gè)簡(jiǎn)單易用的前后端分離方案,它只需要對(duì)客戶(hù)端請(qǐng)求進(jìn)行處理,然后返回結(jié)果即可, 無(wú)需考慮頁(yè)面渲染,一定程度上減輕了后端開(kāi)發(fā)人員的負(fù)擔(dān)。
然而,正是由于 Restful API 不需要考慮頁(yè)面渲染,導(dǎo)致它不能在頁(yè)面上展示錯(cuò)誤信息。
那就意著當(dāng)出現(xiàn)錯(cuò)誤的時(shí)候,它只能通過(guò)返回一個(gè)錯(cuò)誤的響應(yīng),來(lái)告訴用戶(hù)和開(kāi)發(fā)者相應(yīng)的錯(cuò)誤信息,提示他們接下來(lái)應(yīng)該怎么辦。
本文將討論 Restful API 中的錯(cuò)誤處理方案。

設(shè)計(jì)錯(cuò)誤信息

當(dāng) Restful API 需要拋出錯(cuò)誤的時(shí)候,我們要考慮的是:這個(gè)錯(cuò)誤應(yīng)該包含哪些信息。
我們先看看 Github, Google, Facebook, Twitter, Twilio 的錯(cuò)誤信息是怎樣的。

Github (use http status)

{
  "message": "Validation Failed",
  "errors": [
    {
      "resource": "Issue",
      "field": "title",
      "code": "missing_field"
    }
  ]
}

Google (use http status)

{
  "error": {
    "errors": [
      {
        "domain": "global",
        "reason": "insufficientFilePermissions",
        "message": "The user does not have sufficient permissions for file {fileId}."
      }
    ],
    "code": 403,
    "message": "The user does not have sufficient permissions for file {fileId}."
  }
}

Facebook (use http status)

{
  "error": {
    "message": "Message describing the error", 
    "type": "OAuthException",
    "code": 190,
    "error_subcode": 460,
    "error_user_title": "A title",
    "error_user_msg": "A message",
    "fbtrace_id": "EJplcsCHuLu"
  }
}

Twitter (use http status)

{
  "errors": [
    {
      "message": "Sorry, that page does not exist",
      "code": 34
    }
  ]
}

Twilio (use http status)

{
  "code": 21211,
  "message": "The "To" number 5551234567 is not a valid phone number.",
  "more_info": "https://www.twilio.com/docs/errors/21211",
  "status": 400
}

觀察這些結(jié)構(gòu)可以發(fā)現(xiàn)它們都有一些共同的地方:

都利用了 Http 狀態(tài)碼

有些返回了業(yè)務(wù)錯(cuò)誤碼

都提供了給用戶(hù)看的錯(cuò)誤提示信息

有些提供了給開(kāi)發(fā)者看的錯(cuò)誤信息

Http 狀態(tài)碼

在 Restful API 中利用 Http 狀態(tài)碼來(lái)表明錯(cuò)誤類(lèi)型再合適不過(guò)了,因?yàn)?Http 狀態(tài)碼定義了很多抽象的錯(cuò)誤類(lèi)型。
雖然 Http 狀態(tài)碼定義了非常多的錯(cuò)誤類(lèi)型,但實(shí)際應(yīng)用中,我們常用的狀態(tài)碼并不多,通常都是下面這幾方面:

API 正常工作 (200, 201)

客戶(hù)端錯(cuò)誤 (400, 401, 403, 404)

服務(wù)端錯(cuò)誤 (500, 503)

業(yè)務(wù)錯(cuò)誤碼

很多時(shí)候,我們根據(jù)業(yè)務(wù)類(lèi)型來(lái)自定義錯(cuò)誤碼。
這些業(yè)務(wù)錯(cuò)誤碼與 Http 狀態(tài)碼并不重疊,這時(shí)候我們可以返回業(yè)務(wù)錯(cuò)誤碼,用來(lái)提示用戶(hù)/開(kāi)發(fā)者錯(cuò)誤類(lèi)型。

給用戶(hù)看的錯(cuò)誤信息

當(dāng)出現(xiàn)錯(cuò)誤的時(shí)候,我們需要提示用戶(hù)如何處理這種情況,通常這種錯(cuò)誤信息都是必須的。
可以看到上面幾個(gè)例子中都有返回給用戶(hù)看的錯(cuò)誤信息。

給開(kāi)發(fā)者看的錯(cuò)誤信息

若我們的 API 需要開(kāi)放給第三方開(kāi)發(fā)者,那么我們就需要考慮返回一些給開(kāi)發(fā)者看的錯(cuò)誤信息。

設(shè)計(jì)錯(cuò)誤類(lèi)型

我們剛才提到過(guò),可以利用 Http 狀態(tài)碼來(lái)為錯(cuò)誤類(lèi)型進(jìn)行分類(lèi)。
通常我們所說(shuō)的分類(lèi)通常是對(duì)客戶(hù)端錯(cuò)誤進(jìn)行分類(lèi), 即 4xx 類(lèi)型的錯(cuò)誤。

而這些錯(cuò)誤類(lèi)型中,我們最常用的是:

400 Bad Request
由于包含語(yǔ)法錯(cuò)誤,當(dāng)前請(qǐng)求無(wú)法被服務(wù)器理解。除非進(jìn)行修改,否則客戶(hù)端不應(yīng)該重復(fù)提交這個(gè)請(qǐng)求。
通常在請(qǐng)求參數(shù)不合法或格式錯(cuò)誤的時(shí)候可以返回這個(gè)狀態(tài)碼。

401 Unauthorized
當(dāng)前請(qǐng)求需要用戶(hù)驗(yàn)證。
通常在沒(méi)有登錄的狀態(tài)下訪問(wèn)一些受保護(hù)的 API 時(shí)會(huì)用到這個(gè)狀態(tài)碼。

403 Forbidden
服務(wù)器已經(jīng)理解請(qǐng)求,但是拒絕執(zhí)行它。與401響應(yīng)不同的是,身份驗(yàn)證并不能提供任何幫助。
通常在沒(méi)有權(quán)限操作資源時(shí)(如修改/刪除一個(gè)不屬于該用戶(hù)的資源時(shí))會(huì)用到這個(gè)狀態(tài)碼。

404 Not Found
請(qǐng)求失敗,請(qǐng)求所希望得到的資源未被在服務(wù)器上發(fā)現(xiàn)。
通常在找不到資源時(shí)返回這個(gè)狀態(tài)碼。

盡管我們可以通過(guò) Http 狀態(tài)碼來(lái)表示錯(cuò)誤的類(lèi)型,
但在實(shí)際應(yīng)用中,如果僅僅使用 Http 狀態(tài)碼的話,我們的代碼中就遍布 Http 狀態(tài)碼:

// Node.js
if (!res.body.title) {
  res.statusCode = 400
}

if (!user) {
  res.statusCode = 401
}

if (!post) {
  res.statusCode = 404
}

上面的實(shí)現(xiàn)方式在小項(xiàng)目中還可以接受,當(dāng)項(xiàng)目變大、需求變多的時(shí)候,維護(hù)起來(lái)就變得很麻煩了。
為了提高錯(cuò)誤的可讀性和可維護(hù)性,我們需要對(duì)各種錯(cuò)誤進(jìn)行分類(lèi)。
我個(gè)人習(xí)慣把錯(cuò)誤分成以下幾種類(lèi)型:

格式錯(cuò)誤 (FORMAT_INVALID)

數(shù)據(jù)不存在 (DATA_NOT_FOUND)

數(shù)據(jù)已存在 (DATA_EXISTED)

數(shù)據(jù)無(wú)效 (DATA_INVALID)

登錄錯(cuò)誤 (LOGIN_REQUIRED)

權(quán)限不足 (PERMISSION_DENIED)

錯(cuò)誤分類(lèi)之后,我們拋錯(cuò)誤的時(shí)候就變得更加直觀了:

if (!res.body.title) {
  throw new Error(ERROR.FORMAT_INVALID)
}

if (!user) {
  throw new Error(ERROR.LOGIN_REQUIRED)
}

if (!post) {
  throw new Error(ERROR.DATA_NOT_FOUND)
}

if (post.creator.id !== user.id) {
  throw new Error(ERROR.PERMISSION_DENIED)
}

這種形式比上面的寫(xiě)死狀態(tài)碼的方式方便很多,而且維護(hù)起來(lái)也更加簡(jiǎn)單。
但有一個(gè)問(wèn)題,就是不能根據(jù)錯(cuò)誤類(lèi)型來(lái)返回指定的錯(cuò)誤信息。

自定義錯(cuò)誤類(lèi)型

要實(shí)現(xiàn)根據(jù)錯(cuò)誤類(lèi)型來(lái)返回指定的錯(cuò)誤信息,我們可以通過(guò)自定義錯(cuò)誤的方式來(lái)實(shí)現(xiàn)。
假設(shè)我們自定義錯(cuò)誤的結(jié)構(gòu)如下:

{
  "type": "",
  "code": 0,
  "message": "",
  "detail": ""
}

我們需要做到如下幾點(diǎn):

根據(jù)錯(cuò)誤類(lèi)型來(lái)自動(dòng)設(shè)置 type, code, message

detail 為可選項(xiàng),用來(lái)描述該錯(cuò)誤的具體原因

const ERROR = {
  FORMAT_INVALID: "FORMAT_INVALID",
  DATA_NOT_FOUND: "DATA_NOT_FOUND",
  DATA_EXISTED: "DATA_EXISTED",
  DATA_INVALID: "DATA_INVALID",
  LOGIN_REQUIRED: "LOGIN_REQUIRED",
  PERMISSION_DENIED: "PERMISSION_DENIED"
}

const ERROR_MAP = {
  FORMAT_INVALID: {
    code: 1,
    message: "The request format is invalid"
  },
  DATA_NOT_FOUND: {
    code: 2,
    message: "The data is not found in database"
  },
  DATA_EXISTED: {
    code: 3,
    message: "The data has exist in database"
  },
  DATA_INVALID: {
    code: 4,
    message: "The data is invalid"
  },
  LOGIN_REQUIRED: {
    code 5,
    message: "Please login first"
  },
  PERMISSION_DENIED: {
    code: 6,
    message: "You have no permission to operate"
  }
}

class CError extends Error {
  constructor(type, detail) {
    super()
    Error.captureStackTrace(this, this.constructor)

    let error = ERROR_MAP[type]
    if (!error) {
      error = {
        code: 999,
        message: "Unknow error type"
      }
    }

    this.name = "CError"
    this.type = error.code !== 999 ? type : "UNDEFINED"
    this.code = error.code
    this.message = error.message
    this.detail = detail
  }
}

自定義好錯(cuò)誤之后,我們調(diào)用起來(lái)就更加簡(jiǎn)單了:

// in controller
if (!user) {
  throw new CError(ERROR.LOGIN_REQUIRED, "You should login first")
}

if (!req.body.title) {
  throw new CError(ERROR.FORMAT_INVALID, "Title is required")
}

if (!post) {
  throw new CError(ERROR.DATA_NOT_FOUND, "The post you required is not found")
}

最后,還剩下一個(gè)問(wèn)題,根據(jù)錯(cuò)誤類(lèi)型來(lái)設(shè)置狀態(tài)碼,然后返回錯(cuò)誤信息給客戶(hù)端。

捕獲錯(cuò)誤信息

在 Controller 中拋出自定義錯(cuò)誤后,我們需要捕獲該錯(cuò)誤,才能返回給客戶(hù)端。
假設(shè)我們使用 koa 2 作為 web 框架來(lái)開(kāi)發(fā) restful api,那么我們要做的是添加錯(cuò)誤處理的中間件:

module.exports = async function errorHandler (ctx, next) {
  try {
    await next()
  } catch (err) {

    let status

    switch (err.type) {
      case ERROR.FORMAT_INVALID:
      case ERROR.DATA_EXISTED:
      case ERROR.DATA_INVALID:
        status = 400
        break
      case ERROR.LOGIN_REQUIRED:
        status = 401
      case ERROR.PERMISSION_DENIED:
        status = 403
      case ERROR.DATA_NOT_FOUND:
        status = 404
        break
      default:
        status = 500
    }

    ctx.status = status
    ctx.body = err
  }
}

// in app.js
app.use(errorHandler)
app.use(router.routes())

通過(guò)這種方式,我們就能優(yōu)雅地處理 Restful API 中的錯(cuò)誤信息了。

參考資料

https://zh.wikipedia.org/zh-hans/HTTP%E7%8A%B6%E6%80%81%E7%A0%81
https://www.loggly.com/blog/n...
http://blog.restcase.com/rest...
https://apigee.com/about/blg/...
http://stackoverflow.com/ques...
http://goldbergyoni.com/check...
http://blogs.mulesoft.com/dev...
https://developers.facebook.c...
https://developers.google.com...
https://developer.github.com/...
https://dev.twitter.com/overv...
https://www.twilio.com/docs/a...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75712.html

相關(guān)文章

  • 7點(diǎn)關(guān)于RESTful規(guī)范的API接口設(shè)計(jì)的想法

    摘要:返回值結(jié)構(gòu)在完成了上面的部署之后,接下來(lái)我們來(lái)看看返回結(jié)果應(yīng)該怎么樣來(lái)確定。因?yàn)榉祷刂抵?,我們常常要?duì)數(shù)據(jù)進(jìn)行區(qū)分分組,或者按照從屬關(guān)系打包,所以,我們?cè)俜祷貢r(shí),最好有包裹的思想,把數(shù)據(jù)存放在不同的包裹中進(jìn)行返回。 在項(xiàng)目中,需要為APP撰寫(xiě)API。剛開(kāi)始接觸的時(shí)候,并沒(méi)有考慮太多,就想提供URL,APP端通過(guò)該URL進(jìn)行查詢(xún)、創(chuàng)建、更新等操作即可。但再對(duì)相關(guān)規(guī)范進(jìn)行了解后,才發(fā)現(xiàn),A...

    Jason 評(píng)論0 收藏0
  • 使用swagger 生成 Flask RESTful API

    摘要:指定篩選條件選擇合適的狀態(tài)碼應(yīng)答中,需要帶一個(gè)很重要的字段。返回結(jié)果針對(duì)不同操作,服務(wù)器向用戶(hù)返回的結(jié)果應(yīng)該符合以下規(guī)范。如果狀態(tài)碼是,就應(yīng)該向用戶(hù)返回出錯(cuò)信息。 什么是 RESTful 什么是REST REST(英文:Representational State Transfer,又稱(chēng)具象狀態(tài)傳輸)是Roy Thomas Fielding博士于2000年在他的博士論文 中提出來(lái)的一種...

    printempw 評(píng)論0 收藏0
  • Flask 擴(kuò)展系列之 Flask-RESTful

    摘要:勵(lì)以最少的安裝方式進(jìn)行最佳實(shí)踐。上面的例子接收了一個(gè)對(duì)象并準(zhǔn)備將其序列化。裝飾器會(huì)通過(guò)進(jìn)行轉(zhuǎn)換。從對(duì)象中提取的唯一字段是。是一個(gè)特殊的字段,它接受端點(diǎn)名稱(chēng)并為響應(yīng)中的端點(diǎn)生成一個(gè)。可以查看項(xiàng)查看完整列表。 大綱 簡(jiǎn)介 安裝 快速入門(mén) 一個(gè)最小的 api 例子 資源豐富的路由 端點(diǎn) 參數(shù)解析 數(shù)據(jù)格式化 完整 TODO 應(yīng)用例子 簡(jiǎn)介 Flask-RESTful是一個(gè)Flas...

    阿羅 評(píng)論0 收藏0
  • SpringBoot RESTful 應(yīng)用中的異常處理小結(jié)

    摘要:和的區(qū)別方法注解作用于級(jí)別注解為一個(gè)定義一個(gè)異常處理器類(lèi)注解作用于整個(gè)工程注解定義了一個(gè)全局的異常處理器需要注意的是的優(yōu)先級(jí)比高即拋出的異常如果既可以讓標(biāo)注的方法處理又可以讓標(biāo)注的類(lèi)中的方法處理則優(yōu)先讓標(biāo)注的方法處理處理中的異常為了方便地展 @ControllerAdvice 和 @ExceptionHandler 的區(qū)別 ExceptionHandler, 方法注解, 作用于 Co...

    jackzou 評(píng)論0 收藏0
  • 人人都是 API 設(shè)計(jì)師:我對(duì) RESTful API、GraphQL、RPC API 的思考

    摘要:通常情況下,偽都是基于第一層次與第二層次設(shè)計(jì)的。為了解決這個(gè)版本不兼容問(wèn)題,在設(shè)計(jì)的一種實(shí)用的做法是使用版本號(hào)。例如,建議第三位版本號(hào)通常表示兼容升級(jí),只有不兼容時(shí)才需要變更服務(wù)版本。 原文地址:梁桂釗的博客 博客地址:blog.720ui.com 歡迎關(guān)注公眾號(hào):「服務(wù)端思維」。一群同頻者,一起成長(zhǎng),一起精進(jìn),打破認(rèn)知的局限性。 有一段時(shí)間沒(méi)怎么寫(xiě)文章了,今天提筆寫(xiě)一篇自己對(duì) API 設(shè)...

    ormsf 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<