是否有一种方法显示所有枚举作为他们的字符串值在swagger而不是他们的int值?
我希望能够提交POST动作,并根据它们的字符串值放置枚举,而不必每次都查看enum。
我尝试了DescribeAllEnumsAsStrings,但服务器然后接收字符串而不是enum值,这不是我们要寻找的。
有人解决了吗?
编辑:
public class Letter
{
[Required]
public string Content {get; set;}
[Required]
[EnumDataType(typeof(Priority))]
public Priority Priority {get; set;}
}
public class LettersController : ApiController
{
[HttpPost]
public IHttpActionResult SendLetter(Letter letter)
{
// Validation not passing when using DescribeEnumsAsStrings
if (!ModelState.IsValid)
return BadRequest("Not valid")
..
}
// In the documentation for this request I want to see the string values of the enum before submitting: Low, Medium, High. Instead of 0, 1, 2
[HttpGet]
public IHttpActionResult GetByPriority (Priority priority)
{
}
}
public enum Priority
{
Low,
Medium,
High
}
在这里搜索答案后,我发现了在模式中显示枚举的问题的部分解决方案[SomeEnumString = 0, AnotherEnumString = 1],但与此相关的所有答案都只是部分正确的,正如@OhWelp在其中一个评论中提到的那样。
下面是。net core的完整解决方案(2、3,目前在6上工作):
public class EnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema model, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
model.Enum.Clear();
var names = Enum.GetNames(context.Type).ToList();
names.ForEach(name => model.Enum.Add(new OpenApiString($"{GetEnumIntegerValue(name, context)} = {name}")));
// the missing piece that will make sure that the new schema will not replace the mock value with a wrong value
// this is the default behavior - the first possible enum value as a default "example" value
model.Example = new OpenApiInteger(GetEnumIntegerValue(names.First(), context));
}
}
private int GetEnumIntegerValue(string name, SchemaFilterContext context) => Convert.ToInt32(Enum.Parse(context.Type, name));
}
在启动/程序中:
services.AddSwaggerGen(options =>
{
options.SchemaFilter<EnumSchemaFilter>();
});
编辑:对代码进行了一些重构,如果你想看到原始版本,它与其余的答案更相似,请检查编辑的年表。
这在标准的OpenAPI中是不可能的。枚举只使用它们的字符串值进行描述。
幸运的是,您可以使用客户机生成器使用的一些非标准扩展来实现这一点。
NSwag支持x-enumNames
AutoRest支持x-ms-enum。
Openapi-generator支持x-enum-varnames
其他生成器可能支持这些扩展之一或有自己的扩展。
要为NSwag生成x-enumNames,请创建以下模式过滤器:
public class EnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
var array = new OpenApiArray();
array.AddRange(Enum.GetNames(context.Type).Select(n => new OpenApiString(n)));
// NSwag
schema.Extensions.Add("x-enumNames", array);
// Openapi-generator
schema.Extensions.Add("x-enum-varnames", array);
}
}
}
并将其注册为:
services.AddSwaggerGen(options =>
{
options.SchemaFilter<EnumSchemaFilter>();
});
为了得到Swagger和. net版本6的答案,我遇到了一些问题。x工作。我想继续为枚举使用整数值,但也要显示可能值的列表(以可读的格式)。所以这是我的修改版本(包括一些答案的部分),也许它可以为你们节省一些时间;)
附注:还有一些改进的空间,你也应该检查方法“GetEnumTypeByName”的逻辑是否适合你。在我的情况下,我想主要更新描述仅为项目内部和唯一的枚举。
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Models.Api;
using Swashbuckle.AspNetCore.SwaggerGen;
/// <summary>
/// Add enum value descriptions to Swagger
/// </summary>
public class SwaggerEnumDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
// add enum descriptions to result models
foreach (var property in swaggerDoc.Components.Schemas)
{
var propertyEnums = property.Value.Enum;
if (propertyEnums is { Count: > 0 })
{
property.Value.Description += DescribeEnum(propertyEnums, property.Key);
}
}
if (swaggerDoc.Paths.Count <= 0)
{
return;
}
// add enum descriptions to input parameters
foreach (var pathItem in swaggerDoc.Paths.Values)
{
DescribeEnumParameters(pathItem.Parameters);
var affectedOperations = new List<OperationType> { OperationType.Get, OperationType.Post, OperationType.Put };
foreach (var operation in pathItem.Operations)
{
if (affectedOperations.Contains(operation.Key))
{
DescribeEnumParameters(operation.Value.Parameters);
}
}
}
}
private static void DescribeEnumParameters(IList<OpenApiParameter> parameters)
{
if (parameters == null) return;
foreach (var param in parameters)
{
if (param.Schema.Reference != null)
{
param.Description += DescribeEnum(param.Schema.Reference.Id);
}
}
}
private static Type GetEnumTypeByName(string enumTypeName)
{
if (string.IsNullOrEmpty(enumTypeName))
{
return null;
}
try
{
var projectNamespaceRoot = "MyProject.";
return AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Single(x => x.FullName != null
&& x.FullName.StartsWith(projectNamespaceRoot)
&& x.Name == enumTypeName);
}
catch (InvalidOperationException _)
{
throw new ApiException($"SwaggerDoc: Can not find a unique Enum for specified typeName '{enumTypeName}'. Please provide a more unique enum name.");
}
}
private static string DescribeEnum(IEnumerable<IOpenApiAny> enums, string propertyTypeName)
{
var enumType = GetEnumTypeByName(propertyTypeName);
if (enumType == null)
{
return null;
}
var parsedEnums = new List<OpenApiInteger>();
foreach (var @enum in enums)
{
if (@enum is OpenApiInteger enumInt)
{
parsedEnums.Add(enumInt);
}
}
return string.Join(", ", parsedEnums.Select(x => $"{x} = {Enum.GetName(enumType, x.Value)}"));
}
}
正如其他人已经提到的,你必须在你的Swagger设置中注册这个过滤器:
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
// some configuration
});
options.DocumentFilter<SwaggerEnumDocumentFilter>();
});
我得到了它的工作。net 6 Web API使用以下代码从其他答案在这里:
1 -创建一个DocumentFilter
/// <summary>
/// Add enum value descriptions to Swagger
/// </summary>
public class SwaggerEnumDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
// add enum descriptions to result models
foreach (var property in swaggerDoc.Components.Schemas)
{
var propertyEnums = property.Value.Enum;
if (propertyEnums is { Count: > 0 })
{
property.Value.Description += DescribeEnum(propertyEnums, property.Key);
}
}
if (swaggerDoc.Paths.Count <= 0)
{
return;
}
// add enum descriptions to input parameters
foreach (var pathItem in swaggerDoc.Paths.Values)
{
DescribeEnumParameters(pathItem.Parameters);
var affectedOperations = new List<OperationType> { OperationType.Get, OperationType.Post, OperationType.Put, OperationType.Patch };
foreach (var operation in pathItem.Operations)
{
if (affectedOperations.Contains(operation.Key))
{
DescribeEnumParameters(operation.Value.Parameters);
}
}
}
}
private static void DescribeEnumParameters(IList<OpenApiParameter> parameters)
{
if (parameters == null) return;
foreach (var param in parameters)
{
if (param.Schema.Reference != null)
{
var enumType = GetEnumTypeByName(param.Schema.Reference.Id);
var names = Enum.GetNames(enumType).ToList();
param.Description += string.Join(", ", names.Select(name => $"{Convert.ToInt32(Enum.Parse(enumType, name))} - {name}").ToList());
}
}
}
private static Type GetEnumTypeByName(string enumTypeName)
{
if (string.IsNullOrEmpty(enumTypeName))
{
return null;
}
try
{
return AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Single(x => x.FullName != null
&& x.Name == enumTypeName);
}
catch (InvalidOperationException e)
{
throw new Exception($"SwaggerDoc: Can not find a unique Enum for specified typeName '{enumTypeName}'. Please provide a more unique enum name.");
}
}
private static string DescribeEnum(IEnumerable<IOpenApiAny> enums, string propertyTypeName)
{
var enumType = GetEnumTypeByName(propertyTypeName);
if (enumType == null)
{
return null;
}
var parsedEnums = new List<OpenApiInteger>();
foreach (var @enum in enums)
{
if (@enum is OpenApiInteger enumInt)
{
parsedEnums.Add(enumInt);
}
}
return string.Join(", ", parsedEnums.Select(x => $"{x.Value} - {Enum.GetName(enumType, x.Value)}"));
}
}
2 -将其添加到Program.cs文件中
services.AddSwaggerGen(config =>
{
config.DocumentFilter<SwaggerEnumDocumentFilter>();
})
我在这里找到了不错的解决方法:
@PauloVetor -用ShemaFilter解决了这个问题,就像这样:
public class EnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema model, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
model.Enum.Clear();
Enum.GetNames(context.Type)
.ToList()
.ForEach(n =>
{
model.Enum.Add(new OpenApiString(n));
model.Type = "string";
model.Format = null;
});
}
}
}
在Startup.cs中:
services.AddSwaggerGen(options =>
{
options.SchemaFilter<EnumSchemaFilter>();
}
注意:当枚举以int形式生成json时使用此解决方案(StringEnumConverter不使用或不能使用)。Swagger将显示枚举名称,并将字符串值传递给API,幸运的是ASP。NET Core可以将enum值作为int和字符串处理,其中字符串值必须是区分大小写的enum名称(例如,对于enum优先级。低,ASP。NET Core接受字符串值Low, Low, Low等)。