关于 REST API 设计,我的看法

2015-03-17 11:49:05 +08:00
 yueyoum
如果是设计公共API,你给其他人提供服务,那么老老实实按照规范来,
因为规范就是统一的认知,也不会给别人带来多少困扰。

但如果是自家产品,我的经验就是不要REST了, 所有请求统统POST
参数加密后放入 request body 中

http://wtser.com/2015/03/08/who-cares-about-get-vs-post-norest.html

这篇文章 与我现在的观点一致。


除了文章中所说的,我再说几点好处:

1, 更方便的自动生成代码。
不用区分是GET,POST,DELETE 这些。客户端自动生成代码将极度简化。

2, 数据加密。
上面已经说了, 数据在 post body中,是加密的。
至少不去研究,直接抓包看不出什么。

3, 更方便的服务器安全控制。
自己的API都是POST,可能有一些与第三方厂商对接的接口是GET,
那么只要在nginx中 将第三方GET接口 设置为 可以GET访问
其他接口全部 要 POST 访问,非POST统统deny

4, 更明确的错误提示
客户端有一个输入框,输入其他用户ID (纯数字),可查看这个用户的信息
那么 REST API 就是 GET /userInfo/<ID>

URL ROUTE 配置应该是 <ID> 匹配纯数字 对不对? 这样才是明确的 REST API 啊
如果 用户输入有 汉字, 那么你返回 404呢?
还是返回200 但是 返回结果是这样的?

{
'ok': false,
'message': '用户ID只能是数字'
}

这个时候 客户端只要 判断返回的 ok 不为 true,直接弹框显示 message 即可。




总之我的经验就是全部POST,简单,方便,少写代码!
13271 次点击
所在节点    程序员
76 条回复
yyfearth
2015-03-17 14:42:29 +08:00
@yueyoum 你确实是对这方面不太了解 虽然你实践过这些API
但是如果你只用POST方法 那可以说就压根没有用到REST带来的任何好处 虽然实现起来比较方便

Web Service/API 常用的大致有3种 比较早的 RPC 然后是 SOAP 最近才是流行 REST
其中你的做法 实际上就是介于 RPC和SOAP 或者是更接近 RPC Style+JSON的做法
首先 RPC和SOAP 都不适用HTTP的方法
其中RPC一般来说读取用GET其他都用POST 也可以全部用POST(全部GET是非常糟糕的 但也有人这么做)
而 SOAP 和你一样 全部用POST 只不过数据都是XML 而且有很严格的规范

对于方法名 RPC和你一样 一般都是把方法名写在URL里面
不过既然是放在URL里面 那么何必拘泥于这几个方法
比如你可以直接映射Class里面的方法处理岂不是更直观 而且可以用生成器直接生成API
比如 POST /userService/setUserById /userService/getUserById ...

另外你说的 “配置文件写好简单的规则(xml) 自动生成代码” 这样的技术已经有了 就是 SOAP!
SOAP 全部POST
而且SOAP你定义好API规则后(WSDL) 客户端可以根据WSDL自动生成代码!
而且SOAP的XML数据支持企业级加密
主要的不同是你的数据是用JSON 而SOAP规范是用XML
而且XML数据支持校验(DTD、Schema) 而目前JSON还没有这方面的标准

REST 的好处 其实就是想要充分利用HTTP协议已经定义的东西
比如Method 比如Status Code 比如无状态 比如GET PUT DELETE幂等(多次调用等于一次) 而且GET应该不改变状态 所以可以Cache之类的

好了写了这么多 可以说楼主对现有的技术并不是很了解
但是通过实践总结出了很多经验和模式
但是如果你了解了现有的技术 其实你自己总结的很多东西 其实他们都已经考虑到了
而且可能考虑的更远 自己是在从新发明轮子罢了
或者你可以结合多种方案 找到你自己最喜欢的方案

衷心的建议LZ了解一下这些技术 并且比较优劣 和使用场景
https://zh.wikipedia.org/wiki/REST
https://zh.wikipedia.org/wiki/SOAP
https://zh.wikipedia.org/wiki/XML-RPC

另外还有一个不常用方案 就是使用Websocket/Commet实现一个双向的消息队列MQ
统一API入口 统一的数据格式 可以把方法名和参数封装在一个消息里面
satanwoo
2015-03-17 14:44:26 +08:00
@laoyur

TLS是握手阶段用了非对称加密,在传输过程中还是对称加密传输。
一般情况下,如果你信任了非认证的ca,被中间人流量劫持是很有可能的。
learnshare
2015-03-17 14:49:44 +08:00
@crs0910 Ghost 这个 blog 是前后分离的,可以参考 https://github.com/TryGhost/Ghost

https://github.com/TryGhost/Ghost/blob/master/core/server/routes/api.js 里面是 server 部分的 API,RESTful 的
yyfearth
2015-03-17 14:53:25 +08:00
@laoyur TLS本来就是非对称加密 至于证书的问题 你完全可以通过配置 让客户端严格检查证书即可
我相信浏览器和一般的安全库是支持的 只不过对浏览器而言 控制权在用户手里
另外我记得安装charles抓包的时候 你本地是可以信任证书的

自己模仿这个实现一个简单的 当然可以 只不过还不一定比TLS 实现的好 效率也是个问题

另外对于Web而言 不用https 你怎么加密都没用 因为中间人可以完全替换掉html或者js的内容
直接加入恶意代码(比如这里经常讨论的ISP劫持弹广告)直接处理你加密之前或者解密之后的内容(相当于直接安装了一个木马) 加密就形同虚设了

如果你真的非常注意安全 在https的前提下 对称或者非对称加密数据
而且对于敏感数据 比如密码 可以使用不可逆加密(加盐Hash)
zhicheng
2015-03-17 15:03:31 +08:00
@laoyur
这个不是协议的问题,是典型编码人员的疏忽。如果传输的是信用卡,密码等敏感数据的话,就是严重安全漏洞。对于能够犯这种错误的公司和工程师,即使你在 TLS 上再加一层加密,效果也不会强到哪里去。
一个关于安全的建议,永远选择用的人最多的那个方案。比如 TLS,比如 PGP 。
另外建议你可以多测试一些大公司的App,如果中间人成功,反馈给厂商应该会有奖金 :-)
HowardMei
2015-03-17 15:10:38 +08:00
@kxxoling 我也觉得如此,REST用在有些场景并不方便,另外对楼主"更安全"的说法不认可。
下面表里Web Native的接口有很多,SOAP比REST更复杂,但还有JSON/XML-RPC啊
http://wamp.ws/compared/
dustinth
2015-03-17 15:15:14 +08:00
不懂Rest就开始谈设计不妥吧.
laoyur
2015-03-17 15:18:36 +08:00
@satanwoo
@yyfearth
@zhicheng
感谢回复。
funsunz
2015-03-17 15:44:33 +08:00
我对RESTful的理解:
HTTP协议是一个[应用层]协议,
RESTful其实强调的, 就是重新认识这一点, 真正把HTTP当做应用层协议来用(相对于之前的webservice等将其作为传输层用):
比如URI就真正对应你业务的抽象资源
POST DELETE PUT GET这些HTTP协议定义的操作就真正对应你业务逻辑的增删改查
...
这样, 所有懂得HTTP协议的人或机器, 就都能明白你操作的语义.


我想楼主的意思是, 将HTTP完全作为传输层协议来使用.
只用POST, HTTP_STATUS永远都是200, 服务器-客户端传输细节,状态等全部构建于HTTP协议的上层.
在这种情况下, 楼主所说的"变得简单(简化)"是:
因为HTTP被当做了传输层, 那么只要将这一层的框架搭好, 比如
方法名(函数名)对应URI;
请求参数对应REQUEST BODY (不用管参数一会在path中, 一会在query string 中, 一会在请求body中....);
返回对应RESPONSE BODY.
这样, HTTP层就变成透明的了, 请求过程就好像是客户端直接远程调用了服务器上的方法(函数)一样.

如果是的话, 那本质上, 就是以前的webservice
lqs
2015-03-17 15:48:03 +08:00
现实很残酷:

有些透明代理不允许除了 GET POST 之外的方法
有些客户端会丢掉 PUT 请求中包含的 post data
有些防火墙会把 non-2xx 响应中的内容都替换成它自己的页面

当漂亮的 RESTful 设计遇到这些奇奇怪怪的实现,也只能妥协了。
est
2015-03-17 16:09:57 +08:00
@yueyoum 搞到最后,你发现URL都是多余的,直接一个 POST / ,然后在body里实现所有路由,分发,参数,签名,认证过程。

其实这样更加安全,我知道StartSSL就是这样搞的。全站页面都是 /

不过这样做有个问题:就是nginx/haproxy/varnish没法做负载均衡了。必须自己搞个支持9层路由分发的(HTTP是7层,JSON是8层,解密后的URL是9层) 。

最后恭喜发财,你又造了个轮子。
est
2015-03-17 16:16:13 +08:00
@fengliu222 你的例子感觉也没举好。。。

POST /transaction
from=1&to=2&amount=100

如果是 GET/HEAD/DELETE /transaction 感觉怪怪的。

我感觉应该是这样:

> 给 123这个角色 100 金币

POST /player/123/gold
amount=100
zyue
2015-03-17 17:10:00 +08:00
我是来看诸位写的rest url的...
justfly
2015-03-17 17:33:17 +08:00
看下来,这其实更像一场「得过且过,怎么方便怎么来,赶紧完活拿钱」和「愿意深究细节,代码洁癖,尽量完美」的态度之争吧。

个人感觉。。。
codegeek
2015-03-17 18:15:47 +08:00
除了get请求,其他请求也可以有body部分啊。
picasso250
2015-03-17 18:29:55 +08:00
我是来支持楼主的。虽然我不会像楼主那么极端全部用POST。但是我确实会怎么写的快怎么来,怎么样bug少怎么来,最好是别人一看就懂。

设想一个人问我API怎么设计的。我只需要告诉他我是RESTful的就行了吗?不行。不仅我需要在文档中写下各种请求方法,他还需要看文档,需要处理各种程度的细节。代码写的慢,bug还多。

当然,我并不反对REST,相反,理解幂等的含义,并在自己的API中运用是很重要的,会减少很多可能的bug。

毕竟,工程师是来(拯救世界)解决问题的,而非制造问题的。
learnshare
2015-03-17 18:43:37 +08:00
@lqs “有些”不服,来几个“栗子”


@est 全发到 / 上,相当于只有一个 API,用它干了所有的事情。耦合高,程序复杂,参数和状态到处都是
xiaohanyu
2015-03-17 20:31:13 +08:00
100% agree with @yyfearth
xcv58
2015-03-17 20:39:56 +08:00
「你们写四个函数,我写一个函数就能解决问题了?」
est
2015-03-17 21:48:34 +08:00
@codegeek GET 其实也可以有body的

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

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

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

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

© 2021 V2EX