RESTful API URI 设计

Posted

技术标签:

【中文标题】RESTful API URI 设计【英文标题】:RESTful API URI Design 【发布时间】:2012-10-23 20:45:03 【问题描述】:

我正在寻找有关 RESTful API 的 URI 设计的方向。我将有几个嵌套的链接资源,目前我设计的 URI 类似于这篇文章:Hierarchical RESTful URL design

以下示例不是我正在构建的示例,但我认为很好地说明了我的情况。 (假设一个节目只能属于一个网络)。

/networks [GET,POST]
/networks/network_id [GET,PUT]
/networks/network_id/shows [GET,POST]
/networks/network_id/shows/show_id [GET,PUT]
/networks/network_id/shows/show_id/episodes [GET,POST]
/networks/network_id/shows/show_id/episodes/episode_id [GET,PUT]

我的情况将通过关联进一步发展两个层次,但所有关联都是一对多的。我正在考虑将其切换为类似于以下内容:

/networks [GET,POST]
/networks/network_id [GET,PUT]
/networks/network_id/shows [GET,POST]

/shows [GET]
/shows/id [GET,PUT]
/shows/id/episodes [GET,POST]

/episodes [GET]
/episodes/id [GET,PUT]

我的问题是:

    第二个示例是有效的 REST 设计吗? 我应该考虑同时实现这两条路径吗?

【问题讨论】:

请记住,您最终决定了 RESTful API 的结构。将使用它的是您的客户。我认为两者都很好,尽管显然第一个更擅长说明网络、节目和剧集之间的层次结构。但是,如果 URI 变得异常长,那么将这些实体分离到 / 下也不是很疯狂。我会问自己,实际上哪些资源会被使用得最多。如果是剧集和节目,那么将它们放在基础之下很有意义。 【参考方案1】:

第二个例子对我来说很好。 URL 描述了资源,并且使用了正确的 HTTP 动词。

如果有意义的话,让多个 URL 指向同一个资源是完全可以的。但更重要的是,确保资源包含 <link /> 元素,这些元素将节目与网络、剧集与节目等联系起来。

【讨论】:

+1:我自己更喜欢xlink:href 属性,但<link> 元素也不错。 属性在 XML/Xhtml 中运行良好,但像 JSON 这样的一些表示不支持它们。无论如何,重要的是利用超媒体让服务真正实现 RESTful。【参考方案2】:

真正的问题是:您的第二个示例是否满足 URI 标准? URI 标准规定,路径包含分层部分,查询包含非分层部分,但是 afaik。它没有说明如何在您的情况下设计 URI 结构。 REST 统一接口约束有一个 HATEOAS 部分,这意味着您应该在您的情况下发回指向上层和下层资源的链接。您应该使用元数据注释这些链接,这些元数据可以由客户端处理,因此它会知道链接是关于什么的。所以在实践中,URI 结构并不重要......

GET /shows/123


    "label": "The actual show",
    "_embedded": 
        "episodes": [
            
                "label":  "The first episode of the actual show",
                "_embedded": 
                    "associations": [
                        //...
                    ]
                ,
                "_links": 
                    "self": 
                        "label": "Link to the first episode of the actual show",
                        "href": "/episodes/12345"
                    ,
                    "first": 
                        "href": "/episodes/12345"
                    ,
                    "duplicate": 
                        "href": "/networks/3/shows/123/episodes/12345"
                    ,
                    "up": 
                        "label": "Link to the actual show",
                        "href": "/shows/123"
                    ,
                    "next": 
                        "label": "Link to the next episode of the actual show"
                        "href": "/episodes/12346"
                    ,
                    "previous": null,
                    "last": 
                        "href": "/episodes/12350"
                    
                
            //,
            //...
        ]
    ,
    "_links": 
        "self": 
            "label": "Link to the actual show",
            "href": "/shows/123"
        ,
        "duplicate": 
            "href": "/networks/3/shows/123"
        ,
        "up": 
            "label": "Link to the actual network",
            "href": "/networks/3"
        ,
        "collection": 
            "label": "Link to the network tree",
            "href": "/networks"
        ,
        "next": 
            "label": "Link to the next show in the actual network",
            "href": "/shows/124"
        ,
        "previous": 
            "label": "Link to the previous show in the actual network",
            "href": "/shows/122"
        
    

