Sự kế thừa và thành phần của Swagger


81

Trong API "đơn giản hóa" của tôi, tất cả các phản hồi đều có nguồn gốc ( kế thừa ) từ một lớp "phản hồi" cơ sở. Lớp phản hồi bao gồm phần đầu chứa siêu dữ liệu và phần thân chứa dữ liệu cốt lõi mà người dùng đang yêu cầu. Phản hồi (trong JSON) được bố trí sao cho tất cả siêu dữ liệu đều nằm trên "lớp" đầu tiên và phần thân là một thuộc tính duy nhất được gọi là "body" như vậy

response
|--metadata attribute 1 (string/int/object)
|--metadata attribute 2 (string/int/object)
|--body (object)
    |--body attribute 1 (string/int/object)
    |--body attribute 2 (string/int/object)

Tôi đã cố gắng xác định mối quan hệ này một cách vênh váo với JSON sau:

{
    ...
    "definitions": {
        "response": {
            "allOf": [
                {
                    "$ref": "#/definitions/response_header"
                },
                {
                    "properties": {
                        "body": {
                            "description": "The body of the response (not metadata)",
                            "schema": {
                                "$ref": "#/definitions/response_body"
                            }
                        }
                    }
                }
            ]
        },
        "response_header": {
            "type": "object",
            "required": [
                "result"
            ],
            "properties": {
                "result": {
                    "type": "string",
                    "description": "value of 'success', for a successful response, or 'error' if there is an error",
                    "enum": [
                        "error",
                        "success"
                    ]
                },
                "message": {
                    "type": "string",
                    "description": "A suitable error message if something went wrong."
                }
            }
        },
        "response_body": {
            "type": "object"
        }
    }
}

Sau đó, tôi cố gắng tạo các phản hồi khác nhau bằng cách tạo các lớp body / header khác nhau kế thừa từ body / header, rồi tạo các lớp phản hồi con bao gồm các lớp header / body có liên quan (được hiển thị trong mã nguồn ở dưới cùng). Tuy nhiên, tôi chắc chắn rằng đây là cách làm sai hoặc cách triển khai của tôi không chính xác. Tôi đã không thể tìm thấy một ví dụ về kế thừa trong đặc tả swagger 2.0 (được hiển thị bên dưới) nhưng đã tìm thấy một ví dụ về thành phần .

nhập mô tả hình ảnh ở đây

Tôi khá chắc chắn rằng "kẻ phân biệt đối xử" này có một phần lớn để chơi, nhưng không chắc tôi cần phải làm gì.

Câu hỏi

Ai đó có thể chỉ cho tôi cách người ta phải triển khai thành phần + kế thừa trong swagger 2.0 (JSON), tốt hơn là bằng cách "sửa chữa" mã ví dụ của tôi bên dưới. Cũng sẽ rất tuyệt nếu tôi có thể chỉ định một lớp ErrorResponse kế thừa từ phản hồi trong đó thuộc tính "result" trong tiêu đề luôn được đặt thành "error".

