简体   繁体   English

REST API设计:用于接收和更新资源的不同粒度

[英]REST API design: different granularity for receiving and updating resources

I'm in the process of creating a REST API. 我正在创建REST API。 Among others, there is a resource-type called company which has quite a lot of attributes/fields. 其中,有一种名为company的资源类型,它具有相当多的属性/字段。

The two common use cases when dealing with company resources are: 处理company资源时的两个常见用例是:

  • Load a whole company and all its attributes with one single request 使用一个请求加载整个公司及其所有属性
  • Update a (relatively small) set of attributes of a company, but never all attributes at the same time 更新公司的(相对较小的)属性集, 但不能同时更新所有属性

I came up with two different approaches regarding the design of the API and need to pick one of them (maybe there are even better approaches, so feel free to comment): 我提出了两种不同的API设计方法,需要选择其中一种方法(可能还有更好的方法,所以请随意评论):


1. Using subresources for fine-grained updates 1.使用子资源进行细粒度更新

Since the attributes of a company can be grouped into categories (eg street, city and state represent an address... phone, mail and fax represent contact information and so on...), one approach could be to use the following routes: 由于公司的属性可以分为不同类别(例如街道,城市和州代表地址......电话,邮件和传真代表联系信息等等),一种方法可以是使用以下路线:

/company/id : can be used to fetch a whole company using GET /company/id :可用于使用GET获取整个公司

/company/id/address : can be used to update address information (street, city...) using PUT /company/id/address :可用于使用PUT更新地址信息(街道,城市......)

/company/id/contact : can be used to update contact information (phone, mail...) using PUT /company/id/contact :可用于使用PUT更新联系信息(电话,邮件...)

And so on. 等等。

But: Using GET on subresources like /company/id/address would never happen. 但是:在/company/id/address这样的子资源上使用GET永远不会发生。 Likewise, updating /company/id itself would also never happen (see use cases above). 同样,更新/company/id本身也永远不会发生(参见上面的用例)。 I'm not sure if this approach follows the idea of REST since I'm loading and manipulating the same data using different URLs. 我不确定这种方法是否遵循REST的思想,因为我使用不同的URL加载和操作相同的数据。

2. Using HTTP PATCH for fine-grained updates 2.使用HTTP PATCH进行细粒度更新

In this approach, there are no extra routes for partial updates. 在这种方法中,没有用于部分更新的额外路由。 Instead, there is only one endpoint: 相反,只有一个端点:

/company/id : can be used to fetch a whole company using GET and, at the same time, to update a subset of the resource (address, contact info etc.) using PATCH . /company/id :可用于使用GET获取整个公司,同时使用PATCH更新资源的子集(地址,联系信息等)。


From a technical point of view, I'm quite sure that both approaches would work fine. 从技术角度来看,我很确定这两种方法都能正常工作。 However, I don't want to use REST in a way that it isn't supposed to be used. 但是,我不想以不应该使用的方式使用REST。 Which approach do you prefer? 你更喜欢哪种方法?

Do you really nead each and every field contained in the GET response all the time? 您是否真的知道GET响应中包含的每个字段? If not, than its more than just fine to create own resources for addresses and contacts. 如果没有,那么为地址和联系人创建自己的资源不仅仅是好的。 Maybe you will later find a further use-case where you might reuse these resources. 也许您稍后会找到一个可以重用这些资源的进一步用例。

Moreover, you can embed other resources as well in resources. 此外,您还可以在资源中嵌入其他资源。 JSON HAL ( ) fe explicitely provides an _embedded property where you can embed the current state of fe sub-resources. JSON HAL )fe明确地提供了一个_embedded属性,您可以在其中嵌入fe子资源的当前状态。 A simplified HAL-like JSON representation of an imaginary company resource with embedded resources could look like this: 具有嵌入资源的虚构公司资源的简化类似HAL的JSON表示可能如下所示:

{
    "name":"Test Company",
    "businessType":"PLC",
    "foundingYear": 2010,
    "founders": [
        {
            "name": "Tim Test",
            "_links": {
                "self": {
                    "href": "http://example.org/persons/1234"
                }
            }
        }
    ],
    ...
    "_embedded": {
        "address": {
            "street": "Main Street 1",
            "city": "Big Town",
            "zipCode": "12345",
            "country": "Neverland"
            "_links": {
                "self": {
                    "href": "http://example.org/companies/someCompanyId/address/1"
                },
                "googleMaps": {
                    "href": "http://maps.google.com/?ll=39.774769,-74.86084"
                }
            }
        },
        "contacts":  {
            "CEO": {
                "name": "Maria Sample",
                ...
                "_links": {
                    "self": {
                        "href": "http://example.org/persons/1235"
                    }
                }
            },
            ...
        }
    }
}

Updating embedded resources therefore is straigtforward by sending a PUT request to the enclosed URI of the particluar resource. 因此,通过向特定资源的封闭URI发送PUT请求,可以更新嵌入式资源。 As GET requests my be cached, you might need to provide finer grained caching settings (fe with conditional GET requests aka If-Modified-Since or ETAG header fields) to retrieve the actual state after an update. 由于GET请求我被缓存,您可能需要提供更细粒度的缓存设置(带有条件GET请求,也就是If-Modified-Since或ETAG头字段)来检索更新后的实际状态。 These headers should consider the whole resource (incl. embedded once) in order to return the updated state. 这些头应该考虑整个资源(包括嵌入一次)以返回更新的状态。

Concerning PUT vs. PATCH for "partial updates": 关于PUTPATCH的“部分更新”:

While the semantics of PUT are rather clear, PATCH is often confused with a partial update by just sending the new state for some properties to the service. 虽然PUT的语义相当清楚,但PATCH通常只是通过向服务发送某些属性的新状态而与部分更新混淆。 This article however describes what PATCH really should do. 然而, 本文描述了PATCH应该做什么。

In short, for a PATCH request a client is responsible for comparing the current state of a resource and calculating the necessary steps to transform the current resource to the desired state. 简而言之,对于PATCH请求,客户端负责比较资源的当前状态并计算将当前资源转换为期望状态的必要步骤。 After calculating the steps, the request will have to contain instructions the server has to understand to execute these instructions and therefore produces the updated version. 在计算步骤之后,请求必须包含服务器必须理解的指令才能执行这些指令,从而产生更新的版本。 A PATCH request is furthermore atomic - either all instructions succeed or none. 此外, PATCH请求是原子的 - 要么所有指令都成功,要么无。 This adds some transaction requirements to this request. 这会为此请求添加一些事务要求。

In this particular case I'd use PATCH instead of subresources approach. 在这种特殊情况下,我使用PATCH而不是子资源方法。 First of all this isn't a real subresources. 首先,这不是一个真正的子资源。 It's just a fake abstraction introduced to eliminate the problem of updating the whole big entity (resource). 这只是为了消除更新整个大实体(资源)的问题而引入的虚假抽象。 Whereas PATCH is a REST compatible, well established and common approach. PATCH是REST兼容,成熟且通用的方法。

And (IMO ultima ratio ), imagine that you need to extend company somehow (by adding magazine, venue, CTO, whatever). 和(IMO 最终比率 ),想象你需要以某种方式扩展公司(通过添加杂志,场地,CTO,等等)。 Will you be adding a new endpoint to enable client to update this newly-added part of a resource? 您是否要添加新端点以使客户端能够更新此新添加的资源部分? How it finishes? 怎么结束? With multiple endpoint that no one understands. 具有无人理解的多个端点。 With PATCH your API is ready for new elements of a company. 使用PATCH您的API已准备好用于公司的新元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM