Reputation: 51
I'm using FluentValidation and I want to receive only my custom errors from fluent validators. That's why all my properties in requests classes are strings. However, I would also like to have better documentation for enum types.
Here is my sample request:
public class AddNewPaymentRequest
{
[EnumDataType(typeof(PaymentStatus))]
public string PaymentStatus { get; set; }
public string Id { get; set; }
}
And sample enum:
public enum PaymentStatus
{
Unknown,
New,
Pending,
Completed
}
And controller:
[HttpPost]
public async Task<ActionResult> PostAsync([FromBody] AddNewPaymentRequest request)
{
...
}
I'd like to have this EnumDataType working as an information for Swagger to display enum description, instead of string destripction. I want Swagger to treat this string as an enum.
Actual result: click
Expected result: click
Is there any possibility to configure Swashbuckle in this way?
Upvotes: 3
Views: 13534
Reputation: 665
These answers all effectively say "you can't do that, here are other options." Well, you can do it and here's how.
Create an attribute named OpenApiEnumAttribute
like so:
/// <summary>
/// Used in conjunction with OpenApiEnumSchemaFilter to apply the enum property
/// to an OpenAPI schema.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class OpenApiEnumAttribute : Attribute
{
public OpenApiEnumAttribute(params string[] enumOptions)
{
EnumOptions = enumOptions;
}
public OpenApiEnumAttribute(Type enoom)
{
if (!enoom.IsEnum)
{
throw new ArgumentException("Type not an enum", nameof(enoom));
}
EnumOptions = Enum.GetNames(enoom);
}
/// <summary>
/// Options for values that the property can have
/// </summary>
public string[] EnumOptions { get; }
}
Then also create a Schema Filter like so:
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
/// <summary>
/// Applies the enum property to an OpenAPI schema. Most useful to constrain
/// strings to be a specific value.
/// </summary>
public class OpenApiEnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.MemberInfo == null)
{
return;
}
var enumAnnotation = context.MemberInfo.GetCustomAttributes(typeof(OpenApiEnumAttribute), false)
.Cast<OpenApiEnumAttribute>()
.FirstOrDefault();
if (enumAnnotation == null) return;
schema.Enum = enumAnnotation.EnumOptions.Select(option => new OpenApiString(option))
.Cast<IOpenApiAny>().ToList();
}
}
Finally in your Startup.cs (or wherever you call AddSwaggerGen
:
services.AddSwaggerGen(config => { config.SchemaFilter<OpenApiEnumSchemaFilter>(); } );
Based on your example you can now use it like so:
public class AddNewPaymentRequest
{
[OpenApiEnum(typeof(PaymentStatus))]
public string PaymentStatus { get; set; }
public string Id { get; set; }
}
Upvotes: 0
Reputation: 7941
Swaggers supports only RequiredAttribute
, ObsoleteAttribute
and MetaData
(for external class).
So there's no way to describe accepted values besides writing something like this:
/// <summary> Allowed values are....
Upvotes: 1
Reputation: 8755
I guess we are talking about .NET Core
If it is ok for you it would be a possibility to define it explicity as enum instead of string
public class AddNewPaymentRequest
{
//[EnumDataType(typeof(PaymentStatus))]
//public string PaymentStatus { get; set; }
public PaymentStatus PaymentStatus { get; set; }
public string Id { get; set; }
}
Then you could define how SwaggerGen treats the enums
services.AddSwaggerGen(c => {c.DescribeAllEnumsAsStrings(); } );
If you run into issues with the serialized/deserialzed enums (by Newtonsoft.Json) when receiving/sending data at/from you api you can take more controller about the conversion (if necessary)
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public enum PaymentStatus
{
[EnumMember(Value = "Unknwon")]
Unknown,
[EnumMember(Value = "New")]
New,
[EnumMember(Value = "Pending")]
Pending,
[EnumMember(Value = "Completed")]
Completed,
[EnumMember(Value = "something_different_with_underline")]
SomethingDifferentWithUnderline
}
Mind that ToString() for these enums may result in different strings (with/without) underlines
Upvotes: 3