我经常遇到这样的问题,与此Hierarchical RESTful URL design
非常相似假设该服务仅向用户提供上传文档。
POST, GET /accounts
PUT, DELETE /accounts/{name} or /accounts/{id}
现在一个文件附加到一个特定的用户,无论是公开的还是根本不关注。
这两种方式是POST /documents vs POST /users/documents
为什么呢?因为稍后在创建文档资源时,该文档由用户控制。所以我希望有
GET, PUT, DELETE /users/{name}/documents
用于获取,更改和删除用户拥有的一大堆文档。
我可以GET, PUT, DELETE /users/{name}/documents/{name/id}
但同样可以/documents/{users}/.... or /documents/{id}
实现。这类似于组织unix文件的方式(尽管/users/...
也是组织文件的另一种方式......)你看,还有一种哲学[uri vs url design的哲学。
另一个考虑因素是API是否对用户可见。如果这只是一个后端API,只有开发人员可以访问(backend <- frontend server <- frontend ajax)
,那么网站用户可能会对/users/{name}/documents/{id/name}
更满意,而一些程序员如果API是公共的(例如twitter api)则不喜欢这个长网址
人们对这些问题的看法是什么?
答案 0 :(得分:2)
但哲学上,我认为它归结为REST中的“S”......这实际上是一个资源实际上是你管理状态的问题。如果您的应用程序旨在处理文档,那么这需要是URL中显而易见的资源。如果您的应用更多地是关于用户执行某些工作流程,那么您可能希望在URL中显示用户。
在应用程序内部,为方便起见,可以快速构建内容,以便您可以使用混合和匹配资源的URL来使所有权显而易见(如示例所示)。这样想吧。用户开始使用您的应用来处理他们的东西,其中一些是文档。他们知道他们已经登录,然后他们可以访问他们的文档,他们的一些信息在会话中与他们一起旅行,从那个角度来看
user/{name}/documents
有道理。他们有背景。
如果您公开该API ...也就是说,如果与您的应用中的用户没有相同视角的人正在使用您的API ...那么该公共面需要在语义上清晰 - 是用户裸露?文件?消费者是否关心谁拥有该文件?该API就像一个目录;它应该会有所帮助。
因此,如果您的api与其他应用程序的合同是您的应用程序正在公开文档,那么我会说您最好在您的URL中显示它。到达用户的路线的概念在这里没有意义,因为上下文与合同无关。
你提出了一个有趣的例子,清楚地表明了这一点。拿
document/id
与document/name
文件的命名可以遵循约定或特定于应用程序。很好的例子是图像共享应用程序或特定行业的应用程序,如会计但是公共API不能假设命名约定明显或明显(除非您允许API的消费者访问此类信息)。因为它是一个面向公众的API,所以最好不要使用
document/id
因为ID通常被理解为不可变的,即使在API中也是如此,语义非常清楚。
最终,该技术可以做任何我们想做的事情。但是路由和URL之类的东西对于理解API本身的语义非常重要。如果您正在管理某些内容,那么在使用API时这应该是显而易见的,并且不应该被您当地或技术特定的约定所困扰。
答案 1 :(得分:1)
好的,所以有一些你无疑会碰到,研究或偶然发现的惯例。然而,虽然我在这里可能不是100%正确,但现在暂且不谈惯例。
编写RESTful服务时,首先要考虑流程的层次结构以及谁拥有什么。换一种说法。如果我想删除属于一群人的文档,我不会写一个像/ user / doc / {id}的网址。像/ user / doc / {id}之类的东西通常在逻辑上意味着你首先处理的是与用户相关的东西,然后是一个文档,第三个是该用户的特定文档。
所以我想说的是,我通常会尝试将GET,POST,PUT,DELETE方法保留给实体,并将URL与实体的层次结构相关联。它是100%工作,不是,但是对于任何跟在我后面的编码器来说它是否显而易见,几乎总是答案是肯定的。
您对/ document / / user / docs的示例意味着您有两个不同的文档。将超出用户生命的文档以及将由用户帐户生存和死亡的文档。如果在我的例子中你有/ documents / usr / {id}那么我会说,即使你使用/ documents / usr / {id}“删除”该文档,我会说你只是删除那些用户引用该文档作为文档在层次结构中高于用户。同样,如果您有/ user / document / {id},我会说该文档是特定于用户的,并将与用户一起死亡。
希望这是有道理的。而且,再说一次,并不是说我100%正确,而且总是有特殊情况。
答案 2 :(得分:1)
我在这里看不到任何问题。 REST没有URI约束,这是一个实现细节,完全取决于服务开发人员。
REST是关于M2M通信而不是关于H2M通信,因此最终用户不会看到有关REST URI的任何信息。
如果应用HATEOAS约束,客户端也不会关注URI。如果你不喜欢超媒体响应,而是记录URI模板,那么我认为更短的URI更好,因为客户端开发人员更难以错误地编写它们。
关:
我目前正在阅读Vernon书中关于DDD聚合的内容,这里有一个有趣的相似之处。如果要访问用户文档,则可以使用2个建模选项。
user1 = userRepo.findById(1); user1docs = user1.documents
user1docs = docRepo.findByUserId(1)
。这有点类似于users/1/docs
vs docs?user=1
。
通过DDD,解决方案取决于业务需求。如果在某些情况下需要用户与其文档之间的事务性(即时)一致性,那么第一个是不错的选择,否则您可以坚持最终的一致性和第二个选项。
也许这也可以应用于REST。因此,如果文档和用户属性之间存在不变量,则必须使用
PUT /users/1 {
prop: 123,
docs: [
{},
{},
...
]
}
避免不一致的状态。否则最好坚持以下几点:
PUT /users/1 {
prop:123
}
POST /docs/ [
{},
{},
{}
]
POST /user-docs/ [
{user:1, doc: 1},
{},
{},
...
]
很难找到任何依赖于用户属性和用户文档的验证规则,因此我可能会选择第二个选项。
OFC。此规则可能有例外情况,例如,如果第二个选项速度慢得令人无法接受,或者您希望轻松地将用户文档添加到客户端的开发人员中,那么最好使用第一个选项。