开发者社区> 范大脚脚> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

RESTful API URI 设计: 查询(Query)和标识(Identify)

简介:
+关注继续查看

应用示例:删除名称为 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 相关的一些文章:


本文转自田园里的蟋蟀博客园博客,原文链接:http://www.cnblogs.com/xishuai/p/designing-rest-api-uri-query-and-identify.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
RESTful_URI资源
目录 目录 RESTful的资源 URI 标识资源 URL 定位资源 URI与URL的区别 为什么使用资源的概念 对资源的操作 URI的设计 RESTful的资源 在RESTful基础知识篇中,介绍了资源是存在于业务逻辑层中的概念实体(如:应用程序对象、数据库记录、算法、HTML、视频图像等),它会在表现层中被确定,并最终由表现层将资源向自动化客户端以URI的形式公开。
1014 0
REST与RESTFul API最佳实践
RESTFul API已经是现在互联网里对外开放接口的主流模式
5677 0
Restful API 的设计规范
Restful API 的设计规范 1. URI URI规范 资源集合 vs 单个资源 避免层级过深的URI 对Composite资源的访问 2. Request HTTP方法 安全性和幂等性 复杂查询 Bookmarker Format Content Negotiation 6. Response分页response 7. 错误处理 8. 服务型资源 9. 异步任务 10. API的演进 版本 URI失效 11. 安全 参考文档 本文总结了 RESTful API 设计相关的一些原则,只覆盖了常见的场景。
3545 0
RESTful API 设计指南
来源:http://www.ruanyifeng.com/blog/2014/05/restful_api.html 网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备......)。
758 0
**RESTful API版本控制策略
做RESTful开放平台,一方面其API变动越少, 对API调用者越有利;另一方面,没有人可以预测未来,系统在发展的过程中,不可避免的需要添加新的资源,或者修改现有资源。因此,改动升级必不可少,但是,作为平台开发者,你必须有觉悟:一旦你的API开放出去,有人开始用了,你就不能只管自己Happy了,你对平台的任何改动都需要考虑对当前用户的影响。
1239 0
swagger (可视化RESTful API的工具)
swagger 是一个可视化RESTful WebService的工具。 官网:http://swagger.io 效果 下图可以看出,swagger清晰地展现了web服务的方法、地址、发送json格式与应答json格式。还可以通过它直接进行服务调用,查看结果。 图1 swagger的效果 工作原理 视图部分: swagger-ui是一系列css\js资源,它通过html页
3685 0
+关注
3656
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载