新手问题 客户端 API 开发总结

i5ting · 2013年10月31日 · 最后由 maclinuxp 回复于 2016年10月31日 · 15007 次阅读
本帖已被管理员设置为精华贴

既然有人看,那咱就分享一下

API 标准写法

摘抄:http://www.startupcto.com/backend-tech/building-an-api-best-practices

You'll generally want to wrap all your API responses in an 'envelope' which specifies metadata about the APIcall.

// sample JSON envelope
{
  "status": {
    "code": 10000,
    "message": 'Success'
  },
  "response": {
     ...results...
  }
}

Doing this allows for client handler code to behave the same way for all API calls, since it gets a responses back in a universal format.

语义上再好一点,推荐:

// sample JSON envelope
{
  "status": {
    "code": 10000,
    "message": 'Success'
  },
  "data": {
     ...results...
  }
}

可视化编辑校验: http://jsoneditoronline.org/

注意事项

  • json 一定要规范,不然 ios 的 json 库无法读
  • 如果支持 jsonp,自己再加上一个 callback 就可以了。
  • 状态码说明:code:10000 类似的,要以业务或者功能模块来分类,以便 debug 的时候快速定位
  • 尽量遵守 rest

解释一个问题

http://ruby-china.org/topics/15164#reply5

xiaogui 7楼 , 19小时前  喜欢 
#5楼 @lytsingsun 
先说几点个人建议吧:
1、返回 json 数据,最好外面包一层校验,能让客户端快速知道,这数据合不合法、是否有错;
2、代码复用方面,主要看你项目的实际情况;
3、最好是能给客户端进行充分的沟通,怎么让双方都正规还方便;
4、性能方面,不外乎负载均衡、上缓存、数据库读写分离、代码优化;

第一点:就是说上面返回的 status,要有 code 和 message,根据 code 来判断问题,一般会直接 alert 出来 code:message 或者在日志里

这是 xiaogui 说的外面包一层校验的意思

其他的就没啥需要特殊说明的了

关于 http 的状态码和 api 里的 status.code 说明

http 的状态码是直接在 request 里的,这个一般指服务器的状态,api 是假设每一个请求都是 200 的,在客户端非 200 的请求统一处理异常,而 200 的请求,首先解读 status 获取 code 是否为 0(假设 0 是请求正确返回),如果是 0 就读取 data,继而完成相应业务逻辑处理。

  • 职责单一,该是 server 的就是 server,该是业务逻辑的就是业务逻辑的
  • 少用 http 状态码,把 http 状态码统一封装到一个类,用于处理异常情况(ajax 也好,ios 的 asi 或 afnetworking 也好)
  • 同样可以表示状态的,优先自定的状态码

为什么在 response json 里没有必要加 http status code

每一个 request 都可以获得状态码,而上面我给出的 json 是在 response 里,那么在在 http 请求完成的时候我们仍然可以获得此 request 的所有信息

如果仍然在 response 里加入 request 里的东西,这样做是重复的,真的是没有必要的,not DRY

换个角度讲,一般我们使用 http 库的时候要自己封装处理 http status code 的,尽力减少每个请求的代码,是不可能每个 request 都去

判断code==200.。。。。。然后。。。。。

技术选项

  • rails-api
  • grape
  • sinatra
  • go 的类似 sinatra 的框架beego

开发最佳实践

  • api 的 code 和 msg 可以写一个 gem,直接继承到 api 里
  • api 测试和 mock,最好是可以先 mock 出静态的,可以 api 和 mobile 端同时开发
  • 根据测试生成 api 文档

不过我还没有发现好的实现,还请各位指点

api 的最佳实践

还是看 open api 吧


当。。。。

关联优先

比如获取新闻接口返回数据如下:

{
    "data": [
        {
            "nid": 2,
            "mid": 1,
            "text": "this is a test.",
            "images": [
                "http://121.199.40.172:8086/images/news/12.png",
                "http://121.199.40.172:8086/images/news/13.png",
                ...
            ],
            "videos": [
                "http://121.199.40.172:8086/videos/news/12.amr",
                "http://121.199.40.172:8086/videos/news/13.amr",
                ...
            ],
            "like": 0,
            "comment": 0,
            "publish_time": "2013-01-01 12:30:10"
        },
        ...
    ],
    "status": {
        "code": "0",
        "msg": "success"
    }
}

