RESTful API 中的 Status code 是否要遵守规范

缘起 事情是这样的,我在知乎受到邀请回答一个问题,主要是问 ID 找不到到底要不要用 Status 404 。我回答的还是比较早的,那时候只有一两个回答。我本来以为这是没啥争议的,在一个学术的地方讨论学术问题,当然是要遵守规范了,结果过了几个小时大跌眼镜。自造 code 党竟然支持率第一,还好平时见的也很多的全 200 党没有受到支持,不然真的吐血了。 为什么要遵守规范 一般那种说特殊情况特殊处理,不要拘泥于规范的人,大多都是自己没搞清楚某些知识,拿这句话当作偷懒的借口。其实一般做项目没那么多特殊情况。 为了更好的适应各种库 大部分完善的 HTTP 请求库,都会依照 RFC 的规范去设计错误处理的流程,虽然处理方式各有不同,但一定会在文档说明错误处理的部分的。使用 RFC 标准能最大限度的兼容各种 HTTP 客户端。你说现在你用的HTTP客户端不处理 Status Code,但是你没法保证将来不重构,重构的时候还是不处理。 一般调用 api 使用 js 或者 python 的概率比较大,我们看看知名的库。在 js 里,最近比较流行的 axios 默认会把 200 系列外的 code 归到异常里。在 python 里,最流行的 http client 是 requests ,它更为详尽的预处理了 status code 。 为了开发者更好上手 另外在管理团队的方面,我们的原则是尽量的减少一个项目的“规范”,这样才能更容易去遵守。能用标准的地方,一定不要自己定一个更复杂的规则。无论是服务端的维护者还是 API 的消费者是会换人流动的,每个进入项目的人熟悉一大堆无谓的自定义项目规范都要成本。 更简单的办法是参考大厂 其实给项目定规范,最不靠谱的是自己拍脑袋,稍好一点的是去知乎或论坛问,更好一点的是去 google 搜,最简单的是直接去看大厂的产品或者规范啊。 API 本来就是个公开暴露的东西,还有比这更好找参考的吗?我们来看看: Google 遵守规范 Github 遵守规范 Microsoft 遵守规范 顺便说一句,微软的 API 规范真的很具有指导意义。 Twitter 遵守规范 阿里云 遵守规范 腾讯云 不遵守规范 全部 200 事实上腾讯的技术比较混乱,每个项目都不一样。但最新要执行的统一规范是全部 返回 200 用返回值中的错误码表明错误。 百度云 遵守规范 我的建议 很多人也许用着很简陋的 Web 框架,导致误以为返回了错误码,就不能返回 Response Body 了。其实你返回 204 外的任何 Status Code,最好都伴随着返回 Body 。 ...

2019年2月3日 · 1 分钟 · Hyacinthus

对 echo 框架进行统一的自定义错误处理

借助移动端的增长,如今 RESTful 风格的 API 已经十分流行, 用各种语言去写后端 API 都有很成熟方便的方案,用 golang 写后端 API 更是生产力的代表, 你可以用不输 python/ruby 这类动态语言的速度,写出性能高出一两个数量级的后端 API 。 ECHO 框架 由于 golang 的标准库在网络方面已经很完善,导致框架发挥余地不大。很多高手都说, 用什么框架,用标准库就写好了,框架只是语法糖而已,还会限制项目的发展。 不过我们并不是高手,语法糖也是糖,用一个趁手的框架还是能提高不少效率的。 要是在半年前,你让我推荐框架,我会说有很多,都各有优缺点,除了 beego 随便选一个就可以。 但是来到2017年,一个叫 Echo 的框架脱颖而出。这是我目前最推荐的框架。 Echo 的宣传语用的是 “高性能,易扩展,极简 Go Web 框架” 。它的一些特性如下图所示: 这些特性里,HTTP/2,Auto HTTPS,听着很熟?这是我之前介绍的 Caddy 也有的特性, 因为 golang 实现这些太容易了。还有 Middleware 里的一大堆功能也差不多。 我们在做微服务的时候,这些通用的东西由 API Gateway 统一实现就好了, 如果你写的是个小的独立应用的后端,这些开箱即用的功能倒是能提供很大的帮助。 其实今天我主要想说说最后一个特性里提到的,“中心化的 HTTP 错误处理”。 RESTful API 错误返回 一个团队应当有一份 RESTful API 的规范,而在规范中应该规范响应格式,包括所有错误响应的格式。 比如微软的规范, jsonapi.org 推荐规范等等。 大部分时候我们不需要实现的那么繁琐,我们规定一个简单的结构: STATUS 400 Bad Request { "error": "InvalidID", "message": "invalid id in your url query parameters" } 传统的错误响应可能只有一个伴随 HTTP Status code 的 string 类型的 message, 如今我们把正常的响应格式变成了 JSON ,那么把错误返回也用 JSON 吧。 除了用 JSON 之外,我们又增加了一个 error 字段, 这个字段是一个比 Status code 要详细一个级别的 Key, 消费端可以用这个约定的 Key 做更为灵活的错误处理。 ...

2017年4月10日 · 3 分钟 · Hyacinthus

使用 caddy 作为微服务的 API gateway

背景 大家都知道,Docker这些年让IT界产生了深刻的变革, 从开发到测试到运维,处处都有它的身影。 它同时也和微服务架构相互促进,并肩前行。 在最新版的 Docker(CE 17.03) 里,随着 swarm mode 的成熟, 在较简单的场景里已经可以不再需要专门的基础设施管理, 服务编排,服务发现,健康检查,负载均衡等等。 但是API gateway还是需要一个的。或许再加上一个日志收集, 你的微服务架构就五脏俱全了。 我们知道Nginx Plus是可以很好的胜任 API gateway 的工作的, 但它是商业软件。Nginx我们不说认证啊限流啊统计啊之类的功能, 单就请求转发这一点最基本的就出了问题。 我们知道Docker是用DNS的方式,均衡同一名称的服务请求到不同的node, 但是Nginx为了速度,在反向代理的时候会有一个不可取消的 DNS Cache, 这样我们Docker在根据容器的扩展或收缩动态的更新DNS,可Nginx却不为所动, 坚持把请求往固定的IP上发,不说均衡,这个IP甚至可能已经失效了呢。 有一个配置文件上的小Hack可以实现Nginx每次去查询DNS,我本来准备写一篇文章来着, 现在看来不用了,我们找到了更优雅的API gateway, Caddy 。 我上篇文章也写了一个它的简介。 接下来的所有代码,都在这个demo中, 你可以clone下来玩,也能在此基础上做自己的实验。 应用 我们先用golang写一个最简单的HTTP API,你可以用你会的任何语言写出来, 它为GET请求返回 Hello World 加自己的 hostname . package main import ( "io" "log" "net/http" "os" ) // HelloServer the web server func HelloServer(w http.ResponseWriter, req *http.Request) { hostname, _ := os.Hostname() log.Println(hostname) io.WriteString(w, "Hello, world! I am "+hostname+" :)\n") } func main() { http.HandleFunc("/", HelloServer) log.Fatal(http.ListenAndServe(":12345", nil)) } Docker 化 我们需要把上面的应用做成一个docker镜像,暴露端口12345。 接着才有可能使用Docker Swarm启动成集群。 本来做镜像特别简单,但我为了让大家直接拉镜像测试时快一点,用了两步构建, 先编译出应用,然后添加到比较小的alpine镜像中。大家可以不必在意这些细节。 我们还是先来看看最终的docker-compose.yml编排文件吧。 ...

2017年3月16日 · 1 分钟 · Hyacinthus