总结常见的违背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方法的使用场合,

  1. GET:获取资源,返回消息头和消息表示,即header和body。

  2. HEAD:获取资源元数据,返回消息头

  3. DELETE:删除资源

  4. POST:REST设计中,POST通常用来为一个已有资源创建一个从属资源(subordinate resource),如AWS S3的POST Object(或者称web post)接口。

  5. PUT:创建或修改一个资源

PUT和POST的区别比较微妙,这里拿AWS S3(或者参考网易对象存储NOS)的接口设计来举例。其中AWS S3的详细API文档参见:http://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html。 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设计还应该遵循安全性和幂等性约束,如下:

  1. GET和HEAD应当是安全的:GET和HEAD请求不应该导致服务器状态发生改变

  2. GET、HEAD、PUT和DELETE应当是幂等的:向一个URL发送多次PUT和DELETE请求,跟只做过一次请求一样。比如PUT不能是append语义,否则不幂等。GET和HEAD也是幂等。

统一接口原则的好处:

  1. 给一个资源URI,不用看文档就知道可以有GET、DELETE等操作及其意义,世界通用。

  2. 安全性和幂等性增加了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是这样的:

  1. 新增渠道

    POST /xhr/thirdparty/admin/channel/add.json?{渠道信息参数}
  2. 编辑渠道

POST /xhr/thirdparty/admin/channel/update.json?{渠道信息参数}
  1. 删除渠道

POST /xhr/thirdparty/admin/channel/delete.json?channelId=id

这里的接口设计有三个特点:

  1. http方法都是POST;

  2. URI里携带操作信息,如URI里出现“add”,“update”,“delete”等字眼;

  3. 同一个资源由于操作不一样而URI不一样。

其实REST式的设计中,URI即是资源的名称,也是资源的地址,因为不同的操作而资源地址不一样是不合适的。资源的操作(方法信息)应该由统一接口来表示,即http 方法PUT、POST、GET、DELETE等,而不应该放到URI中。

对照统一接口和面向资源这两个特征来设计,上面的接口RESTful化可以是这样的:

  1. 新增渠道

POST /xhr/thirdparty/admin/channel

[渠道具体信息]
  1. 修改渠道

PUT /xhr/thirdparty/admin/channel?channelId=id 或者PUT /xhr/thirdparty/admin/channel/${id}

[渠道具体信息]
  1. 删除渠道

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》


相关文章:
【推荐】 网易七鱼 Android 高性能日志写入方案
【推荐】 使用 typescript ,提升 vue 项目的开发体验(2)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,649评论 18 139
  • 一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式。”但是在要求详细讲述它所提出的各个约束,以及如...
    时待吾阅读 3,422评论 0 19
  • https://aws.amazon.com/cn/s3/faqs/#sia_anchor Amazon Simp...
    守望者_1065阅读 8,252评论 0 5
  • 记得年初找工作的时候收到了一份Offer由于大家劝不要到那家公司去所以对公司没有太重视反而要薪酬时狮子大开口没想到...
    承谦阅读 356评论 1 0
  • 一 碧空万里流云追, 芦笛声里麦鹅回。 都道天凉有秋意, 今日初尝个中味。 二 苇花纷扬山枫醉...
    时光清浅阿莲阅读 226评论 0 2