其他 请教如何设计获取多个资源的 URL (或者 API)

zhangyuan · 2012年03月04日 · 最后由 vkill 回复于 2012年03月05日 · 6692 次阅读

比如,对于资源 Post, 请求一个资源的的地址为 http://example.com/posts/1 请求一个命名集合(collection)的资源,比如最新的 posts,可以用 http://example.com/posts/recent

如果我想请求多个离散的资源,比如 id=2, id=12, id=13, id=4 ,可以访问 http://example.com/posts/:id 这个接口 4 次,但这样多次请求,效率不高;在实际使用中,结合其他接口,很有可能出现类似于 SQL 中的 1+N 问题。所以希望在一次请求中,返回满足要求的结果。

SO 上有一个讨论: How to construct a REST API that takes an array of id's for the resources ,提到可以用类似 http://example.com/posts/?id=2,12,14,4 这种形式的接口。但是根据Query String的规范:

Letters (A-Z and a-z), numbers (0-9) and the characters '.','-','~' and '_' are left as-is SPACE is encoded as '+' or %20[citation needed] All other characters are encoded as %FF hex representation with any non-ASCII characters first encoded as UTF-8 (or other specified encoding)

所以 Query String 里的,应该用%2C代替,所以上面的 URL 应为

http://example.com/posts?id=2%2C12%2C14%2C4

这样看起来很别扭。有没有其他的解决方案呢?

呵呵,我也同问啊。

其实用 rails 的风格,数组应该使用id[]=2&id[12]&id[]=14&id[]=4,查询串应该为

> {:id => [2, 12, 14, 4]}.to_query
=> "id%5B%5D=2&id%5B%5D=12&id%5B%5D=14&id%5B%5D=4"

这个更别扭了⋯⋯

另外,在 id 很多的时候,有可能超过 URL 的长度,难道要用 POST 请求?

#2 楼 @zhangyuan 嗯,很像那么回事啊。不过 uri 真丑。

不清楚问题出在哪里,你是不希望那么写请求还是怎么的?如果不像这么写,你写个方法来转换不就可以了吗?

#5 楼 @azhao LZ 想让 uri 传数组且可以很漂亮

你们太纠结了,其实以逗号隔开的方式调用起来最简单,#1 楼 那样反而会很麻烦。

#5 楼 @azhao

有两个问题:

  1. 这种访问多个离散资源的 URL,有没有现在比较认可的模式(我希望使用约定俗成的东西,而不是自己搞一套规范)?
  2. 这种请求资源的方式应该用 GET 还是 POST?一般来说应该用 GET 请求,因为是幂等的。但是 GET 请求可能造成 URL 过长超出规范长度;使用 POST 请求,又好似修改了服务器的状态,觉得不妥。

http://www.infoq.com/cn/news/2008/12/restapi-must-be-hypertext-driven

楼主的问题实际上是所谓批量操作的问题

对于批操作,Fielding认为人们觉得需要批操作是因为他们没有理解资源的范围。他指出资源并非存储项(至少不等同于后台中某些存储项),并且同一资源状态可以由多个资源来分担。如果谁发现他需要一个批操作,那么很可能只是因为他没有定义足够的资源。(回复#21)

所以,如果你要简单,那么不必纠结, http://example.com/posts?id=2%2C12%2C14%2C4 没有任何问题。 如果你要追求 REST ,那么应该考虑的是,你为什么要用零散的 ID 数组获取一组资源,也许你需要的是定义另外一个资源抽象,这个新资源包括了那些零散的资源

#8 楼 @zhangyuan URL 最长可以允许多少字符?

#10 楼 @huacnlee 以前都是设计为 1024 以内的,他们的场景应该不能太特别吧

这样子 API 没问题, URL 就可以不用露出来了.

GET /posts/1,4,50,90

#10 楼 @huacnlee http://www.boutell.com/newfaq/misc/urllength.html 不同浏览器不同。一般如果怕 url 太长,就 post 吧

URL 长度好像和浏览器和服务都有关系吧,以前看过,好像是 2k/4k 这样的长度

呵呵,没想到什么好的解决办法

Rails 通常的 routes 按理说应该成为原子性的 API 接口,你可以在 controller 中对其进行扩展,同时提供多个资源。但是如果你不想把网站现在用的 controller 搞得一团糟,你可以把 api 的逻辑分开来写,独立成另一套 controller,你可以参考下 teambox 这个在 github 上的项目,虽然我觉得有点重复,不过他们的 api 写法确实能够解决问题。

#9 楼 @fsword LZ 的问题确实是批量操作的问题,不过在某些时候,我们并不好把客户端要传过来的 ids 抽象成资源,比如操作是在需要操作邮件这类的 list 的列表时,我们无法预知用户到底是要操作哪些邮件,因此这类我们怎么抽象?不过我认为您例子里面的 http://example.com/posts?id=2%2C12%2C14%2C4这样就很好了。也同时期待有更为优雅的解决方法。

#16 楼 @soloara 这还要看 “客户端” 是人还是机器,如果是机器,那么这个不太优雅的方式就够了,如果是人则有可能找到抽象,典型的例子看看 mail 的场景就明白,用户很少有 “获取第 1,3,7,8 封邮件 “的需求,他需要的是一封一封的认真看(原子操作),或者” 获取重要邮件”(批量操作),后者就可以抽象出的新的资源。 为什么人会比较特别?因为人的生理结构决定他在交互时不可能附加太多的信息,比如删除这件事,一个人可以指定删除几封邮件,但如果高达几百封则很容易出错,于是需求变成了搜索,又是不同的资源...

#17 楼 @fsword @zhangyuan 其实我对 LZ 的要批量操作一个无规律的数组的用的地方也有些疑问。如果是 post、put、delete 的操作并不应该把 ids 出现在 paramters 里面,丁丁说用 post 也许也是在指批量操作吧(我不代表他的意思)。好吧有点偏题了,不过正如@fsword 所说,什么应用场景下必须处理一个不可预知的 get 请求呢?

批量操作是 REST 的一个软肋之一,很多情况没必要再为了 REST 而 REST,

#19 楼 @hisea 这句话挺对的@poshboytl一直不是推崇要从实际出发有意 break 吗?要真把 current_user 抽象成单独资源也太傻了。??我歪楼了???

多个离散的资源 他们之间如果没共同点,就偏离了 REST 吧? 如果有共同点就简单哈 /xxx/:xxx_id/posts

需要 登录 后方可回复, 如果你还没有账号请 注册新账号