RESTful 修补和删除子资源最佳实践

问题描述 投票:0回答:1

我的后端有以下对象结构:

包含一些字段和产品报价列表的报价。

实际上在用户界面中,用户可以打开他的报价,单击编辑按钮并修改、添加新的、删除现有的产品报价、修改报价字段。他单击“确定”(应用更改)

如何为此设计 RESTful API?

我考虑的选项:

  1. 添加一个 PATCH(或 PUT)端点,它将接受包含 ProductOffers 列表的请求。该列表将更新现有的productOffers,创建新的productOffers,如果某些productOffer丢失,它将被删除。
  2. 类似的方法,但在我想要删除的productOffer中添加一个delete: true标志
  3. PATCH 仅在没有 id 的情况下添加新的 ProductOffers,或者在带有 id 的情况下修改现有产品。缺少产品优惠没有任何意义。另一个请求将到来,这将是批量删除。

我个人认为选项 2. 对于我的需求来说是最方便的,但我不想创建一个怪物。

大家觉得怎么样?

rest
1个回答
0
投票

我发现您面临的问题之一是在设计 REST API 时,您应该注意原子性,特别是对于在单个请求中处理多个操作或资源修改的端点。确保请求中的所有更改均已成功应用,或者没有应用,从而防止部分更新可能导致数据不一致。

考虑使用 PATCH /offers/{offerId} 端点来更新商品及其关联的产品商品。如果系统在处理过程中遇到错误(无效的 ProductOffer 数据),原子性可确保不会应用部分更改。

在这种情况下:

如果强制执行原子性,则不会应用任何更新,并且客户端将收到错误响应。 如果不强制执行原子性,则部分报价可能会被更新,从而给客户端留下部分修改的报价和不一致的数据。

另外,我认为您想要遵循的方法(选项 1、2 或 3)是对您的软件进行修补。如果您以一种轻松的方式实现端点,考虑到原子性,它将为您省去很多麻烦。在某种程度上,它会创建更多对后端的调用,人们往往会说这是一个糟糕的设计,但事实并非如此。如果您包含其他休息元素(例如缓存),那么这不是问题。 在

选项 1:尝试找出 UI 上的增量或更改会产生不同类型的问题:差异图(“差异图”问题是指您需要分析和可视化之间的差异的情况)两个图,通常通过突出显示同一图结构的两个版本之间添加、删除或修改的边和节点,本质上显示它们之间发生的变化,它通常在软件开发中用于比较不同版本的代码或数据结构 )

选项 2:您没有使用 HTTP 动词 + 原子性的 Rest 语义,这可以让您真正拥有删除正确元素的能力。

选项3:当你进行补丁时,你应该瞄准该字段,就像补丁提供而不是嵌套集合

我的建议是:

  1. 创建单独的资源,一种用于优惠,一种用于产品 与这些优惠相关。
  2. 有一个带分页的 get 请求 对于您的收藏,例如优惠或产品,如果您有一百万 产品,您不应该将其返回到您的 UI,您应该显示 您想要显示和编辑的内容(在您的 UI 上创建一个网格,如果 需要)
  3. 嵌套资源可以作为优惠的一部分返回,如果 你100%确定数据并不大
  4. 不允许更新 包括嵌套资源的资源,原子性是关键,如果你 想要避免 UI 上出现这种令人头疼的问题

我将继续这个实施。点击以下链接:

优惠API

我没有包含 HATEOAS(超媒体作为应用程序状态引擎 (HATEOAS) 是 RESTful API 中的一项原则,允许客户端通过 API 响应中的超媒体链接与应用程序交互),如果您想说您有REST API,你应该实现它,根据我的经验,并不是每个人都会达到这种程度的细节,所以你可以把它留在那里,但该 API 不是 REST,因为它不符合 REST 约束 WIKI 信息

这是该链接上提供的 swagger 定义,您可以将其粘贴到 swagger 编辑器 并检查所有详细信息:

openapi: 3.0.0
info:
  title: Offer and Product API
  version: 1.0.2
  description: RESTful API for managing Offers and associated Products.
servers:
  - url: https://api.example.com/v1
    description: Main API server
