restful api 错误
简介
随着移动开发和前端开发的崛起,越来越多的 Web 后端应用都倾向于实现 Restful API。
Restful API 是一个简单易用的前后端分离方案,它只需要对客户端请求进行处理,然后返回结果即可, 无需考虑页面渲染,一定程度上减轻了后端开发人员的负担。
然而,正是由于 Restful API 不需要考虑页面渲染,导致它不能在页面上展示错误信息。
那就意着当出现错误的时候,它只能通过返回一个错误的响应,来告诉用户和开发者相应的错误信息,提示他们接下来应该怎么办。
本文将讨论 Restful API 中的错误处理方案。
设计错误信息
当 Restful API 需要抛出错误的时候,我们要考虑的是:这个错误应该包含哪些信息。
我们先看看 Github, Google, Facebook, Twitter, Twilio 的错误信息是怎样的。
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
}
|
观察这些结构可以发现它们都有一些共同的地方:
- 都利用了 Http 状态码
- 有些返回了业务错误码
- 都提供了给用户看的错误提示信息
- 有些提供了给开发者看的错误信息
Http 状态码
在 Restful API 中利用 Http 状态码来表明错误类型再合适不过了,因为 Http 状态码定义了很多抽象的错误类型。
虽然 Http 状态码定义了非常多的错误类型,但实际应用中,我们常用的状态码并不多,通常都是下面这几方面:
- API 正常工作 (200, 201)
- 客户端错误 (400, 401, 403, 404)
- 服务端错误 (500, 503)
业务错误码
很多时候,我们根据业务类型来自定义错误码。
这些业务错误码与 Http 状态码并不重叠,这时候我们可以返回业务错误码,用来提示用户/开发者错误类型。
给用户看的错误信息
当出现错误的时候,我们需要提示用户如何处理这种情况,通常这种错误信息都是必须的。
可以看到上面几个例子中都有返回给用户看的错误信息。
给开发者看的错误信息
若我们的 API 需要开放给第三方开发者,那么我们就需要考虑返回一些给开发者看的错误信息。
设计错误类型
我们刚才提到过,可以利用 Http 状态码来为错误类型进行分类。
通常我们所说的分类通常是对客户端错误进行分类, 即 4xx 类型的错误。
而这些错误类型中,我们最常用的是:
400 Bad Request
由于包含语法错误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。
通常在请求参数不合法或格式错误的时候可以返回这个状态码。401 Unauthorized
当前请求需要用户验证。
通常在没有登录的状态下访问一些受保护的 API 时会用到这个状态码。403 Forbidden
服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助。
通常在没有权限操作资源时(如修改/删除一个不属于该用户的资源时)会用到这个状态码。404 Not Found
请求失败,请求所希望得到的资源未被在服务器上发现。
通常在找不到资源时返回这个状态码。
尽管我们可以通过 Http 状态码来表示错误的类型,
但在实际应用中,如果仅仅使用 Http 状态码的话,我们的代码中就遍布 Http 状态码:
|
// Node.js
if (!res.body.title) {
res.statusCode = 400
}
if (!user) {
res.statusCode = 401
}
if (!post) {
res.statusCode = 404
}
|
上面的实现方式在小项目中还可以接受,当项目变大、需求变多的时候,维护起来就变得很麻烦了。
为了提高错误的可读性和可维护性,我们需要对各种错误进行分类。
我个人习惯把错误分成以下几种类型:
- 格式错误 (FORMAT_INVALID)
- 数据不存在 (DATA_NOT_FOUND)
- 数据已存在 (DATA_EXISTED)
- 数据无效 (DATA_INVALID)
- 登录错误 (LOGIN_REQUIRED)
- 权限不足 (PERMISSION_DENIED)
错误分类之后,我们抛错误的时候就变得更加直观了:
|
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)
}
|
这种形式比上面的写死状态码的方式方便很多,而且维护起来也更加简单。
但有一个问题,就是不能根据错误类型来返回指定的错误信息。
自定义错误类型
要实现根据错误类型来返回指定的错误信息,我们可以通过自定义错误的方式来实现。
假设我们自定义错误的结构如下:
|
{
"type": "",
"code": 0,
"message": "",
"detail": ""
}
|
我们需要做到如下几点:
- 根据错误类型来自动设置
type,code,message detail为可选项,用来描述该错误的具体原因
|
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
}
}
|
自定义好错误之后,我们调用起来就更加简单了:
|
// 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')
}
|
最后,还剩下一个问题,根据错误类型来设置状态码,然后返回错误信息给客户端。
捕获错误信息
在 Controller 中抛出自定义错误后,我们需要捕获该错误,才能返回给客户端。
假设我们使用 koa 2 作为 web 框架来开发 restful api,那么我们要做的是添加错误处理的中间件:
|
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())
|
通过这种方式,我们就能优雅地处理 Restful API 中的错误信息了。
restful api 错误的更多相关文章
- Restful API 中的错误处理
简介 随着移动开发和前端开发的崛起,越来越多的 Web 后端应用都倾向于实现 Restful API. Restful API 是一个简单易用的前后端分离方案,它只需要对客户端请求进行处理,然后返回结 ...
- (转载) RESTful API 设计指南
作者: 阮一峰 日期: 2014年5月22日 网络应用程序,分为前端和后端两个部分.当前的发展趋势,就是前端设备层出不穷(手机.平板.桌面电脑.其他专用设备......). 因此,必须有一种统一的机制 ...
- Node.js实现RESTful api,express or koa?
文章导读: 一.what's RESTful API 二.Express RESTful API 三.KOA RESTful API 四.express还是koa? 五.参考资料 一.what's R ...
- 使用Flask设计带认证token的RESTful API接口[翻译]
上一篇文章, 使用python的Flask实现一个RESTful API服务器端 简单地演示了Flask实的现的api服务器,里面提到了因为无状态的原则,没有session cookies,如果访问 ...
- 使用python的Flask实现一个RESTful API服务器端[翻译]
最近这些年,REST已经成为web services和APIs的标准架构,很多APP的架构基本上是使用RESTful的形式了. 本文将会使用python的Flask框架轻松实现一个RESTful的服务 ...
- RESTful API 设计指南
转自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html 网络应用程序,分为前端和后端两个部分.当前的发展趋势,就是前端设备层出不穷(手机. ...
- RESTful API 设计最佳实践
背景 目前互联网上充斥着大量的关于RESTful API(为了方便,以后API和RESTful API 一个意思)如何设计的文章,然而却没有一个"万能"的设计标准:如何鉴权?API ...
- RESTful API URI 设计: 判断资源是否存在?
相关的一篇文章:RESTful API URI 设计的一些总结. 问题场景:判断一个资源(Resources)是否存在,URI 该如何设计? 应用示例:判断 id 为 1 用户下,名称为 window ...
- RESTful API 设计指南 (转)
RESTful API 设计指南 2016-02-23 ImportNew (点击上方公号,可快速关注) 作者:阮一峰 链接:http://www.ruanyifeng.com/blog/2014/0 ...
随机推荐
- Light oj-1100 - Again Array Queries,又是这个题,上次那个题用的线段树,这题差点就陷坑里了,简单的抽屉原理加暴力就可以了,真是坑~~
1100 - Again Array Queries ...
- HDU 2897 经典巴什博弈
从n个石子中每次取p~q个,求先手能否获胜 可以先列举一部分数据,然后观察可得总是在p+q中循环,所以只要用n对p+q取模就好了 #include <cstdio> #include &l ...
- CRT(secureCRT)中文显示研究&Linux中文字符显示
关于secureCRT设置编码: 基本上只需要设置crt字符编码与远程服务器一致就可以了.要注意的是,有时设置完之后要重启secureCRT, 不然不会生效.
- 【BZOJ3925】地震后的幻想乡(期望概率DP,状压DP)
题意:给定一张点数不超过10的无向连通图,每条边有一个[0,1]之间的随机权值,求最小生成树上最大边的期望值 提示:对于n个[0,1]之间的随机变量x1,x2,...,xn,第k小的那个的期望值是k/ ...
- .net如何统计在线人数
原文发布时间为:2008-10-17 -- 来源于本人的百度文章 [由搬家工具导入] 统计在线用户的作用不言而喻,就是为了网站管理者可以知道当前用户的多少,然后根据用户数量来观察服务器或者程序的性能, ...
- [bzoj2733][HNOI2012]永无乡_权值线段树_线段树合并
永无乡 bzoj-2733 HNOI-2012 题目大意:题目链接. 注释:略. 想法: 它的查询操作非常友善,就是一个联通块内的$k$小值. 故此我们可以考虑每个联通块建一棵权值线段树. 这样的话每 ...
- PLSQL Developer来实现不同数据库的表结构以及表数据同步
PLSQL Developer菜单条中 Tools选项下有Compare User Objects和Compare Table Data功能. 一.Tools --> compare user ...
- 牛腩新闻系统(一)——UML、数据库设计
牛腩新闻系统(一)--UML.数据库设计 一.初识牛腩系统 牛腩(Brisket)即牛腹部及靠近牛肋处的松软肌肉,是指带有筋.肉.油花的肉 块.这是一种统称. 若依部位来分,牛身上很多地方的肉都能够叫 ...
- PLC基础入门
PLC编程入门基础技术知识学习 2016-06-27 xjp7879 摘自 电工技术知... 第一章 可编程控制器简介 可编程序控制器,英文称Programmable Controlle ...
- Angular Scope解析与应用
Scope层级结构 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA== ...