现在这只是 HAL+JSON 中带有 IANA 链接关系的测试版,但您也可以将 JSON-LD 与 RDF 词汇表(例如 schema.org+hydra)一起使用。这个例子只是关于层次结构(上,第一个,下一个,上一个,最后一个,集合,项目等......),但您应该添加更多元数据,例如哪个链接指向一个网络,哪个指向一个节目,哪个指向一集,等等……所以您的客户将从元数据中知道内容是关于什么的,例如,他们可以使用这些链接自动导航。这就是 REST 的工作方式。所以 URI 结构对客户端来说并不重要。 (如果您想让响应更紧凑,也可以使用紧凑 URI 和 URI 模板。)

【讨论】:

【参考方案3】:

URI 是“任何可以命名的信息”

您的问题是与域相关的问题,只有知道您使用 URI 命名的资源的人才能真正回答。

在尝试猜测您的域时想到的问题是,“节目”真的依赖于“网络”吗?

您的域中的网络是什么?节目和网络是什么关系?只是单纯的播过节目的人吗?还是更多地与生产信息有关?

我相信您的示例 2 更合适。

【讨论】:

【参考方案4】:

考虑到您在以下层次结构中有一对多关系:

network --> shows --> episodes

我认为第二种设计没有向服务器端提供足够的信息来处理您的请求。例如,如果您有以下数据:

Network id  show_id episode_id
    1         1        1
    1         2        1
    1         1        2

第一个详细的设计将在 HTTP 请求中提供足够的信息来获取数据:/networks/1/shows/1/episodes/1

相反的第二种设计将有:

/episodes/1 

在第二种设计中,服务器端无法知道您是指数据中的第 1 行还是第 2 行

回答你的问题:

    恕我直言,第二个设计对于您的示例可能不是有效的 REST 设计。一种 解决方法可能是传递查询参数

    我认为设计 1 是自给自足的

更新:请忽略我上面的回答

    第二个设计是您的示例的有效 REST 设计 只有设计 2 就足够了

另外:

/networks
/networks/id

/shows
/shows/id

/episodes
/episodes/id

应该有足够数量的 REST URL

或者换句话说,以下 URL 将是多余的:

/networks/network_id [GET,PUT]
/networks/network_id/shows [GET,POST]

/shows/id/episodes [GET,POST]

【讨论】:

亲爱的 Vikram,如果我误解了,请纠正我,您是否建议剧集正确设计需要 3 列复合主键? @AlessandroOliveira 感谢您的评论。你的评论帮助我理解了问题中的第二个设计也是一个有效的设计!!回答您的问题:3 列复合主键只是理解这 3 个分层实体之间关系的一种表示。我不是要推荐它... 我非常关心的另一件事是将主键暴露为对象 ID,我认为即使系统具有身份验证和授权措施,这些实施决策也不应该暴露在任何大大地。但由于我是 UUID 的拥护者,我的立场也算是有点偏颇了。 @AlessandroOliveira 我同意你的看法【参考方案5】:

我认为我们应该使 REST API URL 尽可能简单。

例如https://www.yoursite.com/api/xml/1.0/

这里我以 1.0 版的 XML API 为例。请记住使用 API 版本以供将来更新。

您可以查看客户端请求的方法。

e.g. tag

<method>getEpisodes</method>

【讨论】:

这看起来不是特别 RESTful,而是非常 SOAPy(如果您正在这样做,这没关系,但是 SOAP 和 REST 是构建应用程序的非常不同的方法......) 我主要关心的是 API 的版本。例如我的稳定 API 版本是 1.2,我的客户在他们的应用程序中使用相同的版本。现在我们增强了我们的 API(1.4 版)并弃用了 1.2 版支持的 API 的一些功能。我使用 1.2 版的客户将收到有关已弃用功能的错误。请告诉我你的想法。【参考方案6】:

我认为第二个选项还可以,但如果您想验证关系,我会考虑第一个选项。例如,当您使用不同的网络获得一集时,这可能意味着该集在您的请求之前已被修改,因此您可能需要使用 422 响应,对于其他服务也是如此。有了这个,您可以确定您要处理的实体涉及其父级。

PD:对不起我的英语。

【讨论】:

以上是关于RESTful API URI 设计的主要内容,如果未能解决你的问题,请参考以下文章

RESTful URL 设计:公共与私有 API、分层 API 设计模式、URI 与 URL 设计?

RESTful URI 设计

架构师之路 — API 经济 — RESTful API 设计规范原则

RESTful API 设计 - 使用资源 URI 与 ID

架构师之路 — API 经济 — RESTful API 设计规范原则

RESTFul API 设计 简明指导与规范