返回的对象数组,乍看是合适的,但是我们发现 1 条新闻对应着多个评论,如果评论没有出现在此接口中,我就要发一个新的请求,这样问题就来了,该不该呢?

个人觉得,好的 api,如果一次 news 的数据不是特别多,就应该最新评论,如 5 条,10 条的带上

更新:使用 rack-cors 解决跨域问题

使用 rack-cors,解决跨域问题,实际中也是这样用,不过 application.rb 里配置的路由还需要更严谨

config.middleware.use Rack::Cors do
   allow do
     origins '*'
     resource '/*', headers: :any, methods: [:get, :post, :put, :delete, :destroy]
   end
 end

一般移动测试都要起一个 server,然后访问 api 服务器的时候就要跨域,这样就可以在移动端浏览器里测试了。

而 phonegap 打包的时候是本地 html 所以不用跨域,但是为了测试方便,服务器还是建议加上 rack-cors。

欢迎关注我的公众号【node 全栈】 node全栈.png

#1 楼 @hooopo 炮哥回复,荣幸之至啊

#2 楼 @i5ting 最近也在搞这个 有时间总结一下去

#3 楼 @hooopo 其实 api 的 code 和 msg 可以写一个 gem,直接继承到 api 里,再有就是 api 测试和 mock,最好是可以先 mock 出静态的,可以 api 和 mobile 端同时开发,不过目前还没有啥好的东西

客户端是异步的,服务端也应该返回这个 json 属于哪个请求的吧?不然客户端拿到 JSON,都不知道是对应哪个请求的,怎么进行下一步的处理呢。难道是 code:10000?呵呵。。感觉太难理解了这样做。除非有相当完善的文档,不然客户端和服务端都容易搞晕。

#5 楼 @zfjoy520 这个不需要的,如果真有这样的需要,只要在客户端里处理就好了,曾见过有人在 ios 里把 asi 里 request 里把 self 做参数传入,其实没啥必要,http 是无状态的,又有队列,何必一定要标示是哪个请求呢?

除了 render html => render json 之外好像也没有什么区别

#7 楼 @dddd1919 json 是目前最适合也是最常用的格式,主要是客户端里好解析,html 和 xml 这类的非常麻烦,虽然 sdk 里也有

#8 楼 @zfjoy520 其实 request 里也可以取到 url 信息的,施主多虑了,哇哈哈

新手求问,api client 应该都能直接收到 HTTP Status 吧?为何需要把 status code 放在 response body 里?

#11 楼 @larryzhao http 的状态码是直接在 request 里的,这个一般指服务器的状态,api 是假设每一个请求都是 200 的,在客户端非 200 的请求统一处理异常,而 200 的请求,首先解读 status 获取 code 是否为 0(假设 0 是请求正确返回),如果是 0 就读取 data,继而完成相应业务逻辑处理。

#11 楼 @larryzhao 有些 code 是不希望用 500 ==,最好是自己处理 error code 便于查找错误,http status 200 保证 api 有足够的容错

第一点:就是说上面返回的status,要有code和message,根据code来判断问题,一般会直接alert出来code:message或者在日志里

这是xiaogui说的外面包一层校验的意思

其他的就没啥需要特殊说明的了

这是校验的意思?

#13 楼 @dddd1919 嗯,而且乱用 http 状态码,比如登陆不成功你来个 404,那服务器真 404 了怎么呢?这种有业务含义的还是自定一个比较好的

#14 楼 @xstmjh 通过校验,才能返回 status 啊,这不是外面包一层校验的意思?

#13 楼 @dddd1919

有些code是不希望用500 ==

Could I have an example for that?

返不返回状态码应该是在理论和现实之间妥协的结果吧,事实上 http 的状态还是挺丰富的。http://httpstatus.es/ 如果是全 RESTful,应该能表示大多意思了。 无论哪种情况,都是需要维护一个关于返回状态的文档的。

#15 楼 @i5ting 这个的确是不对的,不过如果服务端是自己写的话,这点应该要尽量保证,

