Как изменить обязательные поля для POST и PUT в Swashbuckle?
Я несу ответственность за ведение документации API компании. Наш API написан на ASP.NET. Недавно я перешел на использование Swashbuckle 5.6.0, который работает хорошо.
Проблема, с которой я столкнулся, заключается в следующем:
Например, мы разделяем наши модели данных на данные Post и данные Get WebAccountGetData.cs
а также WebAccountPostData.cs
, Данные Post могут использоваться при создании (POST) и обновлении (PUT).
Большинство, если не все, поля в классах данных Post обнуляются, когда вызывается метод API, хранимый процесс возвращает сообщения об ошибках, описывающие, какие поля отсутствуют / обязательны для заполнения. API не обрабатывает обязательные поля.
Использование пустых полей означает, что Swashbuckle не добавит обязательный флаг в документацию. Но мы хотели бы показать, является ли поле обязательным или нет, основываясь на используемом методе Http (Post / Put).
Ключ API является обязательным параметром, так как он не может иметь значение NULL.
Я знаю, что могу использовать [Required]
атрибут из пространства имен System.ComponentModel.DataAnnotations, но при этом флаг Required будет применяться как к методам POST, так и к методам PUT, которые нам не нужны.
В идеале я хотел бы использовать собственный атрибут, в котором я могу указать, требуется ли поле в методе Post или Put.
public class ApiRequiredAttribute : Attribute
{
public bool RequiredInPost
{
get;
set;
} = false;
public bool RequiredInPut
{
get;
set;
} = false;
}
А затем используйте это так:
[ApiRequired(RequiredInPost = true)]
public int? ApprovalStatusId
{
get;
set;
}
Есть ли способ использовать кастом IDocumentFilter
, IOperationFilter
или же ISchemaFilter
применить изменения (например, переключение требуемого флага) к свойствам схемы поля модели? Или в Swashbuckle невозможно ссылаться на атрибуты в модели?
1 ответ
Мне удалось найти решение!
Я создал IOperationFilter
который создает новые схемы для методов POST и PUT, основываясь на свойствах моего ApiRequired
атрибут (см. оригинальный вопрос).
internal class ApplyRequiredAttributeFilter : IOperationFilter
{
public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription )
{
HttpParameterBinding[] parameterBindings = apiDescription.ActionDescriptor.ActionBinding.ParameterBindings;
foreach ( HttpParameterBinding binding in parameterBindings ) {
PropertyInfo[] properties = binding.Descriptor.ParameterType.GetProperties();
// If the type is not an object and has no properties, ignore it.
if ( properties.Length == 0 ) {
continue;
}
Parameter modelParamater = operation.parameters.Last();
if ( modelParamater == null ) {
continue;
}
string schemaPath = modelParamater.schema?.@ref;
string schemaName = schemaPath?.Split( '/' ).Last();
if ( schemaName == null ) {
continue;
}
Schema oldSchema = schemaRegistry.Definitions[ schemaName ];
// Copy the existing schema.
Schema newSchema = new Schema
{
description = oldSchema.description,
properties = new Dictionary<string, Schema>( oldSchema.properties ),
required = oldSchema.required != null ? new List<string>( oldSchema.required ) : new List<string>(),
@type = oldSchema.type,
vendorExtensions = new Dictionary<string, object>( oldSchema.vendorExtensions )
};
// Find model properties with the custom attribute.
foreach ( PropertyInfo property in properties ) {
ApiRequiredAttribute attribute = property.GetCustomAttribute<ApiRequiredAttribute>();
if ( attribute != null ) {
// If the model property is required in POST/PUT and current HTTP method is POST/PUT
// Add the property to the new schema's required flags.
if ( attribute.RequiredInPut && apiDescription.HttpMethod.Method.Equals( "PUT" ) ||
attribute.RequiredInPost && apiDescription.HttpMethod.Method.Equals( "POST" ) ) {
newSchema.required.Add( property.Name );
string newSchemaName = $"{schemaName}:{apiDescription.HttpMethod.Method}";
if ( !schemaRegistry.Definitions.ContainsKey( newSchemaName ) ) {
schemaRegistry.Definitions.Add( newSchemaName, newSchema );
}
// Change the current model schema reference to the new schema with the addition required flags.
modelParamater.schema.@ref = $"{schemaPath}:{apiDescription.HttpMethod.Method}";
}
}
}
}
}
}
Затем я добавляю фильтр в мой вызов EnableSwagger.
GlobalConfiguration.Configuration
.EnableSwagger("docs/swagger/", c =>
{
// Other initialization code...
c.OperationFilter<ApplyRequiredAttributeFilter>();
});
Атрибуты используются так:
[ApiRequired( RequiredInPost = true, RequiredInPut = true)]
public bool? Active
{
get;
set;
}
[ApiRequired( RequiredInPost = true )]
public string UserName
{
get;
set;
}
Наконец, на документах требуемые флаги выглядят следующим образом. Параметры метода POST находятся слева, а параметры метода PUT - справа: