Net Core Web Api – 如何为 SwaggerDoc 映射Optional<T>

M. *_*och 5 c# swagger swashbuckle.aspnetcore

我正在Optional<T>我的 DTO 中使用 Json Patch(详细信息)。类型 T 存储在类的“Value”字段中Optional<T>。Optional 类允许区分 null 和未提供。

public readonly struct Optional<T>
    {
        public Optional(T? value)
        {
            this.HasValue = true;
            this.Value = value;
        }

        public bool HasValue { get; }
        public T? Value { get; }
        public static implicit operator Optional<T>(T value) => new Optional<T>(value);
        public override string ToString() => this.HasValue ? (this.Value?.ToString() ?? "null") : "unspecified";
    }
Run Code Online (Sandbox Code Playgroud)

我使用的第一个 DTO 仅包含原始类型,例如

public class PatchGroupDTO
    {
        public Optional<Guid?> SalesGroupId { get; init; }       
        public Optional<string?> Name { get; init; }
    }
Run Code Online (Sandbox Code Playgroud)

Swagger UI 显示的示例是错误的,因为它显示属性“value”:

{
  "salesGroupId": {
    "value": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  },
  "name": {
    "value": "string"
  }
}
Run Code Online (Sandbox Code Playgroud)

我们的解决方案是映射 Startup.cs 中的类型。

services.AddSwaggerGen(c =>
  {
   c.SwaggerDoc("v1", new OpenApiInfo { Title = "xxx.API", Version = "v1" });
   c.MapType<Optional<Guid?>>(() => new OpenApiSchema { Type = "string", Format = "uuid" });
   c.MapType<Optional<string?>>(() => new OpenApiSchema { Type = "string" });
   c.MapType<Optional<bool?>>(() => new OpenApiSchema { Type = "boolean" });
   c.MapType<Optional<int?>>(() => new OpenApiSchema { Type = "integer" });
})
Run Code Online (Sandbox Code Playgroud)

该示例随后正确显示为:

{
  "salesGroupId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name":"string"
}
Run Code Online (Sandbox Code Playgroud)

在我最新的 DTO 中,我需要嵌套对象。简化的 DTO 如下所示:

public record UpdateCartDTO
{
   public Optional<AddressDTO?> InvoicingAddress { get; init; }
   public Optional<List<CommentDTO>?> Comments { get; init; }
   public Optional<List<string>?> ReservationCodes { get; init; }
};
Run Code Online (Sandbox Code Playgroud)

该示例将再次错误地显示嵌套值属性:

{
  "invoicingAddress": {
    "value": {
      "type": "string",
      "receipient": "string",
      "deliveryInstructio": "string",
      "street": "string",
      "streetNumber": "string",
      "streetAffix": "string",
      "zip": "string",
      "city": "string",
      "state": "string",
      "isO3": "string"
    }
  },
"comments": {
    "value": [
      {
        "language": "string",
        "comment": "string"
      }
    ]
  },
  "reservationCodes": {
    "value": [
      "string"
    ]   
  }
}
Run Code Online (Sandbox Code Playgroud)

List<string>可以映射为:

c.MapType<Optional<List<string>?>>(
 () => new OpenApiSchema { Type = "array", Items = new OpenApiSchema { Type="string" } });
Run Code Online (Sandbox Code Playgroud)

我在映射嵌套对象时遇到问题。我试过:

c.MapType<Optional<AddressDTO?>>(() => new OpenApiSchema { Type = "object" });
Run Code Online (Sandbox Code Playgroud)

但这只显示空括号{}。根据我理解 api 的方式,我需要使用Item = new OpenApiSchema{...}. 有没有什么解决方案可以只引用AddressDTO?

为Optional中使用的每个T定义一个MapType通常很麻烦。有什么办法可以实现从Optional到T的通用映射吗?就像是:

c.MapType<Optional<T?>>(() => new OpenApiSchema { Object= "T"});
Run Code Online (Sandbox Code Playgroud)

Ant*_*ury 1

关键是使用对现有模式的引用,以避免必须使用OpenApiSchema.

您可以使用下面的代码来映​​射Optional<AddressDTO?>

s.MapType <Optional<AddressDTO?>>(() =>
    new OpenApiSchema
    {
        Type = "object",
        Reference = new OpenApiReference()
        {
            Type = ReferenceType.Schema,
            Id = nameof(AddressDTO)
        },
        Nullable = true
    });
Run Code Online (Sandbox Code Playgroud)

的替代方案Optional<List<AddressDTO>?>

s.MapType<Optional<List<AddressDTO>?>>(() =>
    new OpenApiSchema { 
        Type = "array",
        Items = new OpenApiSchema { 
            Type = "object",
            Reference = new OpenApiReference() { 
                Type = ReferenceType.Schema,
                Id = nameof(AddressDTO)
            }
        }, 
        Nullable = true 
    });
Run Code Online (Sandbox Code Playgroud)

它假设AddressDTO有一个可以引用的现有架构。就我而言, 是对此对象的唯一引用,并且不会自动生成 的Optional<AddressDTO?>架构。AddressDTO

因此,您可以使用以下命令DocumentFilter来生成缺少的架构:

internal class AdditionalSchemasDocumentFilter : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        context.SchemaGenerator.GenerateSchema(typeof(AddressDTO), context.SchemaRepository);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你在 Swagger 配置中这样称呼它:

s.DocumentFilter<AdditionalSchemasDocumentFilter>();
Run Code Online (Sandbox Code Playgroud)