Restful API 资源未找到应该返回什么状态码?

2019-06-27 21:20:04 +08:00
 zuoakang

需求是这样的: 查询某个用户是否存在,GET users/zhangshan

我这边使用的是 drf 框架,为了与框架统一,资源未找到返回的状态码都是 404。

与我这边对接的是 java 后台,他们觉得返回 404 会被他们 try cache,认为是请求参数有问题或者 URL 出错,程序不往下走了,而返回 200,response 为空,程序会往下走,而且知道 zhangshan 这个对象为空。

大家觉得资源不存在应该返回什么状态码呢?

12416 次点击
所在节点    程序员
93 条回复
Varobjs
2019-06-27 22:30:55 +08:00
try catch 住,是因为偷懒,把所有非 200 的报错了。
404 不是请求参数错误,请求参数有 400 可以表示
其实调用方不管什么,都要写很多处理啊,这种扯皮只有一个,两边不统一,不想改,懒。
df0618
2019-06-27 22:34:18 +08:00
@zuoakang 比如说,github 的 Api 是这样的
request: https://api.github.com/repos/aspnet/unknownrepo
response: code 404
{
"message": "Not Found",
"documentation_url": "https://developer.github.com/v3/repos/#get"
}

这是仓库不存在的情况

request: https://api.github.com/unknown
response: code 404
{
"message": "Not Found",
"documentation_url": "https://developer.github.com/v3"
}

这是资源不存在(url 错误)的情况
mamian
2019-06-27 22:34:34 +08:00
就算你接口正常返回 200 对方也需要 try catch,不使用 try catch 才是偷懒
Varobjs
2019-06-27 22:35:58 +08:00
如果 URL 是 users/zhangsan,写出 user/zhangsan 我觉得应该返回 404,没有疑问。
如果应该 zhangsan 不在数据库中,还是返回 200 + 具体 json 内容吧,
两个不能有歧义
johnniang
2019-06-27 22:49:00 +08:00
@zuoakang 接口响应 404,返回体中的都是一些自定义的内容,随意~
broadliyn
2019-06-27 22:58:13 +08:00
没必要把 restful 奉为圭臬,等你系统做大做复杂了,restful 怎么设计 url 符合规范会把你搞得纠结不已。
普通 get/post 完全能满足需求。

至于你说的 http status。我只想说,这个完全就是个人喜好 /团队规范的问题。
就个人来说,我喜欢 error_code 的方式来表述业务逻辑上边的错误,类似根据 id 获取资源不存在、用户余额不足等等都属于业务异常这个范畴。

http status 是用在什么地方呢?我一般会用在请求还没进入业务代码的这一层,比如我用的是 spring boot,前边有 nginx 做前端,nginx 上边的 403 无权限、500 内部服务器错误、502 网关异常、以及 spring boot 404 路径不存在、400 requestparam 参数不符合格式、405 method not support 等这类没有进入 controller 业务逻辑代码的错误统一使用 http status 做表述。
zhilincom
2019-06-27 23:13:15 +08:00
@zqx 上千个状态?不知道你们是怎么管理的?配置文件?写成枚举?用的时候不知道用哪个怎么办?如何防止重复定义?非常好奇。
Takamine
2019-06-27 23:16:04 +08:00
不要单纯用状态码来做这个(也完全不够用)。
只要是这次请求成功那么 http 返回的状态码就是 200,具体业务问题的返回码放在 response 里,用自定义的业务码表。
broadliyn
2019-06-27 23:17:28 +08:00
另外再补充一下为什么会将请求状态分为 http status 和 error code 自定义错误码两级,是因为在请求没有进入到业务 controller 时,请求可能会经过 waf 高防、nginx squid 等不同网关,每一个网关都可能会出错,而且每一个网关返回 http status 时,他们的格式都是不尽相同的,有的是 html、有的是 json、有的是 plain text,前端这边没办法做统一解析处理,因此对于在没有进入 controller 业务层以前的非 200 status,说明这次请求是失败的,前端这边只能根据 status 做粗的分类进行用户提示。

如果 http status 为 200,说明请求能顺利进入 controller 层,那么这时候返回的 json 格式完全能保证由开发者控制,前端也能顺利解析。

举个例子,某个 url 路径 GET /api/user/1 , 产品需求是,如果没有查找到这个 id 的用户,那么前端展示后端返回的错误提示。

在整个 http 请求过程中,出现 404 的地方可能会有哪些?
1. nginx 没有配置正确,返回了 404 的错误,response body 是 html 格式的 not found
2. 业务代码没有找到 id 为 1 的用户,返回了 404 错误,response body 是 json:{"code":-1,"msg":"用户未找到"}
3. CDN 回源失败,返回 404 错误,response body 是 html 格式的 404 页面
等等等...

假设发生了 404 错误,前端 js 代码 onerror=(resp)=>{if(resp.status==404) {....}},这时候你需要展示 404 相关的错误,你改怎么展示?格式内容不定,完全无法解析。

如果我们把上边的 2,改成 http status=200,前端判断到 status==200,那么就可以放心大胆的根据 code 和 msg 去做数据展示和错误展示,因为只要是 status==200 的,完全能保证返回的内容格式是预期的。


restful 提出来的时间是 2000 年,到现在已经快 20 年了,现代互联网云服务的 web 服务器架构和 20 年前已经相差很大了,
http 承载了更多的复杂业务和功能,已经不是 20 年前 web 1.0 那种 增删改查 PUT DELETE POST GET 就能满足需求了。
YzSama
2019-06-27 23:23:21 +08:00
其实,前后端的接口可以采用 restful api 设计。但是,服务端接口采用 get/post 比较好。然后就是响应状态码就 200。URL 路径设计可以参考 restful 设计。微信好像就是这么干
AlloVince
2019-06-28 00:05:13 +08:00
理论上应该返回 404

产生这个争执的根本原因是客户端并没有针对 RESTFul API 调整自己的处理逻辑

几年前写过一个客户端处理 RESTFul API 的流程伪代码,如果真的是严谨的 RESTFul API,客户端应该按如下流程处理异常

https://gist.github.com/AlloVince/4ec938b41ee2142333ca

```
//请求成功返回 2XX
if (statusCode.startWith('2')) {
//请求成功,处理业务
} else {
//5XX 错误,服务器有问题
if (statusCode.startWith('5')) {
//响应格式不定,显示网络错误或未知错误给用户
} elseif (statusCode.startWith('4')) {
//4XX 错误,输入有问题
//4XX 错误后端必须保证错误格式
res = json_decode(responseBody)
switch (res.errors[0].message) {
//客户端需要处理的异常分支
case 'ERR_USER_MOBILE_CAPTCHA_CHECK_FAILED':
//验证码错误
break
//客户端无法预料的异常分支
default:
//打印错误信息
print res.errors[0].message_human
}
} else {
//未知错误
}
}
```

问题在于,这样对服务端和客户端双方人员的要求都很高,需要能理解 RESTFul 的思想,并且一直维护状态码及 API 的约定,这对于人员经常有流动,缺少 Code Review 的项目来说,几乎不可能。而一旦出现了一个例外的 API,客户端处理起来就非常麻烦。所以大部分人都会选择更不容易出错的返回 200

当然,9012 年了,GraphQL 可以用起来了

如果对这个话题有兴趣,我的 Blog 有几个相关的 PPT 可供深入了解

https://avnpc.com/about#%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB-ppt
YakuMioto
2019-06-28 00:08:52 +08:00
看 Twitter, Github, Facebook
cuzfinal
2019-06-28 05:13:28 +08:00
看看 github 怎么搞的
jorneyr
2019-06-28 06:50:06 +08:00
我们返回 200,响应数据部分为 null,属性 data 是查询的响应数据:
找不到单个对象: { "code": 200, "success": true, "message": "随缘", "data": null }
找不到多个对象: { "code": 200, "success": true, "message": "随缘", "data": [] }
luozic
2019-06-28 07:03:19 +08:00
前后一致,不提供给第三方服务可以,否则,这叫制造不标准通讯
ericgui
2019-06-28 07:14:35 +08:00
我觉得返回 200,然后 data:null . internal_code: 404
sutra
2019-06-28 07:32:19 +08:00
404 with response body, in response body describe the error.
Imr
2019-06-28 08:08:07 +08:00
未找到的肯定是 403,报无权限访问
baiyi
2019-06-28 08:19:55 +08:00
@est #11 Roy T.Fielding 的得意作品不应该是 http1.1 吗
janus77
2019-06-28 08:23:35 +08:00
那你接口不存在也 404 ?前端如何区分?
所以 状态码和业务码需要分开。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/578131

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX