总结常见的违背Rest原则的接口设计做法
此文已由作者郑华斌授权网易云社区发布。
REST这词我们常常挂在嘴边,比如“开发一个rest接口”,又比如Spring项目的代码:
@RestControllerpublic class CommonController { @RequestMapping("/") public String index() { return "Welcome to Yanxuan DMS!";}复制代码
CommonController使用了@RestController注解,顾名思义,告诉读者这是一个Rest接口的实现。然而以@RestController注解的接口却不一定符合Rest原则。结合最近的项目,总结下常见的违背Rest设计的一些做法。
一、一律使用POST或者GET方法
典型的错误做法:无论什么请求,一律用POST,或者‘增删改’用POST,‘查’用GET。
其实REST有个原则叫统一接口(uniform interface),统一接口原则建议了各http方法的使用场合,
GET:获取资源,返回消息头和消息表示,即header和body。
HEAD:获取资源元数据,返回消息头
DELETE:删除资源
POST:REST设计中,POST通常用来为一个已有资源创建一个从属资源(subordinate resource),如AWS S3的POST Object(或者称web post)接口。
PUT:创建或修改一个资源
PUT和POST的区别比较微妙,这里拿AWS S3(或者参考网易对象存储NOS)的接口设计来举例。其中AWS S3的详细API文档参见:docs.aws.amazon.com/AmazonS3/la… S3有两种资源,桶(bucket)和对象(object),对象从属于某个桶。
创建一个桶的接口为:
PUT /BucketName HTTP/1.1
Host: s3.amazonaws.com复制代码
创建/修改一个对象的PUT Object接口为:
PUT /BucketName/ObjectName HTTP/1.1
Host: s3.amazonaws.com[对象数据]复制代码
AWS S3同时提供了POST Object接口,同样可以创建/修改一个对象,如下
POST /BucketName HTTP/1.1Host: s3.amazonaws.comContent-Type: multipart/form-data; boundary=9431149156168[包含对象数据的body]复制代码
获取对象的GET Object接口为:
GET /BucketName/ObjectName HTTP/1.1
Host: s3.amazonaws.com复制代码
同样的创建/修改一个对象,一个用PUT方法,另一个用POST方法,为什么?关键在于URL,PUT请求的目标URL(这里为/BucketName/ObjectName),就是将来用于获取该对象的URL,即PUT Object和GET Object的URL是一致的。但是POST Object的URL与GET ObjectURL不一样,POST 请求只知道父资源的URL(即/BucketName),表示在该父资源下创建新资源,至于新资源的确切URL,是由服务器决定的,一般来说是POST请求的响应应该包含一个Location消息头,其包含新建从属资源的URL。
安全性safe和幂等性idempotent
REST设计还应该遵循安全性和幂等性约束,如下:
GET和HEAD应当是安全的:GET和HEAD请求不应该导致服务器状态发生改变
GET、HEAD、PUT和DELETE应当是幂等的:向一个URL发送多次PUT和DELETE请求,跟只做过一次请求一样。比如PUT不能是append语义,否则不幂等。GET和HEAD也是幂等。
统一接口原则的好处:
给一个资源URI,不用看文档就知道可以有GET、DELETE等操作及其意义,世界通用。
安全性和幂等性增加了http的可靠性:如果请求没成功(但也许已成功了),只需重新发一次即可,不用担心副作用。
二、HTTP Code一律返回200
典型的错误做法:无论成功失败,HTTP Code一律返回200,具体错误信息交由json body里的内容来判断,举例如下,
某甲服务xxx接口的响应如下
HTTP/1.1 200 OK{ "status":1, //1: 成功 0: 参数异常 -1: 失败"message":"" //返回的消息成功时返回的数据
}复制代码
某乙服务xxx接口的响应如下
HTTP/1.1 200 OK{ "code":200, //1: 成功 0: 参数异常 -1: 失败"msg":"" //code非200时返回的错误信息"data":{成功时返回的数据内容}
}复制代码
其实RESTful的设计的一个标志特征是充分并正确利用HTTP响应码,典型的如:
200 -- OK,成功
301 -- Moved Permanently,重定向
400 -- Bad Request,错误的请求,比如缺少参数或者参数值不对
403 -- Forbidden,无权限访问
404 -- Not Found,url不存在
500 -- Internal Server Error,系统错误,如数据库访问失败或者bug导致的错误
设计REST接口应该遵循上面的响应码,语义明确并通用。如果像上面例子那样,任何情况都一律返回200,而具体成功与否需要到http响应消息体里去解析,而且不同的服务或开发者自定义消息体的格式,那么服务调用方就需要针对不同的服务写不同的判断逻辑,增加系统交互复杂性。
有些通用的客户端,会针对301自动处理重定向,针对500以上的响应自动重试,而一律返回200的设计是没法使用这些特性的,只能调用方一一自个处理。
三、 面向操作而不是面向资源的url设计
典型的错误做法:设计的URI是面向操作而不是面向资源的,举例如下,
某系统 设计的渠道相关的URI是这样的:
新增渠道
POST /xhr/thirdparty/admin/channel/add.json?{渠道信息参数}复制代码
编辑渠道
POST /xhr/thirdparty/admin/channel/update.json?{渠道信息参数}复制代码
删除渠道
POST /xhr/thirdparty/admin/channel/delete.json?channelId=id复制代码
这里的接口设计有三个特点:
http方法都是POST;
URI里携带操作信息,如URI里出现“add”,“update”,“delete”等字眼;
同一个资源由于操作不一样而URI不一样。
其实REST式的设计中,URI即是资源的名称,也是资源的地址,因为不同的操作而资源地址不一样是不合适的。资源的操作(方法信息)应该由统一接口来表示,即http 方法PUT、POST、GET、DELETE等,而不应该放到URI中。
对照统一接口和面向资源这两个特征来设计,上面的接口RESTful化可以是这样的:
新增渠道
POST /xhr/thirdparty/admin/channel[渠道具体信息]复制代码
修改渠道
PUT /xhr/thirdparty/admin/channel?channelId=id 或者PUT /xhr/thirdparty/admin/channel/${id}[渠道具体信息]复制代码
删除渠道
DELETE /xhr/thirdparty/admin/channel?channelId=id或者DELETE /xhr/thirdparty/admin/channel/${id}复制代码
渠道的地址为/xhr/thirdparty/admin/channel?channelId=id或者/xhr/thirdparty/admin/channel/${id},重在url唯一。
参考文献
《RESTful Web Services》
相关文章:
【推荐】 硬盘任性丢数据,但分布式存储一定可靠吗?
【推荐】 验证码示例代码演示——以java为例
【推荐】 JVM垃圾收集器(2)
总结常见的违背Rest原则的接口设计做法相关推荐
- 腾讯技术分享:微服务接口设计原则
来源|腾讯技术工程(ID:Tencent_TEG) 本文结合自身后台开发经验,从高可用.高性能.易维护和低风险(安全)角度出发,尝试总结业界常见微服务接口设计原则,帮助大家设计出优秀的微服务. 1.前 ...
- 其他系统 对外接口设计_设计模式分类及设计原则
点击蓝字,关注我们 01 概述 设计模式的基础是:多态. 学习设计模式将有助于更加深入地理解面向对象思想,让你知道: 1.如何将代码分散在几个不同的类中? 2.为什么要有"接口"? ...
- 6大设计原则之接口隔离原则
接口隔离原则的定义 什么是接口. 实例接口,比如定义了一个Person类,然后 Person p = new Pserson(); 产生一个实例,Person类就是 p 的接口 类接口,就是Java中 ...
- 软件接口设计 六大原则
转载自 http://www.cnblogs.com/zfc2201/p/3423370.html 一.单一职责原则 Single Responsibility Principle, 简称SRP. 定 ...
- 六大设计原则之接口隔离原则
接口隔离原则定义 接口隔离有两种定义: Clients should not be forced to depend upon interfaces that they don't use. 客户端不 ...
- 16. 设计模式之契约原则:如何做好 API 接口设计?
一.契约式设计原则:API 设计的指导书 无论是架构设计还是编码实现,现在都越来越离不开接口设计,接口可以说是新时代的"集装箱",是得到了几乎所有人一致共识的通用标准. GoF 在 ...
- API接口设计要考虑的几个重要原则和方法总结
转载:https://ask.zkbhj.com/?/article/254 2020博客地址汇总 2019年博客汇总 这里想和大家讨论的是在后台接口设计过程中,还有哪些方面需要考虑,以及还有哪些优秀 ...
- 软件设计原则之接口隔离原则、合成复用原则、迪米特原则
系列文章目录 软件设计原则之单一职责原则.开闭原则 软件设计原则之里氏替换原则.依赖倒置原则 软件设计原则之接口隔离原则.合成复用原则.迪米特原则 文章目录 系列文章目录 一.接口隔离原则 什么是接口 ...
- 面向对象编程原则(07)——接口隔离原则
版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 参考资料 <大话设计模式> 作者:程杰 <Java设计模式> 作者:刘 ...
最新文章
- 五点讲述C++智能指针的点点滴滴
- 表间数据复制--SELECT表中的数据插入到新的表中(ORACLE,MSSQL)
- 如何应用streamlit开发一个网络应用程序?
- BSTR、char*和CString转换
- iPad+MacBook+安卓手机的图书馆工作方案!超高效率!堪比移动工作站!
- 微信验证服务器是怎么回事,微信服务器认证为什么需要这么多参数?
- jfinal mysql date,jfinal与mysql数据类型拾掇大全
- Android: AndroidStudio使用OpenCV-Java
- eclipse配置python开发环境_eclipse怎样搭建Python开发环境
- 网上科学计算机,【图片】计算机-科普—都是从网上找的【计算机科学与技术吧】_百度贴吧...
- XWiki 6.3 M1 发布,Java 的 Wiki 系统
- codejock toolkit pro 源码零售版
- 利用wireshark分析Voip语音RTP协议
- C++中#define宏定义的min与max函数
- 【Devc++】战斗1.0
- python笔记5 - 字符串格式化表达式,while语句嵌套,for循环,break,continue,死循环
- sklearn降维算法1 - 降维思想与PCA实现
- 计算机实验员技能大赛,“迎接挑战”计算机技能大赛圆满结束!
- 面经——oppo2022校招Linux系统工程师
- 使用椭球表示物体的语义SLAM