{
    "swagger": "2.0",
    "info": {
        "title": "Test API",
        "description": "Request data from the system.",
        "version": "1.0.0"
    },
    "host": "xxx.xxx.com",
    "schemes": [
        "https"
    ],
    "basePath": "/",
    "produces": [
        "application/json"
    ],
    "paths": {
        "/request_filename": {
            "post": {
                "summary": "Request Filename",
                "description": "Generates an appropriate filename for a given data request.",
                "responses": {
                    "200": {
                        "description": "A JSON response with the generated filename",
                        "schema": {
                            "$ref": "#/definitions/filename_response"
                        }
                    }
                }
            }
        }
    },
    "definitions": {
        "response": {
            "allOf": [
                {
                    "$ref": "#/definitions/response_header"
                },
                {
                    "properties": {
                        "body": {
                            "description": "The body of the response (not metadata)",
                            "schema": {
                                "$ref": "#/definitions/response_body"
                            }
                        }
                    }
                }
            ]
        },
        "response_header": {
            "type": "object",
            "required": [
                "result"
            ],
            "properties": {
                "result": {
                    "type": "string",
                    "description": "value of 'success', for a successful response, or 'error' if there is an error",
                    "enum": [
                        "error",
                        "success"
                    ]
                },
                "message": {
                    "type": "string",
                    "description": "A suitable error message if something went wrong."
                }
            }
        },
        "response_body": {
            "type": "object"
        },
        "filename_response": {
            "extends": "response",
            "allOf": [
                {
                    "$ref": "#definitions/response_header"
                },
                {
                    "properties": {
                        "body": {
                            "schema": {
                                "$ref": "#definitions/filename_response_body"
                            }
                        }
                    }
                }
            ]
        },
        "filename_response_body": {
            "extends": "#/definitions/response_body",
            "properties": {
                "filename": {
                    "type": "string",
                    "description": "The automatically generated filename"
                }
            }
        }
    }
}

Cập nhật sơ đồ

Để thử và làm rõ những gì tôi muốn, tôi đã tạo sơ đồ rất cơ bản dưới đây nhằm mục đích chỉ ra rằng tất cả các phản hồi đều là các bản khởi tạo của đối tượng "response" đã được tạo bởi (thành phần) bằng cách sử dụng bất kỳ sự kết hợp nào của các đối tượng response_header và response_body. Các đối tượng response_header và response_body có thể được mở rộng và chèn vào bất kỳ đối tượng phản hồi nào, điều này được thực hiện trong trường hợp tệpname_response sử dụng con tệp tên_response_body của lớp response_body cơ sở. Cả phản hồi lỗi và phản hồi thành công đều sử dụng đối tượng "phản hồi".

nhập mô tả hình ảnh ở đây


1
một mẫu cho các thành phần, nhưng nó xấu như vậy nó không phải là giá trị chia sẻ. Tôi sẽ làm việc xem thông số kỹ thuật của bạn sẽ như thế nào. Xin lưu ý rằng giao diện người dùng hiện không hỗ trợ nhưng sẽ có khi hỗ trợ đầy đủ cho 2.0.
Ron

1
Và trước khi tôi đi sâu vào, một điều nữa - bạn đang tìm kiếm bố cục hay sự kế thừa? Thành phần về cơ bản là nói I have the properties of X and my own properties.. Kế thừa gợi ý một mối quan hệ X is my parent. I have its properties and my own.. Kế thừa rất hữu ích nếu bạn muốn nói rằng một tập hợp các mô hình nhất định có thể áp dụng được của nguồn gốc đang được sử dụng.
Ron

1
Tôi đã hy vọng sẽ chứng minh việc sử dụng cả kế thừa thành phần trong một lần với ví dụ này. Rõ ràng là tôi nhận ra rằng người ta có thể dễ dàng sử dụng nó, nhưng trong trường hợp này, tất cả các phản hồi đều là con của lớp "phản hồi" cơ sở. Và lớp phản hồi được "bao gồm" hai đối tượng khác, phần đầu và phần thân.
trình

2
Tôi có thể đã không được rõ ràng. Kế thừa là một phần mở rộng của sáng tác. Nếu có kế thừa, có thành phần. Nếu có thành phần, không nhất thiết phải có sự kế thừa. Ngoài ra, trong mẫu của bạn, mô hình "phản hồi" không được sử dụng ở bất kỳ đâu. Tôi có nên bỏ qua điều đó và chỉ cho biết nó sẽ trông như thế nào?
Ron

à, không nhận ra mối quan hệ đó giữa kế thừa và thành phần. Vì vậy, sử dụng kế thừa để hiển thị cả hai. Liên quan đến mô hình phản hồi không được sử dụng, nó nên được sử dụng với "extension" trong con filename_response mà yêu cầu phản hồi.
trình

Câu trả lời:


