Swagger 继承与组合

Posted

技术标签:

【中文标题】Swagger 继承与组合【英文标题】:Swagger Inheritance and Composition 【发布时间】:2015-03-07 21:11:24 【问题描述】:

在我的“简化”API 中,所有响应都派生自(继承)基础“响应”类。响应类组成 由一个充满元数据的标头和包含用户请求的核心数据的主体组成。响应(在 JSON 中)的布局使得所有元数据都在第一个“层”上,并且正文是一个称为“正文”的单个属性

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)

我试图用以下 JSON 大摇大摆地定义这种关系:


    ...
    "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"
        
    

然后我尝试通过创建从 body/header 继承的各种 body/header 类来创建不同的响应,然后创建由相关 header/body 类组成的子响应类(显示在底部的源代码中)。但是,我确信这要么是错误的做事方式,要么是我的实现不正确。我一直无法在swagger 2.0 specification(如下所示)中找到继承示例,但找到了composition 的示例。

我很确定这个“鉴别器”有很大的作用,但不确定我需要做什么。

问题

有人可以告诉我应该如何在 swagger 2.0 (JSON) 中实现组合+继承,最好是通过“修复”下面的示例代码。如果我可以指定一个继承自响应的 ErrorResponse 类,其中标头中的“result”属性始终设置为“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"
                
            
        
    

图表更新

为了尝试阐明我想要什么,我创建了下面的非常基本的图表,旨在显示所有响应都是由(组合)使用 response_header 和 response_body 对象的任意组合构建的“响应”对象的实例化. response_header 和 response_body 对象可以扩展并插入到任何响应对象中,这是在使用基本 response_body 类的 filename_response_body 子类的 filename_response 的情况下完成的。错误响应和成功响应都使用“响应”对象。

【问题讨论】:

这里一个作曲样本,但太糟糕了,不值得分享。我会研究你的规格应该是什么样子。请记住,用户界面目前不支持它,但在对 2.0 的完全支持可用时会支持。 在我深入研究之前,还有一件事——你在寻找组合还是继承?作文基本上是在说I have the properties of X and my own properties.。继承表明关系X is my parent. I have its properties and my own.。如果您想说某组模型适用于正在使用的父级,则继承很有用。 我更希望通过这个示例一次性演示继承组合的使用。显然,我意识到一个人可以轻松地单独使用其中任何一个,但在这种情况下,所有响应都是基本“响应”类的子级。响应类由另外两个对象“组成”,即标题和正文。 我可能还不清楚。继承是组合的延伸。如果有继承,就有组合。如果有组合,则不一定有继承。此外,在您的示例中,“响应”模型未在任何地方使用。我应该忽略它,只展示它的外观吗? 啊,没有意识到继承和组合之间的关系。所以使用继承来显示两者。关于未使用的响应模型,应与请求响应的 filename_response 子项中的“扩展”一起使用。 【参考方案1】:

作为 swagger 的初学者,我不觉得 official documentation 关于多态性和组合很容易理解,因为它缺少示例。当我在网上搜索时,当extends有效时,有很多good examples指的是swagger 1.2。

对于swagger 2.0,我通过google group在swagger spec sources on github 中找到了一个很好的例子

根据以上来源,这里有一个简短的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

【讨论】:

非常感谢!这对我有用。在editor.swagger.io 中,我看到了一个小错误:在模型部分,我多次看到Pet 模型。这些模型的内容还可以。只有名称是错误的。 @schellingerht 在editor2.swagger.io 你不会看到这个问题 我发现这种定义继承方式的唯一问题是,petType 属性在生成的类中有点没用。它将是空的。但至少它确实像我想的那样生成了类层次结构。谢谢! 为了像上面一样创建继承json,你必须注释你的父类和子类如下:@ApiModel(discriminator="type", subTypes =Cat.class, Dog.class ) public abstract class Animal @ApiModel(parent = Animal.class) public cals Cat extends Animal 判别器是不是只有在我们实现一个Interface的时候才会用到Pet,如果A类扩展了B类,我们是不是也应该使用呢?谢谢【参考方案2】:

我发现即使没有 discriminator 的定义,合成也能正常工作。

例如基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

呈现为:

我们可以扩展它来细化result字段的自定义模式:

  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

它会正确呈现为:

请注意,allOf 足以使其工作,并且不使用 discriminator 字段。这很好,因为它有效,而且我认为这很重要,工具将能够在没有discriminator 字段的情况下生成代码。

【讨论】:

我也用过allOf,但不知怎的在openapi.yaml中,我发现子类以冗余的方式包含了超类的属性,对吗?【参考方案3】:

这里的所有答案都已经很好了,但我只想添加一个关于composition versus inheritance 的小注释。根据Swagger/OpenAPI Spec,要实现composition,使用allOf属性就足够了,如@oblalex correctly points out。但是,要实现继承,您需要将allOfdiscriminator 一起使用,如example by @TomaszSętkowski。

另外,我在 API Handyman 找到了更多关于 composition 和 inheritance 的 Swagger 示例。它们是 Arnaud Lauret 的 excellent Swagger/OpenAPI tutorial series 的一部分,我认为每个人都应该查看。

【讨论】:

@虽然发布相关链接是一个好的开始,但实际上是一个有用的答案,您还应该引用将在链接中找到的相关文本。不鼓励仅提供链接的答案,因为链接会经常失效。【参考方案4】:

您分享的 Swagger 2.0 标准示例描述了一种组合关系,特别是它捕获了一种“是一种”超类型/子类型关系,但它本身并不是多态性。

如果您可以引用 Pet 的基本定义作为输入参数,然后选择 Cat 或输入一个 Cat JSON 对象作为输入请求的值,并且让 Swagger UI 可以接受。

我无法让它直接工作。

我能做的最好的工作是在基础对象(例如 Pet)上将 AdditionalProperties 设置为 true,使用 JSON 指针引用指定 Pet 作为输入模式,最后将我的 Cat JSON 值对象复制并粘贴到 Swagger UI 中。由于允许附加属性,因此 Swagger UI 生成了有效的输入请求负载。

【讨论】:

您不会在网络上进行多态性(或公开您的数据实体)..除非您想紧密耦合到特定的 hack 以使其工作。 多态性通过继承启用,但不需要使用继承。从逻辑上讲,继承是一种“is-a”关系,而组合是一种“has-a”关系。根据实现语言和领域用例,两者之间的界限可能很模糊。但这是起点。 Fwiw,鉴别器支持多态类型的反序列化。还有其他方法(例如,包括 Java 类名)。但是,同意,这些可能很笨拙且不便携。例如。 python 客户端将如何处理 Java 类名?

以上是关于Swagger 继承与组合的主要内容,如果未能解决你的问题,请参考以下文章

Swagger 仅适用于 apicontroller,不适用于 mvc 控制器?

Springboot继承swagger2

sprigboot--组合 swagger

动作需要 Swagger 的唯一方法/路径组合

spring boot整合swagger时,打开swagger-ui中文出现乱码

使用Swagger构建Node.js API文档与Mock Server