Swagger C# 枚举生成 - 底层 int 值与原始枚举不匹配
Swagger C# Enum generation - underlying int values do not match the original enum
我在我的服务器上创建了一个枚举,其中包含手动设置的整数值,而不是从 0 开始的默认增量
public enum UserType
{
Anonymous = 0,
Customer = 10,
Technician = 21,
Manager = 25,
Primary = 30
}
我的服务器 运行 使用 AspNetCore.App 2.2.0。它在 Startup.cs 中配置为使用 swashbuckle aspnetcore 4.0.1 生成一个 swagger json 文件来描述每次服务器启动时的 api。
然后我使用 NSwag Studio for windows v 13.2.3.0 生成一个 C sharp api 客户端和那个 swagger JSON 文件,用于 Xamarin 应用程序。在生成的 c sharp api 客户端中生成的枚举如下所示 - 基础整数值与原始枚举不匹配。
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.1.5.0 (Newtonsoft.Json v11.0.0.0)")]
public enum UserType
{
[System.Runtime.Serialization.EnumMember(Value = @"Anonymous")]
Anonymous = 0,
[System.Runtime.Serialization.EnumMember(Value = @"Customer")]
Customer = 1,
[System.Runtime.Serialization.EnumMember(Value = @"Technician")]
Technician = 2,
[System.Runtime.Serialization.EnumMember(Value = @"Manager")]
Manager = 3,
[System.Runtime.Serialization.EnumMember(Value = @"Primary")]
Primary = 4,
}
这对我的客户端造成了问题,因为在某些情况下我需要知道整数值。我正在寻找一种解决方案,可以避免每次我想知道客户端的整数值时都编写转换器。
选项 1:
NSwag Studio 或 .net 配置中是否缺少一个选项(下面是我的 Startup.Cs 配置以供参考),我可以在其中强制生成的枚举获得与原始枚举相同的整数值?
选项 2:
或者,如果没有,我的客户端和我的服务器都可以通过共享的 class 库访问相同的原始枚举。有没有办法让生成的 api 客户端使用 apiclient.cs 中的实际原始枚举而不是生成自己的枚举?
参考:
Startup.Cs 中我的 swagger 生成代码的枚举部分如下所示
services.AddJsonOptions(options =>
{
options.
SerializerSettings.Converters.Add(new StringEnumConverter());
....
services.AddSwaggerGen(setup =>
{
setup.SwaggerDoc("v1", new Info { Title = AppConst.SwaggerTitle, Version = "v1" });
setup.UseReferencedDefinitionsForEnums();
... other stuff...
}
更新
dawood 在上面发布了一个工作解决方案,它完全符合我的要求。
原始答案
目前似乎没有办法做到这一点。正如@sellotape 在他的评论中提到的那样,这甚至可能不是一个好主意。由于我控制着服务器,而且这是一个相对较新的项目,因此我将我的枚举重构为正常的“从零开始顺序”样式。
我确实认为它对某些用例很有用 - 例如支持无法轻易重构的遗留枚举,或者能够对中间有间隙的枚举进行编号,例如10、20、30。这将允许稍后插入 11,12 等,同时保留将某种“顺序”编码到枚举的能力,并且不会随着项目的增长而破坏该顺序。
不过目前似乎不可能,所以我们就这样吧。
这是我正在使用的两个 Enum Helper。一个由 NSwag (x-enumNames
) 使用,另一个由 Azure AutoRest (x-ms-enums
)
使用
终于找到 EnumDocumentFilter
()
的参考
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace SwaggerDocsHelpers
{
/// <summary>
/// Add enum value descriptions to Swagger
///
/// </summary>
public class EnumDocumentFilter : IDocumentFilter
{
/// <inheritdoc />
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
// add enum descriptions to result models
foreach (var schemaDictionaryItem in swaggerDoc.Definitions)
{
var schema = schemaDictionaryItem.Value;
foreach (var propertyDictionaryItem in schema.Properties)
{
var property = propertyDictionaryItem.Value;
var propertyEnums = property.Enum;
if (propertyEnums != null && propertyEnums.Count > 0)
{
property.Description += DescribeEnum(propertyEnums);
}
}
}
if (swaggerDoc.Paths.Count <= 0) return;
// add enum descriptions to input parameters
foreach (var pathItem in swaggerDoc.Paths.Values)
{
DescribeEnumParameters(pathItem.Parameters);
// head, patch, options, delete left out
var possibleParameterisedOperations = new List<Operation> { pathItem.Get, pathItem.Post, pathItem.Put };
possibleParameterisedOperations.FindAll(x => x != null)
.ForEach(x => DescribeEnumParameters(x.Parameters));
}
}
private static void DescribeEnumParameters(IList<IParameter> parameters)
{
if (parameters == null) return;
foreach (var param in parameters)
{
if (param is NonBodyParameter nbParam && nbParam.Enum?.Any() == true)
{
param.Description += DescribeEnum(nbParam.Enum);
}
else if (param.Extensions.ContainsKey("enum") && param.Extensions["enum"] is IList<object> paramEnums &&
paramEnums.Count > 0)
{
param.Description += DescribeEnum(paramEnums);
}
}
}
private static string DescribeEnum(IEnumerable<object> enums)
{
var enumDescriptions = new List<string>();
Type type = null;
foreach (var enumOption in enums)
{
if (type == null) type = enumOption.GetType();
enumDescriptions.Add($"{Convert.ChangeType(enumOption, type.GetEnumUnderlyingType())} = {Enum.GetName(type, enumOption)}");
}
return $"{Environment.NewLine}{string.Join(Environment.NewLine, enumDescriptions)}";
}
}
public class EnumFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
if (model == null)
throw new ArgumentNullException("model");
if (context == null)
throw new ArgumentNullException("context");
if (context.SystemType.IsEnum)
{
var enumUnderlyingType = context.SystemType.GetEnumUnderlyingType();
model.Extensions.Add("x-ms-enum", new
{
name = context.SystemType.Name,
modelAsString = false,
values = context.SystemType
.GetEnumValues()
.Cast<object>()
.Distinct()
.Select(value =>
{
//var t = context.SystemType;
//var convereted = Convert.ChangeType(value, enumUnderlyingType);
//return new { value = convereted, name = value.ToString() };
return new { value = value, name = value.ToString() };
})
.ToArray()
});
}
}
}
/// <summary>
/// Adds extra schema details for an enum in the swagger.json i.e. x-enumNames (used by NSwag to generate Enums for C# client)
/// https://github.com/RicoSuter/NSwag/issues/1234
/// </summary>
public class NSwagEnumExtensionSchemaFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
if (model == null)
throw new ArgumentNullException("model");
if (context == null)
throw new ArgumentNullException("context");
if (context.SystemType.IsEnum)
{
var names = Enum.GetNames(context.SystemType);
model.Extensions.Add("x-enumNames", names);
}
}
}
}
然后在你的 startup.cs 中配置它们
services.AddSwaggerGen(c =>
{
... the rest of your configuration
// REMOVE THIS to use Integers for Enums
// c.DescribeAllEnumsAsStrings();
// add enum generators based on whichever code generators you decide
c.SchemaFilter<NSwagEnumExtensionSchemaFilter>();
c.SchemaFilter<EnumFilter>();
});
这应该会在 Swagger.json 文件中生成您的枚举
sensorType: {
format: "int32",
enum: [
0,
1,
2,
3
],
type: "integer",
x-enumNames: [
"NotSpecified",
"Temperature",
"Fuel",
"Axle"
],
x-ms-enum: {
name: "SensorTypesEnum",
modelAsString: false,
values: [{
value: 0,
name: "NotSpecified"
},
{
value: 1,
name: "Temperature"
},
{
value: 2,
name: "Fuel"
},
{
value: 3,
name: "Axle"
}
]
}
},
虽然这个解决方案有一个问题,(我没有时间研究)
枚举名称是用我在 NSwag 中的 DTO 名称生成的吗?如果您确实找到了解决方案,请告诉我:-)
例如,以下枚举是使用 NSwag 生成的:
@Dawood 上面的回答是杰作
它仅适用于 Swashbuckle
的旧版本(我不确定是哪个版本)
但是如果你有 Swashbuckle
6.x 代码将 NOT compile.
这是相同的解决方案,但适用于 版本 6.x
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
/// <summary>
/// Add enum value descriptions to Swagger
///
/// </summary>
public class EnumDocumentFilter : IDocumentFilter
{
/// <inheritdoc />
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
foreach (KeyValuePair<string, OpenApiPathItem> schemaDictionaryItem in swaggerDoc.Paths)
{
OpenApiPathItem schema = schemaDictionaryItem.Value;
foreach (OpenApiParameter property in schema.Parameters)
{
IList<IOpenApiAny> propertyEnums = property.Schema.Enum;
if (propertyEnums.Count > 0)
property.Description += DescribeEnum(propertyEnums);
}
}
if (swaggerDoc.Paths.Count == 0)
return;
// add enum descriptions to input parameters
foreach (OpenApiPathItem pathItem in swaggerDoc.Paths.Values)
{
DescribeEnumParameters(pathItem.Parameters);
foreach (KeyValuePair<OperationType, OpenApiOperation> operation in pathItem.Operations)
DescribeEnumParameters(operation.Value.Parameters);
}
}
private static void DescribeEnumParameters(IList<OpenApiParameter> parameters)
{
if (parameters == null)
return;
foreach (OpenApiParameter param in parameters)
{
if (param.Schema.Enum?.Any() == true)
{
param.Description += DescribeEnum(param.Schema.Enum);
}
else if (param.Extensions.ContainsKey("enum") &&
param.Extensions["enum"] is IList<object> paramEnums &&
paramEnums.Count > 0)
{
param.Description += DescribeEnum(paramEnums);
}
}
}
private static string DescribeEnum(IEnumerable<object> enums)
{
List<string> enumDescriptions = new();
Type? type = null;
foreach (object enumOption in enums)
{
if (type == null)
type = enumOption.GetType();
enumDescriptions.Add($"{Convert.ChangeType(enumOption, type.GetEnumUnderlyingType())} = {Enum.GetName(type, enumOption)}");
}
return Environment.NewLine + string.Join(Environment.NewLine, enumDescriptions);
}
}
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
//
public class EnumFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema is null)
throw new ArgumentNullException(nameof(schema));
if (context is null)
throw new ArgumentNullException(nameof(context));
if (context.Type.IsEnum is false)
return;
schema.Extensions.Add("x-ms-enum", new EnumFilterOpenApiExtension(context));
}
}
using System.Text.Json;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;
using Swashbuckle.AspNetCore.SwaggerGen;
public class EnumFilterOpenApiExtension : IOpenApiExtension
{
private readonly SchemaFilterContext _context;
public EnumFilterOpenApiExtension(SchemaFilterContext context)
{
_context = context;
}
public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
JsonSerializerOptions options = new() { WriteIndented = true };
var obj = new {
name = _context.Type.Name,
modelAsString = false,
values = _context.Type
.GetEnumValues()
.Cast<object>()
.Distinct()
.Select(value => new { value, name = value.ToString() })
.ToArray()
};
writer.WriteRaw(JsonSerializer.Serialize(obj, options));
}
}
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
/// <summary>
/// Adds extra schema details for an enum in the swagger.json i.e. x-enumNames (used by NSwag to generate Enums for C# client)
/// https://github.com/RicoSuter/NSwag/issues/1234
/// </summary>
public class NSwagEnumExtensionSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema is null)
throw new ArgumentNullException(nameof(schema));
if (context is null)
throw new ArgumentNullException(nameof(context));
if (context.Type.IsEnum)
schema.Extensions.Add("x-enumNames", new NSwagEnumOpenApiExtension(context));
}
}
using System.Text.Json;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;
using Swashbuckle.AspNetCore.SwaggerGen;
public class NSwagEnumOpenApiExtension : IOpenApiExtension
{
private readonly SchemaFilterContext _context;
public NSwagEnumOpenApiExtension(SchemaFilterContext context)
{
_context = context;
}
public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
string[] enums = Enum.GetNames(_context.Type);
JsonSerializerOptions options = new() { WriteIndented = true };
string value = JsonSerializer.Serialize(enums, options);
writer.WriteRaw(value);
}
}
最后一件事,过滤器的注册
services.AddSwaggerGen(c =>
{
... the rest of your configuration
// REMOVE THIS to use Integers for Enums
// c.DescribeAllEnumsAsStrings();
// add enum generators based on whichever code generators you decide
c.SchemaFilter<NSwagEnumExtensionSchemaFilter>();
c.SchemaFilter<EnumFilter>();
});
注释
- 我正在使用 C# 10 功能(隐式使用和其他)
如果您不使用 C# 10,那么您必须添加一些 USING 语句并恢复到旧的命名空间样式,并为 C# 做一些其他小的修改
- 我测试了这段代码,结果和原来的答案一样
我在我的服务器上创建了一个枚举,其中包含手动设置的整数值,而不是从 0 开始的默认增量
public enum UserType
{
Anonymous = 0,
Customer = 10,
Technician = 21,
Manager = 25,
Primary = 30
}
我的服务器 运行 使用 AspNetCore.App 2.2.0。它在 Startup.cs 中配置为使用 swashbuckle aspnetcore 4.0.1 生成一个 swagger json 文件来描述每次服务器启动时的 api。
然后我使用 NSwag Studio for windows v 13.2.3.0 生成一个 C sharp api 客户端和那个 swagger JSON 文件,用于 Xamarin 应用程序。在生成的 c sharp api 客户端中生成的枚举如下所示 - 基础整数值与原始枚举不匹配。
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.1.5.0 (Newtonsoft.Json v11.0.0.0)")]
public enum UserType
{
[System.Runtime.Serialization.EnumMember(Value = @"Anonymous")]
Anonymous = 0,
[System.Runtime.Serialization.EnumMember(Value = @"Customer")]
Customer = 1,
[System.Runtime.Serialization.EnumMember(Value = @"Technician")]
Technician = 2,
[System.Runtime.Serialization.EnumMember(Value = @"Manager")]
Manager = 3,
[System.Runtime.Serialization.EnumMember(Value = @"Primary")]
Primary = 4,
}
这对我的客户端造成了问题,因为在某些情况下我需要知道整数值。我正在寻找一种解决方案,可以避免每次我想知道客户端的整数值时都编写转换器。
选项 1: NSwag Studio 或 .net 配置中是否缺少一个选项(下面是我的 Startup.Cs 配置以供参考),我可以在其中强制生成的枚举获得与原始枚举相同的整数值?
选项 2: 或者,如果没有,我的客户端和我的服务器都可以通过共享的 class 库访问相同的原始枚举。有没有办法让生成的 api 客户端使用 apiclient.cs 中的实际原始枚举而不是生成自己的枚举?
参考:
Startup.Cs 中我的 swagger 生成代码的枚举部分如下所示
services.AddJsonOptions(options =>
{
options.
SerializerSettings.Converters.Add(new StringEnumConverter());
....
services.AddSwaggerGen(setup =>
{
setup.SwaggerDoc("v1", new Info { Title = AppConst.SwaggerTitle, Version = "v1" });
setup.UseReferencedDefinitionsForEnums();
... other stuff...
}
更新
dawood 在上面发布了一个工作解决方案,它完全符合我的要求。
原始答案
目前似乎没有办法做到这一点。正如@sellotape 在他的评论中提到的那样,这甚至可能不是一个好主意。由于我控制着服务器,而且这是一个相对较新的项目,因此我将我的枚举重构为正常的“从零开始顺序”样式。
我确实认为它对某些用例很有用 - 例如支持无法轻易重构的遗留枚举,或者能够对中间有间隙的枚举进行编号,例如10、20、30。这将允许稍后插入 11,12 等,同时保留将某种“顺序”编码到枚举的能力,并且不会随着项目的增长而破坏该顺序。
不过目前似乎不可能,所以我们就这样吧。
这是我正在使用的两个 Enum Helper。一个由 NSwag (x-enumNames
) 使用,另一个由 Azure AutoRest (x-ms-enums
)
终于找到 EnumDocumentFilter
()
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace SwaggerDocsHelpers
{
/// <summary>
/// Add enum value descriptions to Swagger
///
/// </summary>
public class EnumDocumentFilter : IDocumentFilter
{
/// <inheritdoc />
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
// add enum descriptions to result models
foreach (var schemaDictionaryItem in swaggerDoc.Definitions)
{
var schema = schemaDictionaryItem.Value;
foreach (var propertyDictionaryItem in schema.Properties)
{
var property = propertyDictionaryItem.Value;
var propertyEnums = property.Enum;
if (propertyEnums != null && propertyEnums.Count > 0)
{
property.Description += DescribeEnum(propertyEnums);
}
}
}
if (swaggerDoc.Paths.Count <= 0) return;
// add enum descriptions to input parameters
foreach (var pathItem in swaggerDoc.Paths.Values)
{
DescribeEnumParameters(pathItem.Parameters);
// head, patch, options, delete left out
var possibleParameterisedOperations = new List<Operation> { pathItem.Get, pathItem.Post, pathItem.Put };
possibleParameterisedOperations.FindAll(x => x != null)
.ForEach(x => DescribeEnumParameters(x.Parameters));
}
}
private static void DescribeEnumParameters(IList<IParameter> parameters)
{
if (parameters == null) return;
foreach (var param in parameters)
{
if (param is NonBodyParameter nbParam && nbParam.Enum?.Any() == true)
{
param.Description += DescribeEnum(nbParam.Enum);
}
else if (param.Extensions.ContainsKey("enum") && param.Extensions["enum"] is IList<object> paramEnums &&
paramEnums.Count > 0)
{
param.Description += DescribeEnum(paramEnums);
}
}
}
private static string DescribeEnum(IEnumerable<object> enums)
{
var enumDescriptions = new List<string>();
Type type = null;
foreach (var enumOption in enums)
{
if (type == null) type = enumOption.GetType();
enumDescriptions.Add($"{Convert.ChangeType(enumOption, type.GetEnumUnderlyingType())} = {Enum.GetName(type, enumOption)}");
}
return $"{Environment.NewLine}{string.Join(Environment.NewLine, enumDescriptions)}";
}
}
public class EnumFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
if (model == null)
throw new ArgumentNullException("model");
if (context == null)
throw new ArgumentNullException("context");
if (context.SystemType.IsEnum)
{
var enumUnderlyingType = context.SystemType.GetEnumUnderlyingType();
model.Extensions.Add("x-ms-enum", new
{
name = context.SystemType.Name,
modelAsString = false,
values = context.SystemType
.GetEnumValues()
.Cast<object>()
.Distinct()
.Select(value =>
{
//var t = context.SystemType;
//var convereted = Convert.ChangeType(value, enumUnderlyingType);
//return new { value = convereted, name = value.ToString() };
return new { value = value, name = value.ToString() };
})
.ToArray()
});
}
}
}
/// <summary>
/// Adds extra schema details for an enum in the swagger.json i.e. x-enumNames (used by NSwag to generate Enums for C# client)
/// https://github.com/RicoSuter/NSwag/issues/1234
/// </summary>
public class NSwagEnumExtensionSchemaFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
if (model == null)
throw new ArgumentNullException("model");
if (context == null)
throw new ArgumentNullException("context");
if (context.SystemType.IsEnum)
{
var names = Enum.GetNames(context.SystemType);
model.Extensions.Add("x-enumNames", names);
}
}
}
}
然后在你的 startup.cs 中配置它们
services.AddSwaggerGen(c =>
{
... the rest of your configuration
// REMOVE THIS to use Integers for Enums
// c.DescribeAllEnumsAsStrings();
// add enum generators based on whichever code generators you decide
c.SchemaFilter<NSwagEnumExtensionSchemaFilter>();
c.SchemaFilter<EnumFilter>();
});
这应该会在 Swagger.json 文件中生成您的枚举
sensorType: {
format: "int32",
enum: [
0,
1,
2,
3
],
type: "integer",
x-enumNames: [
"NotSpecified",
"Temperature",
"Fuel",
"Axle"
],
x-ms-enum: {
name: "SensorTypesEnum",
modelAsString: false,
values: [{
value: 0,
name: "NotSpecified"
},
{
value: 1,
name: "Temperature"
},
{
value: 2,
name: "Fuel"
},
{
value: 3,
name: "Axle"
}
]
}
},
虽然这个解决方案有一个问题,(我没有时间研究) 枚举名称是用我在 NSwag 中的 DTO 名称生成的吗?如果您确实找到了解决方案,请告诉我:-)
例如,以下枚举是使用 NSwag 生成的:
@Dawood 上面的回答是杰作
它仅适用于 Swashbuckle
的旧版本(我不确定是哪个版本)
但是如果你有 Swashbuckle
6.x 代码将 NOT compile.
这是相同的解决方案,但适用于 版本 6.x
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
/// <summary>
/// Add enum value descriptions to Swagger
///
/// </summary>
public class EnumDocumentFilter : IDocumentFilter
{
/// <inheritdoc />
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
foreach (KeyValuePair<string, OpenApiPathItem> schemaDictionaryItem in swaggerDoc.Paths)
{
OpenApiPathItem schema = schemaDictionaryItem.Value;
foreach (OpenApiParameter property in schema.Parameters)
{
IList<IOpenApiAny> propertyEnums = property.Schema.Enum;
if (propertyEnums.Count > 0)
property.Description += DescribeEnum(propertyEnums);
}
}
if (swaggerDoc.Paths.Count == 0)
return;
// add enum descriptions to input parameters
foreach (OpenApiPathItem pathItem in swaggerDoc.Paths.Values)
{
DescribeEnumParameters(pathItem.Parameters);
foreach (KeyValuePair<OperationType, OpenApiOperation> operation in pathItem.Operations)
DescribeEnumParameters(operation.Value.Parameters);
}
}
private static void DescribeEnumParameters(IList<OpenApiParameter> parameters)
{
if (parameters == null)
return;
foreach (OpenApiParameter param in parameters)
{
if (param.Schema.Enum?.Any() == true)
{
param.Description += DescribeEnum(param.Schema.Enum);
}
else if (param.Extensions.ContainsKey("enum") &&
param.Extensions["enum"] is IList<object> paramEnums &&
paramEnums.Count > 0)
{
param.Description += DescribeEnum(paramEnums);
}
}
}
private static string DescribeEnum(IEnumerable<object> enums)
{
List<string> enumDescriptions = new();
Type? type = null;
foreach (object enumOption in enums)
{
if (type == null)
type = enumOption.GetType();
enumDescriptions.Add($"{Convert.ChangeType(enumOption, type.GetEnumUnderlyingType())} = {Enum.GetName(type, enumOption)}");
}
return Environment.NewLine + string.Join(Environment.NewLine, enumDescriptions);
}
}
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
//
public class EnumFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema is null)
throw new ArgumentNullException(nameof(schema));
if (context is null)
throw new ArgumentNullException(nameof(context));
if (context.Type.IsEnum is false)
return;
schema.Extensions.Add("x-ms-enum", new EnumFilterOpenApiExtension(context));
}
}
using System.Text.Json;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;
using Swashbuckle.AspNetCore.SwaggerGen;
public class EnumFilterOpenApiExtension : IOpenApiExtension
{
private readonly SchemaFilterContext _context;
public EnumFilterOpenApiExtension(SchemaFilterContext context)
{
_context = context;
}
public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
JsonSerializerOptions options = new() { WriteIndented = true };
var obj = new {
name = _context.Type.Name,
modelAsString = false,
values = _context.Type
.GetEnumValues()
.Cast<object>()
.Distinct()
.Select(value => new { value, name = value.ToString() })
.ToArray()
};
writer.WriteRaw(JsonSerializer.Serialize(obj, options));
}
}
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
/// <summary>
/// Adds extra schema details for an enum in the swagger.json i.e. x-enumNames (used by NSwag to generate Enums for C# client)
/// https://github.com/RicoSuter/NSwag/issues/1234
/// </summary>
public class NSwagEnumExtensionSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema is null)
throw new ArgumentNullException(nameof(schema));
if (context is null)
throw new ArgumentNullException(nameof(context));
if (context.Type.IsEnum)
schema.Extensions.Add("x-enumNames", new NSwagEnumOpenApiExtension(context));
}
}
using System.Text.Json;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;
using Swashbuckle.AspNetCore.SwaggerGen;
public class NSwagEnumOpenApiExtension : IOpenApiExtension
{
private readonly SchemaFilterContext _context;
public NSwagEnumOpenApiExtension(SchemaFilterContext context)
{
_context = context;
}
public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
{
string[] enums = Enum.GetNames(_context.Type);
JsonSerializerOptions options = new() { WriteIndented = true };
string value = JsonSerializer.Serialize(enums, options);
writer.WriteRaw(value);
}
}
最后一件事,过滤器的注册
services.AddSwaggerGen(c =>
{
... the rest of your configuration
// REMOVE THIS to use Integers for Enums
// c.DescribeAllEnumsAsStrings();
// add enum generators based on whichever code generators you decide
c.SchemaFilter<NSwagEnumExtensionSchemaFilter>();
c.SchemaFilter<EnumFilter>();
});
注释
- 我正在使用 C# 10 功能(隐式使用和其他) 如果您不使用 C# 10,那么您必须添加一些 USING 语句并恢复到旧的命名空间样式,并为 C# 做一些其他小的修改
- 我测试了这段代码,结果和原来的答案一样