113

Là một người mới bắt đầu vênh vang, tôi không thấy tài liệu chính thức về tính phân cực và bố cục dễ bị phá hủy, vì nó thiếu ví dụ . Khi tôi tìm kiếm trên mạng, có rất nhiều ví dụ điển hình được nhắc đến để vênh nhau 1,2 khi extendslà hợp lệ.

Đối với swagger 2.0, tôi đã tìm thấy một ví dụ điển hình trong các nguồn thông số kỹ thuật swagger trên github thông qua nhóm google này

Dựa trên các nguồn trên, đây là một ví dụ kế thừa hợp lệ ngắn trong YAML:

definitions:
  Pet:
    discriminator: petType
    required:
      - name
      - petType # required for inheritance to work
    properties:
      name: 
        type: string
      petType:
        type: string
  Cat:
    allOf:
      - $ref: '#/definitions/Pet' # Cat has all properties of a Pet
      - properties: # extra properties only for cats
          huntingSkill:
            type: string
            default: lazy
            enum:
              - lazy
              - aggressive
  Dog:
    allOf:
      - $ref: '#/definitions/Pet' # Dog has all properties of a Pet
      - properties: # extra properties only for dogs
          packSize:
            description: The size of the pack the dog is from
            type: integer

Rất cảm ơn! Điều này làm việc cho tôi. Trong editor.swagger.io, tôi thấy một lỗi nhỏ: Trong phần mô hình, tôi nhìn thấy Petmô hình nhiều lần. Nội dung của các mô hình này là ok. Chỉ có tên là sai.
schellingerht

@schellingerht Trong editor2.swagger.iobạn sẽ không thấy vấn đề này
Shiplu Mokaddim

Vấn đề duy nhất tôi tìm thấy với cách xác định kế thừa này, là thuộc tính petType hơi vô dụng trong lớp được tạo. Nó sẽ trống rỗng. Nhưng ít nhất nó cũng tạo ra hệ thống phân cấp lớp như tôi nghĩ. Cảm ơn!
xarlymg89

Để tạo json kế thừa như trên, bạn phải chú thích các lớp cha và con của mình như sau: @ApiModel (phân biệt = "type", subTypes = {Cat.class, Dog.class}) public abstract class Animal {} @ ApiModel (parent = Animal.class) public calss Cat expand Animal {}
Janet

Bộ phân biệt chỉ được sử dụng khi chúng ta triển khai một Giao diện Pet, còn nếu lớp A mở rộng lớp B thì chúng ta có nên sử dụng nó không? Cảm ơn bạn
Bionix1441

23

Tôi thấy rằng bố cục hoạt động tốt ngay cả khi không có định nghĩa về discriminator .

Ví dụ, cơ sở Response:

definitions:
  Response:
    description: Default API response
    properties:
      status:
        description: Response status `success` or `error`
        type: string
        enum: ["success", "error"]
      error_details:
        description: Exception message if called
        type: ["string", "object", "null"]
      error_message:
        description: Human readable error message
        type: ["string", "null"]
      result:
        description: Result body
        type: ["object", "null"]
      timestamp:
        description: UTC timestamp in ISO 8601 format
        type: string
    required:
      - status
      - timestamp
      - error_details
      - error_message
      - result

Được hiển thị dưới dạng:

Hình ảnh hóa phản hồi

Và chúng tôi có thể mở rộng nó để tinh chỉnh lược đồ tùy chỉnh của resulttrường:

  FooServiceResponse:
    description: Response for Foo service
    allOf:
      - $ref: '#/definitions/Response'
      - properties:
          result:
            type: object
            properties:
              foo_field:
                type: integer
                format: int32
              bar_field:
                type: string
        required:
          - result

Và nó sẽ được hiển thị chính xác như sau:

Trực quan hóa FooServiceResponse