paths:
  /offers:
    get:
      summary: Get a list of all offers
      responses:
        '200':
          description: A list of offers
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Offer'
      examples:
        application/json:
          value: [
            {
              "id": "1",
              "name": "Summer Discount",
              "description": "Special summer discount for all items",
              "createdAt": "2023-08-01T12:00:00Z"
            },
            {
              "id": "2",
              "name": "Black Friday Offer",
              "description": "Huge discounts for Black Friday",
              "createdAt": "2023-11-24T10:00:00Z"
            }
          ]
    post:
      summary: Create a new offer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OfferCreateRequest'
      responses:
        '201':
          description: Offer successfully created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Offer'
        '400':
          description: Invalid request format

  /offers/{offerId}:
    get:
      summary: Get a specific offer by its ID
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer to retrieve
      responses:
        '200':
          description: A specific offer and its details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Offer'
      examples:
        application/json:
          value: {
            "id": "2",
            "name": "Black Friday Offer",
            "description": "Huge discounts for Black Friday",
            "createdAt": "2023-11-24T10:00:00Z"
          }
        '404':
          description: Offer not found
    put:
      summary: Replace an existing offer
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer to replace
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OfferCreateRequest'
      responses:
        '200':
          description: Offer successfully updated
        '400':
          description: Invalid request format
        '404':
          description: Offer not found
    patch:
      summary: Partially update an offer
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer to update
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OfferUpdateRequest'
      responses:
        '200':
          description: Offer successfully updated
        '400':
          description: Invalid request format
        '404':
          description: Offer not found
    delete:
      summary: Delete an offer
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer to delete
      responses:
        '204':
          description: Offer successfully deleted
        '404':
          description: Offer not found

  /offers/{offerId}/products:
    get:
      summary: Get a list of products associated with a specific offer
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer for which to retrieve products
      responses:
        '200':
          description: A list of products for the specified offer
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Product'
      examples:
        application/json:
          value: [
            {
              "id": "101",
              "name": "Laptop",
              "price": 899.99
            },
            {
              "id": "102",
              "name": "Smartphone",
              "price": 699.99
            }
          ]
        '404':
          description: Offer not found
    post:
      summary: Add a new product to a specific offer
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer to add the product to
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductCreateRequest'
      responses:
        '201':
          description: Product successfully added to the offer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
        '400':
          description: Invalid request format

  /offers/{offerId}/products/{productId}:
    get:
      summary: Get a specific product by its ID within an offer
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer the product belongs to
        - name: productId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the product to retrieve
      responses:
        '200':
          description: Product details for the specified product
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
        '404':
          description: Product not found
    put:
      summary: Replace a product within an offer
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer the product belongs to
        - name: productId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the product to replace
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductCreateRequest'
      responses:
        '200':
          description: Product successfully updated
        '400':
          description: Invalid request format
        '404':
          description: Product not found
    patch:
      summary: Partially update a product within an offer
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer the product belongs to
        - name: productId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the product to update
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductUpdateRequest'
      responses:
        '200':
          description: Product successfully updated
        '400':
          description: Invalid request format
        '404':
          description: Product not found
    delete:
      summary: Delete a specific product from an offer
      parameters:
        - name: offerId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the offer the product belongs to
        - name: productId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the product to delete
      responses:
        '204':
          description: Product successfully deleted
        '404':
          description: Product not found

components:
  schemas:
    Offer:
      type: object
      properties:
        id:
          type: string
          description: The unique identifier for the offer
        name:
          type: string
          description: The name of the offer
        description:
          type: string
          description: A description of the offer
        createdAt:
          type: string
          format: date-time
          description: The date and time the offer was created
      required:
        - id
        - name
        - description
        - createdAt

    OfferCreateRequest:
      type: object
      properties:
        name:
          type: string
          description: The name of the new offer
        description:
          type: string
          description: A description of the new offer
      required:
        - name
        - description

    OfferUpdateRequest:
      type: object
      properties:
        name:
          type: string
          description: The updated name of the offer
        description:
          type: string
          description: The updated description of the offer
      required:
        - name
        - description

   

此外,如果您仍然想采用发布整个有效负载的方法,但仍然打破 REST 约束,ODATA 实现可以为您提供提示,了解如何设计批量更新创建、检查请求正文以及您将看到提交到后端的不同帖子:

ODATA 批量

您也可以从这里的示例和插图中获得想法

ODATA 批量示例

© www.soinside.com 2019 - 2024. All rights reserved.