另外我看了你贴的 Github 的 API 的链接,好像也是没有把 Status 放在里面,而是在如果 Status 不是 20x/30x 等正确结果的时候,放一条错误信息。

另外我自己一直非常非常喜欢 Twitter 的 API 设计,他们应该也是没有把 Status Code 放到 Response Body 里的 https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline

由于我的确还没有真正设计过给移动端使用的 API,也没有在移动端做过开发,我大多时候是从我的 Ajax 经验角度来想的,如果可以这样类比的话 (我不能完全确定),我觉得其实就是像下面这样来解决。

$.ajax({
  url: <some api url>,
}).done(function(data) {
  // process data
}).fail(function(responseText) {
  // process error
});

那么这样做的问题在哪儿呢?由于最近开始做移动端的开发了,所以也就要遇到这方面的问题了,求教...

#19 楼 @larryzhao 虽然不想说,但是有时你会发现做 app 的同学们没你想的那么高大上....

#20 楼 @WolfLee 哈,不追求高大上哈,反而我很害怕追求高大上遇到实际的问题,因为我自己没有移动开发经验,所以想了解些可能会遇到的问题

#14 楼 @xstmjh 其实我说的第一点“校验”的意思有两部分: 1、校验数据是否经过篡改 1)客户端利用特定唯一的 key(如请求时间 + 用户标示 + 固定码)生成对应校验 value; 2)客户端向服务器发送请求,并将请求时间、校验 value 一同放入 json; 3)服务器在接收到客户端的请求时,同样拿 json 包体内部的请求时间、用户标示和事先约定的固定码生成校验 value,然后与 json 包体内部对比; 4)若校验 value,则抛弃该次请求,并做一些应对措施。

2、校验数据是否正确 这块就像楼主解释的那样。

是否使用“校验数据是否篡改”的措施,请根据你的客户端服务和实际情况酌情处理,这一块想了解更多,请参阅支付宝的部分 json 接口。

没有数据校验和安全加解密部分 (application_controller 覆盖 render 实现),可以考虑复写非 200 等状态的数据,将异常也转化成 json 等要求的格式。想使用同一个请求路径的话,api 就设计成命令模式,根据 cmd_str 再去分发,避免客户端需要调试不同的 api 路径。

非常感谢分享的链接,好好读一下,THX。

#17 楼 @larryzhao 比如数据库未找到数据后返回结果可以是

result = {
  "status": {
    "code": 1,
    "message": "Xxx data not found"
  }
}
render :json => result

为了快速找到请求出错的位置,把一些可预见的错误都写道返回信息里(像这种空结果或者写入错误的处理一般都是用 status 做处理了),这样发现问题调试非常方便。客户端处理 api 的数据首先也会判断 status code 是否是 0(请求结果正确)再决定是否进入下一步处理。我理解的这就是把 http 的状态码变成自定义的 code 方便追踪,另外客户端也可以根据 code 和 message 做一些友好的错误处理(如果不处理而产生 502 了客户端也只好呆傻了)

#24 楼 @dddd1919

首先我觉得,数据库未找到数据简单来说的话,我觉得应该从两个方面来看,

  • Model.find(id) 这样的情况应该是一个 404, Record Not Found
  • Model.where(<condition>) 这样的情况应该是 200, 并且返回一个空数组 (Most of the time)

另外我有疑问的是,把这个 status code 变成自定义的话,HTTP Status Code 还是正常设置么?对于上面这种情况,你 HTTP status code 应该是啥比较好?

#25 楼 @larryzhao 以前有用 find,现在都是 where 了,不知道两者除了 error 之外有没有其他区别。

  • Http status code 不用去管,404 这号的不是我该控制的,所以以前用 find 客户端可能会接收到 502,这样客户端就只能显示请求失败,502 产生的原因多种多样,可能是 find 错误也可能是其他错误
  • 一般做 api 的请求状态无非 200 404 302 502 这类的,404 是 routes 问题,自定义的 error code 主要是为了处理 502 302 等等异常的部分,让客户端也可以友好处理 api 的“502”

