简体   繁体   English

更改 Swagger/Swashbuckle 导出的属性类型

[英]Change property type as exported by Swagger/Swashbuckle

I have a fairly complex object with nested objects;我有一个相当复杂的 object 嵌套对象; please note that in the example below I have simplified this object greatly.请注意,在下面的示例中,我大大简化了这个 object。

Assume the following example object:假设以下示例 object:

public class Result {
    public string Name { get; set; }
    public IpAddress IpAddress { get; set; }
}

I have implemented a JsonConverter<IPAddress> than (de)serializes the Ip as a string:我已经实现了一个JsonConverter<IPAddress> ,而不是将 Ip 序列化为字符串:

public class IPAddressConverter : JsonConverter<IPAddress>
{
    public override IPAddress Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => IPAddress.Parse(reader.GetString());

    public override void Write(Utf8JsonWriter writer, IPAddress value, JsonSerializerOptions options)
        => writer.WriteStringValue(value.ToString());
}

The IPAddressConverter was then 'registered' as a converter in the AddJsonOptions(...) method.然后IPAddressConverterAddJsonOptions(...)方法中被“注册”为转换器。 This nicely returns results as:这很好地将结果返回为:

{ "Name": "Foo", "IpAddress": "198.51.100.1" }

And, vice versa, my controller "understands" IP addresses specified as string:反之亦然,我的 controller “理解” IP 地址指定为字符串:

public IEnumerable<Result> FindByIp(IpAddress ip) {
    // ...
}

However, SwashBuckle exports this as:但是,SwashBuckle 将其导出为:

{
  "openapi": "3.0.1",
  "info": {
    "title": "Example",
    "version": "v1"
  },
  "paths": {
    "/FindByIp": {
      "get": {
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/Result"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AddressFamily": {
        "enum": [
          0,
          1,
          2,
          3,
          4,
          5,
          6,
          6,
          7,
          7,
          8,
          9,
          10,
          11,
          12,
          13,
          14,
          15,
          16,
          17,
          18,
          19,
          21,
          22,
          23,
          24,
          25,
          26,
          28,
          29,
          65536,
          65537,
          -1
        ],
        "type": "integer",
        "format": "int32"
      },
      "IPAddress": {
        "type": "object",
        "properties": {
          "addressFamily": {
            "$ref": "#/components/schemas/AddressFamily"
          },
          "scopeId": {
            "type": "integer",
            "format": "int64"
          },
          "isIPv6Multicast": {
            "type": "boolean",
            "readOnly": true
          },
          "isIPv6LinkLocal": {
            "type": "boolean",
            "readOnly": true
          },
          "isIPv6SiteLocal": {
            "type": "boolean",
            "readOnly": true
          },
          "isIPv6Teredo": {
            "type": "boolean",
            "readOnly": true
          },
          "isIPv4MappedToIPv6": {
            "type": "boolean",
            "readOnly": true
          },
          "address": {
            "type": "integer",
            "format": "int64"
          }
        },
        "additionalProperties": false
      },
      "Result": {
        "type": "object",
        "properties": {
          "ip": {
            "$ref": "#/components/schemas/IPAddress"
          },
          "name": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      }
    }
  }
}

Which, for the more visually inclined, looks like:其中,对于更具视觉倾向的人来说,看起来像:

截屏

What I'd like to achieve, however, is this:然而,我想要实现的是:

{
  "openapi": "3.0.1",
  "info": {
    "title": "Example",
    "version": "v1"
  },
  "paths": {
    "/FindByIp": {
      "get": {
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/Result"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Result": {
        "type": "object",
        "properties": {
          "ip": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      }
    }
  }
}

Again, visualized:再次,可视化:

截屏

I was hoping to be able to add an annotation / attribute on some properties (so I looked at Swashbuckle.AspNetCore.Annotations ) but that doesn't seem to be possible.我希望能够在某些属性上添加注释/属性(所以我查看了Swashbuckle.AspNetCore.Annotations ),但这似乎是不可能的。

Also, because the object is fairly complex and comes from a 3rd party library it's hard for me to actually add annotations / attributes on properties because I can't change the model (easily).此外,由于 object 相当复杂并且来自第三方库,因此我很难在属性上实际添加注释/属性,因为我无法(轻松)更改 model。

I could resort to AutoMapper (or alike) to create another model with a string for IP adresses but that would mean having to model all objects in the original model. I could resort to AutoMapper (or alike) to create another model with a string for IP adresses but that would mean having to model all objects in the original model. Besides, it requires extra code and maintenance when the model changes.此外,当 model 发生变化时,它需要额外的代码和维护。 I'd rather tell Swashbuckle, somehow, that IP adresses (and, so, the type IPAddress will be represented as a string (in- and outgoing to my API). I'm looking for options on how to accomplish this the best way possible within given limitations (preferably not introducing new models to map to, preferably no annotations/attributes because I can't easily access the 3rd party library). Is there a way to register a "type-converter-something" for Swashbuckle to handle this?我宁愿以某种方式告诉 Swashbuckle IP 地址(因此, IPAddress类型将表示为字符串(传入和传出到我的 API)。我正在寻找有关如何以最佳方式完成此任务的选项可能在给定的限制内(最好不要将新模型引入 map,最好没有注释/属性,因为我无法轻松访问第 3 方库)。有没有办法为 Swashbuckle 注册“类型转换器”来处理这个?

Update: Solved !更新:解决了

This is what I ended up with:这就是我最终的结果:

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services
        .AddResponseCompression()
        .AddMemoryCache()
        .AddControllers()
        // etc...
        // etc...

    // Here's the interesting part:
    services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "Example", Version = "v1" });
            c.MapType<IPAddress>(() => new OpenApiSchema { Type = typeof(string).Name });
            // ...
        });
}

Thank you strickt01谢谢strict01

As you are converting to a non-complex type you should be able to use MapType for this IPAddress example:当您转换为非复杂类型时,您应该能够将MapType用于此 IPAddress 示例:

swagger.MapType<IPAddress>(() => new Schema { Type = "string" });

If you are converting to a complex type then you'd need to use SchemaFilter .如果要转换为复杂类型,则需要使用SchemaFilter

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

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