Skip to content
ADP
API Design PrincipleBETA

[ADP-311] 過濾

TIP

如果您不需要邏輯過濾,可以(MAY)使用簡單的 ?k=v&a=b。然而,如果您的用例涉及邏輯,請(RECOMMENDED)考慮實現這個方法,而不是發明一個新的機制。或者,如果您有更好的想法,可以向 API 設計原則所有者提出,因為並沒有真正通用的 '過濾' 標準。

概述

過濾是 RESTful API 中的一個常見功能,允許客戶端只檢索符合特定條件的數據。本指南概述了在 RESTful API 中實現過濾的標準化方法,重點關注嵌套屬性和其他常見方法。

指導

  • 如果您的 API 支持過濾,應該(SHOULD)記錄過濾方法。
  • 應該(SHOULD)使用通用查詢參數 filter 進行資源過濾。

我們提出兩種不同的過濾方法: RHS(右側)和 LHS(左側)。

RHS 適用於簡單查詢,而 LHS 適用於複雜的邏輯操作。

RHS 過濾

  • 應該支持使用 RHS 查詢參數的複雜邏輯組合

    http
    GET /stores?filter=eq(name,John) HTTP/1.1
  • 應該支持嵌套過濾

    http
    GET /stores?filter=eq(user(name),John) HTTP/1.1
  • 可以支持邏輯操作

    http
    GET /stores?filter=gt(registeredAt,2024-07-13T00:00:00) HTTP/1.1

    支持的操作:

    • eq({屬性},{值}): 等於給定值
    • ne({屬性},{值}): 不等於給定值
    • gt({屬性},{值}): 大於給定值
    • ge({屬性},{值}): 大於或等於給定值
    • lt({屬性},{值}): 小於給定值
    • le({屬性},{值}): 小於或等於給定值
    • in({屬性},{值1},{值2},...): 包含列出的至少一個值
    • like({屬性},{值}): 包含類似於列出表達式的值
    • exists({屬性}): 給定路徑存在
    • and({查詢1},{查詢2},...): 邏輯與
    • or({查詢1},{查詢2},...): 邏輯或
    • not({查詢}): 邏輯非

LHS 過濾

  • 應該支持使用通用 filter 查詢參數的簡單 LHS 過濾

    http
    GET /users?filter.name=John HTTP/1.1
  • 應該支持嵌套過濾

    http
    GET /users?filter.address.region=eu HTTP/1.1
  • 應該支持使用 , 進行值的邏輯或操作

    http
    GET /users?filter.role=MANAGER,SUPERVISOR
  • 可以支持邏輯操作

    http
    GET /users?filter.createdAt:gt=2024-07-13T00:00:00 HTTP/1.1

    支持的操作:

    • filter.{屬性}:eq={值}: 等於(可以省略)
    • filter.{屬性}:ne={值}: 不等於
    • filter.{屬性}:gt={值}: 大於
    • filter.{屬性}:ge={值}: 大於或等於
    • filter.{屬性}:lt={值}: 小於
    • filter.{屬性}:le={值}: 小於或等於
  • 可以支持逗號分隔的值進行邏輯或操作

    http
    GET /users?filter.name=John,Jason
  • 可以添加額外的命名空間以滿足不同需求,圍繞屬性部分。如果實施,必須徹底記錄。

    http
    GET /licenses?filter=eq(features/featurelist,"nightwatch")

設計考慮

為什麼不使用僅屬性設計?

結果過濾的一種可能設計是僅使用屬性(property)名稱,例如:

http
GET /emaps?region=us&category=self-defined

這種方法的主要問題是可能與常見查詢參數發生衝突。隨著實現更多自定義查詢參數,發生衝突的可能性更大。

LHS/RHS

LHS(左側)意味著將邏輯放在 = 符號的左側。RHS 則放在右側。

  • 左側括號

    http
    filter[key1][gt]=x
  • 右側括號

    http
    filter[key1]=[gt]x
  • 左側冒號

    http
    filter.key:gte=y
  • 右側冒號

    http
    filter.key=gte:x

OpenAPI 示例

RHS 過濾示例

yaml
openapi: 3.1.0
info:
  title: 商店 API
  version: 1.0.0
paths:
  /stores:
    get:
      summary: 獲取商店列表
      parameters:
        - in: query
          name: filter
          schema:
            type: string
          example: eq(name,John)
          description: RHS 過濾參數
      responses:
        '200':
          description: 成功回應
          content:
            application/json:    
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Store'
components:
  schemas:
    Store:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        registeredAt:
          type: string
          format: date-time

LHS 過濾示例

yaml
openapi: 3.1.0
info:
  title: 用戶 API
  version: 1.0.0
paths:
  /users:
    get:
      summary: 獲取用戶列表
      parameters:
        - in: query
          name: filter.name
          schema:
            type: string
          example: John
          description: 按名稱過濾
        - in: query
          name: filter.role
          schema:
            type: string
          example: MANAGER,SUPERVISOR
          description: 按角色過濾(支持多個值)
        - in: query
          name: filter.createdAt:gt
          schema:
            type: string
            format: date-time
          example: '2024-07-13T00:00:00'
          description: 按創建時間過濾(大於)
      responses:
        '200':
          description: 成功回應
          content:
            application/json:    
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        role:
          type: string
        createdAt:
          type: string
          format: date-time

參考

設計參考

Changelog

  • 2024.09.20 Add tips for common cases.