使用 Swashbuckle 5.x 在通用 T 参数引用属性上指定 nullable = true

Ada*_*ent 9 c# swagger swashbuckle openapi nswagstudio

我最近使用 Swashbuckle 5 和 newtonsoft json nuget 将我的 API 升级到 .net core 3.1 服务器,它生成一个 openapi 3 模式。然后我使用 NSwag 生成一个 C# API。以前我有一个带有 swashbuckle 4 的 .net core 2.2 服务器,生成了一个 swagger 2.0 api 模式。

我有一个适用于所有响应的通用响应类,包含一些关于响应的元数据,如状态代码和消息,以及包含响应内容的通用类型 T 的 Payload 属性。

当响应是错误代码时,我将有效负载属性设置为 null。我正在努力寻找一种定义我的 api 的方法,以便 swashbuckle 和 NSwag 结合产生一个 C# api,它将允许有效载荷属性在反序列化时为空。(swagger 2.0 / swashbuckle 4 没有问题)。

尽我所能,Payload 属性总是获取注释[Newtonsoft.Json.JsonProperty("payload", Required = Newtonsoft.Json.Required.DisallowNull...][System.ComponentModel.DataAnnotations.Required]注释。

据我了解,开放 API 3 现在允许 "$ref" 属性在架构定义中具有 "nullable": true 属性。如果我在创建后手动将其添加到我的定义中,NSwag 会正确删除 CSharp api 中的必需属性,并将 JsonProperty 必需属性设置为“默认”(非必需)而不是“DisallowNull”。
但是,我标记有效负载属性的任何内容都不会导致 nullable: true 出现在我的架构 json 定义中。

我想要的是这个:

"properties": {
          "payload": {
            "nullable": true, 
            "$ref": "#/components/schemas/VisualService.Client.Models.MyResultClass"
          },
Run Code Online (Sandbox Code Playgroud)

我得到的是这样的:

"properties": {
          "payload": {
            "$ref": "#/components/schemas/VisualService.Client.Models.MyResultClass"
          },
Run Code Online (Sandbox Code Playgroud)

也可以在引用的 $ref 对象本身的定义上设置“nullable”=true。我也找不到办法做到这一点。

我尝试了以下补救措施,但没有成功。

  1. 使用 JsonProperty 以不同方式标记 dto 类中的属性:

    [JsonProperty(Required = Required.AllowNull)]
    public T Payload { get; set; }
    
    [AllowNull]
    public T Payload { get; set; }
    
    [MaybeNull]
    public T Payload { get; set; }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 试图告诉 Swashbuckle / Newtonsoft 使用我在这个 github 问题中描述的自定义 Json 解析器- 似乎不服从

    services.AddControllers()
                        .AddNewtonsoftJson(options =>
                        {                        options.SerializerSettings.ContractResolver = MyCustomResolver();

Run Code Online (Sandbox Code Playgroud)
  1. 我创建了自己的自定义属性和过滤器以尝试将该属性设置为可为空

    [NullableGenericProperty]
    public T Payload { get; set; }
    
    Run Code Online (Sandbox Code Playgroud)
   [AttributeUsage(AttributeTargets.Property)]
    public class NullableGenericPropertyAttribute : Attribute
    {

    }

    public class SwaggerNullablePayloadFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            if (schema?.Properties == null || context?.Type == null)
                return;

            var nullableGenericProperties = context.Type.GetProperties()
                .Where(t =>
                    t.GetCustomAttribute<NullableGenericPropertyAttribute>()
                    != null);

            foreach (var excludedProperty in nullableGenericProperties)
            {
                if (schema.Properties.ContainsKey(excludedProperty.Name.ToLowerInvariant()))
                {
                    var prop = schema.Properties[excludedProperty.Name.ToLowerInvariant()];

                    prop.Nullable = true;
                    prop.Required = new HashSet<string>() { "false" };
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我在这个方面取得了小小的成功,因为添加了 prop.Nullable = true; 导致该属性[System.ComponentModel.DataAnnotations.Required]从 c# api 中删除。然而,[Newtonsoft.Json.JsonProperty("payload", Required = Newtonsoft.Json.Required.DisallowNull...]仍然存在,所以它没有多大帮助。我添加prop.Required = new HashSet<string>() { "false" };了一个额外的尝试,但它似乎没有做任何事情。

我可以再次降级到 .net core 2.2 / swashbuckle 4,但 2.2 已失去长期支持,如果可能的话,我想保持在 3.1。我也可以每次都在我生成的 API 客户端上进行查找和替换,但我不想每次重新生成 api 时都必须手动记住要这样做,这在开发周期中可能每天多次。

我有一个 hacky 解决方法 - 这是我拦截 json 响应并"nullable" = true在我的服务器上添加它,通过在响应正文 json 字符串上使用正则表达式匹配,然后将其提供给客户端。虽然它真的很hacky,如果它存在,我想要一种本地方式来做到这一点。

任何和所有的帮助表示赞赏!

Ada*_*ent 19

有一个设置可以实现这一点:

UseAllOfToExtendReferenceSchemas 
Run Code Online (Sandbox Code Playgroud)

它将架构更改为此,nswag 可以使用它来允许 $ref 属性的空值。

  "payload": {
    "required": [
      "false"
    ],
    "allOf": [
      {
        "$ref": "#/components/schemas/MyResultClass"
      }
    ],
    "nullable": true
  },
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

        _ = services.AddSwaggerGen(setup =>
        {
            setup.SwaggerDoc("v1", new OpenApiInfo { Title = AppConst.SwaggerTitle, Version = "v1" });

            setup.UseAllOfToExtendReferenceSchemas();
            ...
Run Code Online (Sandbox Code Playgroud)

  • 这确实有帮助,但我希望: 1. 我冷酷地理解这个“allOf”业务的含义。2、这种不需要字段的行为将是默认行为。3. 我可以使用 [AllowNull(bool)] 等简单属性在各个属性上指定此行为 (8认同)