http api设计笔记 -凯发k8网页登录

记录工作/学习的点点滴滴。

http api设计笔记

前言

最近一段时间,要为一个手机终端app程序从零开始设计一整套http api,因为面向的用户很固定,一个新的移动端app。目前还是项目初期,自然要求一切快速、从简,实用性为主。

下面将逐一论述我们是如何设计http api,虽然相对大部分人而言,没有什么新意,但对我来说很新鲜的。避免忘却,趁着空闲尽快记录下来。

技术堆栈的选择

php嘛?团队内也没几个人熟悉。

java?好几年没有碰过了,那么复杂的凯发天生赢家一触即发官网的解决方案,再加上团队内也没什么人会 ……

团队使用过lua,基于openresty构建过tcp、http网关等,对lua nginx组合非常熟悉,能够快速的应用在线上环境。再说lua语法小巧、简单,一个新手半天就可以基本熟悉,马上开工。

看来,nginx lua是目前最为适合我们的了。

http api,需要充分利用http具体操作语义,来应对具体的业务操作方法。基于此,没有闭门造车,我们选择了 这么一个小巧的框架,用于辅助http api的开发开发。

嗯,openresty lua lor,就构成了我们简单技术堆栈。

http api简要设计

http api路径和语义

每一具体业务逻辑,直接在url path中体现出来。我们要的是简单快速,数据结构之间的连接关系,尽可能的去淡化。eg:

/resource/video/id

比如用户反馈这一模块,将使用下面比较固定的路径:

/user/feedback
  • get,以用户维度查询反馈的历史列表,可分页
    • curl -x get http://localhost/user/feedback?page=1
  • post,提交一个反馈
    • curl -x post http://localhost/user/feedback -d "content=hello"
  • delete,删除一个或多个反馈,参数附加在url路径中。
    • curl -x delete http://localhost/user/feedback?id=1001
  • put,更新评论内容
    • curl -x put http://localhost/user/feedback/1234 -d "content=hello2"

用户属性很多,用户昵称只是其中一个部分,因此更新昵称这一行为,http的 patch 方法可更精准的描述部分数据更新的业务需求:

/user/nickname
  • patch,更新用户昵称,昵称是用户属性之一,可以使用更轻量级的 patch 语义
    • curl -x patch http://localhost/user/nickname -d "nickname=hello2"

嗯,同一类的资源url虽然固定了,但http method呈现了不同的业务逻辑需求。

http api的访问授权

实际业务http api的访问是需要授权的。

传统的access token凯发天生赢家一触即发官网的解决方案,有session回话机制,一般需要结合web浏览器,需要写入到cookie中,或生产一个jsessionid用于标识等。这针对单纯面向移动终端的http api后端来讲,并没有义务去做这一的兼容,略显冗余。

另外就是 oauth 认证了,有整套的认证方案并已工业化,很是成熟了,但对我们而言还是太重,不太适合轻量级的http api,不太可能花费太多的精力去做它的运维工作。

最终选择了轻量级的 ,非常紧凑,开箱即用。

最佳做法是把jwt token放在http请求头部中,不至于和其它参数混淆:

curl -h "authorization: bearer eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9.eyj1awqioii2nyisinv0exblijoxfq.ljkzyriurtqiphsmvojnzz60j0szhpqn3tnqeemspo8" -x get http://localhost/user/info

下面是一副浏览器段的一般认证流程,这与http api认证大体一致:

jwt的lua实现,推荐: https://github.com/skylothar/lua-resty-jwt.git,简单够用。

jwt和lor的结合

jwt需要和业务进行绑定,结合 lor 这个api开发框架提供的中间件机制,可在业务处理之前,在合适位置进行权限拦截。

  • 用户需要请求进行授权接口,比如登陆等
  • 服务器端会把用户标识符,比如用户id等,存入jwt的payload负荷中,然后生成token字符串,发给客户端
  • 客户端收到jwt生成的token字符串,在后续的请求中需要附加在http请求的header中
  • 完成认证过程

不同于oauth,jwt协议的自包含特性,决定了后端可以将很多属性信息存放在payload负荷中,其token生成之后后端可以不用存储;下次客户端发送请求时会发送给服务器端,后端获取之后,直接验证即可,验证通过,可以直接读取原先保存其中的所有属性。

下面梳理一下jwt认证和lor的结合。

  • 全局拦截,针对所有path,所有http method,这里处理jwt认证,若认证成功,会直接把用户id注入到当前业务处理上下文中,后面的业务可以直接读取当前用户的id值
app:use(function(req, res, next)
    local token = ngx.req.get_headers()["authorization"]
    -- 校验失败,err为错误代码,比如 400
    local payload, err = verify_jwt(token)
    if err then
        res:status(err):send("bad access token reqeust")
        return
    end
    -- 注入进当前上下文中,避免每次从token中获取
    req.params.uid = payload.uid
    next()
end)
  • 针对具体路径进行设定权限拦截,较粗粒度;比如 /user 只允许已登陆授权用户访问
app:use("/user", function(req, res, next)
    if not req.params.uid then
        -- 注意,这里没有调用next()方法,请求到这里就截止了,不在匹配后面的路由
        res:status(403):send("not allowed reqeust")
    else
        next() -- 满足以上条件,那么继续匹配下一个路由
    end
end)
  • 一种是较细粒度,具体到每一个api接口,因为虽然url一致,但不同的http method有时请求权限还是有区别的
local function check_token(req, res, next)
    if not req.params.uid then
        res:status(403):send("not allowed reqeust")
    else
        next()
    end
end
local function check_master(req, res, next)
    if not req.params.uid ~= master_uid then
        res:status(403):send("not allowed reqeust")
    else
        next()
    end
end
local lor = require("lor.index")
local app = lor()
-- 声明一个group router
local user_router = lor:router()
-- 假设查看是不需要用户权限的
user_router:get("/feedback", function(req, res, next)
end)
user_router:put("/feedback", check_token, function(req, res, next)
end)
user_router:post("/feedback", check_token, function(req, res, next)
end)
-- 只有管理员才有权限删除
user_router:delete("/feedback", check_master, function(req, res, next)
end)
-- 以middleware的形式将该group router加载进来
app:use("/user", user_router())
......
app:run()

为什么没有选择graphql api ?

我们在上一个项目中对外提供了graphql api,其(在测试环境下)自身提供文档输出自托管机制,再结合方便的调试客户端,确实让后端开发和前端app开发大大降低了频繁交流的频率,节省了若干流量,但前期还是需要较多的培训投入。

但在新项目中,一度想提供graphql api,遇到的问题如下:

  • 全新的项目数据结构属性变动太频繁
  • 普遍求快,业务模型快速开发、调试
  • 大家普遍对graphql api有些抵触,使用json输出格式的http api是约定俗成的习惯选择

毫无疑问,以最低成本快速构建较为完整的app功能,http api json格式是最为舒服的选择。

虽然有些担心服务器端的输出,很多时候还是会浪费掉一些流量,客户端并不能够有效的利用返回数据的所有字段属性。但和进度以及人们已经习惯的http api调用方式相比,又微乎其微了。

小结

当前这一套http api技术堆栈运行的还不错,希望能给有同样需要的同学提供一点点的参考价值 :))

当然没有一成不变的架构模型,随着业务的逐渐发展,后面相信会有很多的变动。但这是以后的事情了,谁知道呢,后面有空再次记录吧~

posted on 2018-01-02 20:53 nieyong 阅读(2274) 评论(0)  编辑  收藏 所属分类: http移动后端


只有注册用户后才能发表评论。


网站导航:
              
 

公告

所有文章皆为原创,若转载请标明出处,谢谢~

新浪微博,欢迎关注:

导航

2018年1月
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

统计

常用链接

留言簿(58)

随笔分类(129)

随笔档案(149)

个人收藏

最新随笔

搜索

最新评论

阅读排行榜

评论排行榜

网站地图