顺带问个问题,假如项目想完全脱离 rails view,前段框架用的是 Marionette。那么在处理表单的时候,那些与一个 model 相关联的字段如何绑定呢?比如一个表单的 select 选项(这个是需要查询数据库的)。有没有一个通用的解决方案,因为要解决一类通过弹出层的表单。

与楼主不谋而合,关于这点

  • api 测试和 mock,最好是可以先 mock 出静态的,可以 api 和 mobile 端同时开发

最近正在开发维护一个项目:API Mock Server

同类的实现有:

Python:https://github.com/tomashanacek/mock-server Scala: https://github.com/studiodev/Mocky

#28 楼 @zlx_star 非常好,有机会一起 code

#22 楼 @xiaogui 我怎么觉得你说的 oauth2 里的用户鉴权呢?每个 token 都有过期时间,如果传入的时间过期则重置 token。。。。

#24 楼 @dddd1919 说的挺好

我理解的这就是把http的状态码变成自定义的code方便追踪

这句是有问题的,每一个 request 都可以获得状态码,而不是在 response 里加入 request 里的东西,这样做是重复的,真的是没有必要的

用 http 库的时候都说要自己封装一层,这时候处理 http status code 的

#31 楼 @i5ting 差不多同样的意思,但这里主要是为了保证所传信息为未被篡改的原始信息。

#28 楼 @zlx_star 果断收藏~正需要这个~给你 32 个攒

#32 楼 @i5ting 我的 rails 是集 api+web,所以这么干了,纯 api 应该是直接封装 code 了

#34 楼 @cod7ce 很高兴能帮到你,欢迎提供反馈和贡献代码

#36 楼 @zlx_star 鄙视一下楼主,push request 了很久,都没反馈,更新不够及时啊,哈哈

#37 楼 @i5ting 我看到了你的 pull request,目前这个功能已经有了,所以就没有 merge。另外,我回复了你的 issue,你看看是不是错过了?

#38 楼 @zlx_star 我之前测试过,确实没有的,我是直接改代码,没有 rackup,不起作用的,可能和你理解的不一样吧

#38 楼 @zlx_star 我之前测试过,确实没有的,我是直接改代码,没有 rackup,不起作用的,可能和你理解的不一样吧

#38 楼 @zlx_star 我之前测试过,确实没有的,我是直接改代码,没有 rackup,不起作用的,可能和你理解的不一样吧

#38 楼 @zlx_star 我之前测试过,确实没有的,我是直接改代码,没有 rackup,不起作用的,可能和你理解的不一样吧

#42 楼 @i5ting 我们在 issue 上面讨论这个问题吧。

讨教一下...开发 API 之后,认证咋整。

  • 当然不会上 oauth, 因为是内部应用,认证不在我这里,没这个条件
  • 希望可以防止 replay attack
  • 实现基于某个现有的标准,或者事实上的标准

可供参考的例子不多

  • ruby-china 的 API, 直接用 Token 认证
  • gitlab API, 直接用 Token
  • api_auth, 满足需求,可是在 header 里面加了太多东西 (DATE & Content-MD5 & APIAuth), 感觉有点非主流。这样实现的问题是文档不好写,毕竟是给别人用的 API 的文档。

#44 楼 @ruohanc 当然是使用 token 了。而且不止一个。通常客户端类型一个,认证的一个。

api 请求的参数无效返回什么状态码?422 吗?

#45 楼 @jimrokliu 不太理解.......不止一个是啥意思..

#47 楼 @ruohanc 有时候你需要一个独立的 token 表示这个是官方版的 ios 请求。有时候需要给第三方伙伴一个 token,再加上你认证登陆的 token,一般都是两个。

不错的分享。

更新:使用 rack-cors 解决跨域问题

51 楼 已删除
52 楼 已删除
53 楼 已删除

#19 楼 @larryzhao 这个说法不错,http status 的 code 已经足够了,code 只是起分类的作用,如果需要 sub-code 来代替 message,倒是可以加在 response 里。

既然有人看,就在推荐个配套工具(Postman,咱就不说了,国外的),这里推荐个过国内的,Apizza(http://apizza.cc?f=rb),更简单,还支持文档

i5ting 如何写精华帖 提及了此话题。 04月03日 10:57
需要 登录 后方可回复, 如果你还没有账号请 注册新账号