Lưu ý, điều allOfnày là đủ để điều này hoạt động và không có discriminatortrường nào được sử dụng. Điều này là tốt, vì nó hoạt động và điều này quan trọng, theo tôi nghĩ, các công cụ sẽ có thể tạo mã mà không cần discriminatortrường.


Tôi cũng đã sử dụng allOf, nhưng bằng cách nào đó openapi.yaml, tôi thấy rằng các lớp con chứa các thuộc tính của lớp siêu một cách thừa, có đúng không?
Bionix1441

8

Tất cả các câu trả lời ở đây đã xuất sắc rồi, nhưng tôi chỉ muốn thêm một lưu ý nhỏ về thành phần so với kế thừa . Theo Swagger / OpenAPI Spec , để triển khai thành phần , sử dụng thuộc allOftính là đủ, như @oblalex đã chỉ ra một cách chính xác . Tuy nhiên, để triển khai kế thừa , bạn cần sử dụng allOfvới discriminator, như trong ví dụ của @ TomaszSętkowski .

Ngoài ra, tôi đã tìm thấy một số ví dụ Swagger khác về cả thành phầnkế thừa tại API Handyman. Chúng là một phần của loạt bài hướng dẫn Swagger / OpenAPI xuất sắc của Arnaud Lauret mà tôi nghĩ mọi người nên xem qua.


1
@ Mặc dù đăng các liên kết có liên quan là một khởi đầu tốt, nhưng để thực sự là một câu trả lời hữu ích, bạn cũng nên trích dẫn văn bản có liên quan sẽ được tìm thấy tại liên kết. Các câu trả lời chỉ có liên kết không được khuyến khích, bởi vì các liên kết sẽ chết thường xuyên.
Stijn de Witt,

3

Ví dụ tiêu chuẩn Swagger 2.0 mà bạn đã chia sẻ mô tả mối quan hệ thành phần, cụ thể là nó nắm bắt mối quan hệ "là một loại" của mối quan hệ siêu kiểu / kiểu phụ, tuy nhiên bản thân nó không phải là đa hình.

Sẽ là nếu bạn có thể tham chiếu định nghĩa cơ sở của Pet làm tham số đầu vào, sau đó chọn Cat hoặc nhập đối tượng Cat JSON làm giá trị cho yêu cầu đầu vào và điều này có thể chấp nhận được với Swagger UI.

Tôi không thể làm việc này để trực tiếp làm việc.

Cách tốt nhất tôi có thể làm là đặt các thuộc tính bổ sung thành true trên đối tượng cơ sở (ví dụ: Pet), chỉ định Pet bằng cách sử dụng tham chiếu con trỏ JSON làm lược đồ đầu vào và cuối cùng sao chép và dán đối tượng giá trị Cat JSON của tôi vào giao diện người dùng Swagger. Vì các thuộc tính bổ sung được cho phép nên giao diện người dùng Swagger đã tạo ra một tải trọng yêu cầu đầu vào hợp lệ.


Bạn không thực hiện đa hình qua dây (hoặc để lộ các thực thể dữ liệu của bạn) .. trừ khi bạn muốn kết hợp chặt chẽ với vụ hack cụ thể để làm cho nó hoạt động.
user1496062,

Tính đa hình được kích hoạt bởi kế thừa nhưng không bắt buộc phải sử dụng kế thừa. Về mặt logic, kế thừa là một mối quan hệ "là một" trong khi thành phần là một mối quan hệ "có-một". Ranh giới giữa cả hai có thể bị mờ tùy thuộc vào ngôn ngữ triển khai và các trường hợp sử dụng miền. Nhưng đó là điểm khởi đầu. Fwiw, bộ phân biệt cho phép giải mã các kiểu đa hình. Có những cách tiếp cận khác (ví dụ bao gồm tên lớp Java). Nhưng, đã đồng ý, những thứ này có thể khó di chuyển và không di động. Ví dụ: một ứng dụng khách python sẽ làm gì với tên lớp Java?
Charlie Reitzel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.