RESTful API URI 设计: 查询(Query)和标识(Identify)
相关文章:RESTful API URI 设计的一些总结。
问题场景:删除一个资源(Resources),URI 该如何设计?
应用示例:删除名称为 iPhone 6 的产品。
是不是感觉很简单呢?根据应用示例,我们用代码实现下:
public class ProductsController : ApiController
{
[HttpDelete]
[Route("api/products")]
public async Task<HttpResponseMessage> DeleteByProductName(string productName)
{
...
}
}
客户端调用:
delete /api/products?productName=iPhone 6
网上有关 RESTful API URI 的一些文章,都是一些示例性质的,也就是说并不能真正运用到实际应用中,比如删除一个资源的 URI,它会告诉你应该这样设计比较好:delete /api/products/{productId}
,productId 是产品的唯一标识,删除资源必须通过唯一标识?示例应用中可以这样,但在实际应用中的场景,往往比示例复杂几十上几百倍,比如我们可以把上面的示例复杂点:
- 删除某一用户下,名称为 iPhone 6 和系统为 iOS 8 的产品
针对上面的应用示例,我们该如何设计 URI 呢?难道还要坚守“删除资源必须通过唯一标识”?如果真是这样,我们的解决方案就“简单”多了,对,没错,先查询再删除,就这么简单,但总感觉哪里不对,心里有个声音(不应该这样啊,肯定有其他解决方案)。另外,Delete 操作包含 ?productName=iPhone 6
,这种方式是不是也感觉很怪,为什么呢?我们一般在设计 MVC URL 或 WebAPI URI 的时候,? 后面一般跟的是查询条件,既然是查询条件,那配合 Delete 操作,就非常“怪”了,我们看一下 GitHub API 中的一个示例:
"user_search_url": "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"
?q={query} 一般用于 URI 的 GET 操作,那能不能用于其他操作呢?我们先来看下 Query 的权威定义:
The query component contains non-hierarchical data that, along with data in the path component (Section 3.3), serves to identify a resource within the scope of the URI's scheme and naming authority (if any). The query component is indicated by the first question mark ("?") character and terminated by a number sign ("#") character or by the end of the URI.
文字虽短,但信息量还是非常大的,主要是讲两个内容:Query 是什么?Query 的作用是什么?
non-hierarchical 是非分层结构的意思,也就是 URI 中的 /(前后结构),Query 并不包含非分层结构数据,简单一句话,? 之后并且 # 之前,这部分内容就是 Query,比如 user_search_url 中的 q={query}{&page,per_page,sort,order}
,那 Query 的作用是什么呢?标识资源(identify a resource),需要注意的是,上面那段话,丝毫没有提到 Get 的字眼,其实,URI 和 HTTP Method 没有半毛钱关系,更不是一一对应关系,这是我自己之前一个很深的误解,要纠正过来。
好,Query 的疑问解开了,我们接下来设计上面应用实例的 URI,大致两种方案:
delete /api/users/1/products?productName=iPhone 6&&system=iOS 8
delete /api/products?userId=1&&productName=iPhone 6&&system=iOS 8
这两种 URI 设计的不同点就是:第一个 URI 把 User 放在 Path 中,第二个 URI 把 User 放在 Query 中。到底哪种设计方案好呢?关于这个疑问,其实,我也没有确定的想法,然后在网上搜索了大量资料,但都是一些笼统的概念讲解,并没有针对问题的具体分析,偶然看到阮一峰的这篇博文《RESTful API 设计指南》,内容也是一大堆概念和简单示例,但有一个哥们的评论吸引了我的注意(遇到知音了),我直接复制下:
现在正在做一个基于RESTful API 架构的接口。但是遇到了一些令自己很困扰的问题:
假如每一个设备(devices)是拥有多张设备图片(images)的,那么按照RESTful,我们的URI 设计如下:
现在的设计:
- GET /devices/ID/images:获取某个设备的所有图片
- GET /devices/ID/images/ID:获取某个设备的某张图片(我们不提供改方法,有人认为没有意义的)
- POST /devices/ID/images:给指定设备添加一张图片
- PUT /Images/ID:修改某张图片(有疑问)
- DELETE /images/ID : 删除某张图片(有疑问)
我觉得合理的设计是:
- PUT /devices/ID/Images/ID:修改某个设备的某张图片
- DELETE /devices/ID/images/ID : 删除某个设备的某张图片
看到,对于修改和删除某张图片,我是有疑问的,现在的删除是直接通过图片ID进行删除(不管这张图片属于哪一个设备的)。其实真的符合RESTful标准吗? 他们这样设计的理由是:由于图片本身就有自己的图片ID,为什么直接通过ID来删除呢,还非得指定某个设备的图片,然后再删除。
RESTful 强调资源的唯一性,images/ID其实就是唯一的图片资源,而/devices/ID/images/ID也是设备的唯一图片,那么,我都不知道应该采用哪一个URI的设计。
看到上面是不是有点熟悉的赶脚呢,和我们的应用示例其实存在一样的疑问,但很遗憾,没有人回复这个哥们,我想回复他,但自己都没搞明白,拿什么回复呢?好,既然发现了同病相怜的人,那就更有勇气去搜索了,后来在茫茫的 Google 搜索中,又偶然发现这个帖子:Designing a REST api by URI vs query string.
这位哥们的疑问是,祖父母(外祖父母)下面是父母,父母下面是孩子,那如果去查询孩子,该如何设计 URI 呢?来看这位哥们的三种 URI 设计:
GET /myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text}
GET /myservice/api/v1/parents/{parentID}/children?search={text}
GET /myservice/api/v1/children?search={text}&grandparentID={id}&parentID=${id}
关于这个疑问,大神们做了详细的回答,每个回答都可以写成一篇文章了,我大概看了几篇,也是云里雾里的,大家如果有英文好的,可以翻译下这个帖子,我觉得是很有价值的。
下面说一下我自己的体会,首先,URI 中的层级结构,并不一定适用,比如上面那个示例,现在的情况是三级,但实际上是多极的,祖父母上面还有祖祖父母等等,所以,如果非要用层级结构来体现 URI,也并不是可取的,其次,要明确你要请求的资源到底是什么?祖父母、父母、还是孩子?如果是孩子,那么不管是哪种 URI 设计,层级结构中最后的那个词一定是 Childrens,这是肯定的,那如果存在层级结构关系,是设计成 Query?还是 Path 呢?我们看一下某一段回复:
I believe I have already thoroughly beaten this to death, but query strings are not for "filtering" resources. They are for identifying your resource from non-hierarchical data. If you have drilled down your hierarchy with your path by going /person/{id}/children/ and you are wishing to identify a specific child or a specific set of children, you would use some attribute that applies to children you are identifying and include it inside the query.
再强调下,Query 并不是过滤资源,而是 Identify 资源(非层级结构数据),他这样设计的 URI:/person/{id}/children/
,我觉得这是一个好的设计,族谱的结构可以再抽象出来,人都有孩子,不管是祖父母,还是父母,这个层级关系是确定的,由确定某一个人,再确定他的孩子,这样更加明确具体,至于之外的标识,都可以放在 Query 中,用来具体标识这个层级结构下的资源,所以,有时候可能不是你的 URI 问题,而是你的 URI 设计问题。
回过头看我们一开始设计的两种 URI,其实我自己觉得都是可以的,用户作为层级结构可以,作为标识资源(Query)也可以,毕竟它们其实没有特别强的层级结构关系,如果有一些层级关系,对于最末端的资源,标识资源中可以进行明确它,比如删图片那哥们的 ImageID,这个就可以直接标识某一个具体的图片,也可以不用 Device 来标识了,这个问题,我自己现在觉得,并没有唯一性,关键看具体的应用场景,和对 RESTful API URI 的理解,但不管怎样,设计出来的 URI,一定要简洁,并让别人看得懂。
我、删图片的那位哥们、还有族谱的那位哥们,所抛出的问题,虽然具体的应用场景不一样,但其实本质上都是一样的,我觉得像这类问题,可以深入探究下,这篇博文就到这。
再补充 REST 相关的一些文章:
- REST-ful URI design(非常赞)
- REST API Quick Tips
- REST
- RESTful API 设计指南
- REST 简介(园子里最好的)
- Understanding REST
- REST API Design - Resource Modeling
- 10 Best Practices for Better RESTful API
RESTful API URI 设计: 查询(Query)和标识(Identify)的更多相关文章
- RESTful API URI 设计: 判断资源是否存在?
相关的一篇文章:RESTful API URI 设计的一些总结. 问题场景:判断一个资源(Resources)是否存在,URI 该如何设计? 应用示例:判断 id 为 1 用户下,名称为 window ...
- RESTful API URI 设计的一些总结
非常赞的四篇文章: Resource Naming Best Practices for Designing a Pragmatic RESTful API 撰写合格的 REST API JSON 风 ...
- 好RESTful API的设计原则
说在前面,这篇文章是无意中发现的,因为感觉写的很好,所以翻译了一下.由于英文水平有限,难免有出错的地方,请看官理解一下.翻译和校正文章花了我大约2周的业余时间,如有人愿意转载请注明出处,谢谢^_^ P ...
- RESTful API的设计原则
好RESTful API的设计原则 说在前面,这篇文章是无意中发现的,因为感觉写的很好,所以翻译了一下.由于英文水平有限,难免有出错的地方,请看官理解一下.翻译和校正文章花了我大约2周的业余时间, ...
- 好的RESTful API的设计原则
转载自一位大佬 英文原版 Principles of good RESTful API Design Good API design is hard! An API represents a cont ...
- [Medium翻译]RESTful API权威设计指南-设计更好的API
本文为授权译文.希望查看原文的同学请戳链接:https://hackernoon.com/restful-api-design-step-by-step-guide-2f2c9f9fcdbf 对于我们 ...
- [转]10个有关RESTful API良好设计的最佳实践
Web API已经在最近几年变成重要的话题,一个干净的API设计对于后端系统是非常重要的. 通常我们为Web API使用RESTful设计,REST概念分离了API结构和逻辑资源,通过Http方法GE ...
- 10个有关RESTful API良好设计的最佳实践
Web API已经在最近几年变成重要的话题,一个干净的API设计对于后端系统是非常重要的. 通常我们为Web API使用RESTful设计,REST概念分离了API结构和逻辑资源,通过Http方法GE ...
- Restful API的设计与实践
Restful这个名称应该很多人都不陌生,但是我发现不少人对Restful存在或多或少的理解偏差,其中不泛比较厉害的程序员,所以有必要为Restful来“正名”. Restful是一种软件架构风格,设 ...
随机推荐
- 转载:iOS开发之让你的应用“动”起来
在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌.在这里你可以看到iOS中如何使用图层精简非交互式绘图,如何通过核心动画创建基础动画.关键帧动画.动画 ...
- [LintCode] Climbing Stairs 爬梯子问题
You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb ...
- CentOS7 编译安装 Mongodb (实测 笔记 Centos 7.0 + Mongodb 2.6.6)
环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G,双网卡) 系统版本:CentOS-7.0-1406-x86_64-DVD.iso 安装步骤: 1.准备 1.1 显示系统版 ...
- Python之路第一课Day11--随堂笔记(异步IO\数据库\队列\缓存之二)
一.RabbitMQ队列 1.安装: a.官网: 安装 http://www.rabbitmq.com/install-standalone-mac.html b.安装python rabbitMQ ...
- ASP.NET中基本语言特性
自动属性 public string Name { get; set; } 对象与集合的初始化 //自动推断类型//集合的初始化 var Products=new List<Product> ...
- MYSQL删除重复数据
delete from co_jobinformation cwhere c.name in (select cc.name from co_jobinformation cc group by ...
- Oracle EBS - PO Approval
PO Approval Except Standard Flow: 1. Personal setting
- C#程序代码分析(第三周)
刚开始看到这段程序,都不知道是什么东西,问过室友才知道是C#程序:但对C#一点都不了解,最基本的项目建设都不会,在室友的帮助下,以及在网上搜了一些资料,勉强算是完成了此次作业吧. using Syst ...
- ABP理论学习之审计日志
返回总目录 本篇目录 介绍 配置 通过特性开启/关闭 注意 我项目中的例子 介绍 维基百科说: "审计跟踪(也叫审计日志)是与安全相关的按照时间顺序的记录,记录集或者记录源,它们提供了活动序 ...
- .NET垃圾回收 – 原理浅析
在开发.NET程序过程中,由于CLR中的垃圾回收(garbage collection)机制会管理已分配的对象,所以程序员就可以不用关注对象什么时候释放内存空间了.但是,了解垃圾回收机制还是很有必要的 ...