我试图从Swagger模型的部分中提取DisplayAttribute和DescriptionAttribute。例如,我可能有一个Body参数,它具有属性属性,我也希望在swagger.json中生成并在SwaggerUI中可见。
到目前为止,我认为应该使用的方法是使用带有swashbuckle的自定义过滤器。我使用IParameterFilter获得了一个概念验证,它显示了description属性,不确定另一个过滤器是否更好。
的问题:
为某些类型(如list)查找schemaRegistry的键失败。
需要获取与swagger相同的生成参数的键。
可能需要递归来遍历包含复杂对象的子属性。
public class SwaggerParameterFilter : IParameterFilter
{
private SchemaRegistrySettings _settings;
private SchemaIdManager _schemaIdManager;
public SwaggerParameterFilter(SchemaRegistrySettings settings = null)
{
this._settings = settings ?? new SchemaRegistrySettings();
this._schemaIdManager = new SchemaIdManager(this._settings.SchemaIdSelector);
}
public void Apply(IParameter parameter, ParameterFilterContext context)
{
try
{
if (context.ApiParameterDescription?.ModelMetadata?.Properties == null) return;
if (parameter is BodyParameter bodyParameter)
{
string idFor = _schemaIdManager.IdFor(context.ApiParameterDescription.Type);
var schemaRegistry = (SchemaRegistry)context.SchemaRegistry;
//not perfect, crashes with some cases
var schema = schemaRegistry.Definitions[idFor];
//bodyParameter.Schema, this doesn't seem right, no properties
foreach (var modelMetadata in context.ApiParameterDescription.ModelMetadata.Properties)
{
if (modelMetadata is DefaultModelMetadata defaultModelMetadata)
{
//not sure right now how to get the right key for the schema.Properties...
var name = defaultModelMetadata.Name;
name = Char.ToLowerInvariant(name[0]) + name.Substring(1);
if (schema.Properties.ContainsKey(name))
{
var subSchema = schema.Properties[name];
var attributes = defaultModelMetadata.Attributes.Attributes.Select(x => (Attribute)x);
var descriptionAttribute = (DescriptionAttribute)attributes.FirstOrDefault(x => x is DescriptionAttribute);
if (descriptionAttribute != null)
subSchema.Description = descriptionAttribute.Description;
}
}
}
}
}
catch (Exception e)
{
//eat because above is broken
}
}
}
编辑添加循环。
public class SwaggerParameterFilter : IParameterFilter
{
private SchemaRegistrySettings _settings;
private SchemaIdManager _schemaIdManager;
public SwaggerParameterFilter(SchemaRegistrySettings settings = null)
{
this._settings = settings ?? new SchemaRegistrySettings();
this._schemaIdManager = new SchemaIdManager(this._settings.SchemaIdSelector);
}
public void Apply(IParameter parameter, ParameterFilterContext context)
{
try
{
if (context.ApiParameterDescription?.ModelMetadata?.Properties == null) return;
//Only BodyParameters are complex and stored in the schema
if (parameter is BodyParameter bodyParameter)
{
var idFor = _schemaIdManager.IdFor(context.ApiParameterDescription.Type);
//not perfect, crashes with some cases
var schema = context.SchemaRegistry.Definitions[idFor];
UpdateSchema(schema, (SchemaRegistry) context.SchemaRegistry, context.ApiParameterDescription.ModelMetadata);
}
}
catch (Exception e)
{
//eat because above is broken
}
}
private void UpdateSchema(Schema schema, SchemaRegistry schemaRegistry, ModelMetadata modelMetadata)
{
if (schema.Ref != null)
{
var schemaReference = schema.Ref.Replace("#/definitions/", "");
UpdateSchema(schemaRegistry.Definitions[schemaReference], schemaRegistry, modelMetadata);
return;
}
if (schema.Properties == null) return;
foreach (var properties in modelMetadata.Properties)
{
if (properties is DefaultModelMetadata defaultModelMetadata)
{
//not sure right now how to get the right key for the schema.Properties...
var name = defaultModelMetadata.Name;
name = Char.ToLowerInvariant(name[0]) + name.Substring(1);
if (schema.Properties.ContainsKey(name) == false) return;
var subSchema = schema.Properties[name];
var attributes = defaultModelMetadata.Attributes.Attributes.Select(x => (Attribute) x).ToList();
var descriptionAttribute =
(DescriptionAttribute) attributes.FirstOrDefault(x => x is DescriptionAttribute);
if (descriptionAttribute != null)
subSchema.Description = descriptionAttribute.Description;
var displayAttribute = (DisplayAttribute) attributes.FirstOrDefault(x => x is DisplayAttribute);
if (displayAttribute != null)
subSchema.Title = displayAttribute.Name;
if (modelMetadata.ModelType.IsPrimitive) return;
UpdateSchema(subSchema, schemaRegistry, defaultModelMetadata);
}
}
}
}
操作过滤器
public class SwaggerOperationFilter : IOperationFilter
{
private SchemaRegistrySettings _settings;
private SchemaIdManager _schemaIdManager;
private IModelMetadataProvider _metadataProvider;
public SwaggerOperationFilter(IModelMetadataProvider metadataProvider, SchemaRegistrySettings settings = null)
{
this._metadataProvider = metadataProvider;
this._settings = settings ?? new SchemaRegistrySettings();
this._schemaIdManager = new SchemaIdManager(this._settings.SchemaIdSelector);
}
public void Apply(Operation operation, OperationFilterContext context)
{
try
{
foreach (var paramDescription in context.ApiDescription.ParameterDescriptions)
{
if (paramDescription?.ModelMetadata?.Properties == null)
{
continue;
}
if (paramDescription.ModelMetadata.ModelType.IsPrimitive)
{
continue;
}
if (paramDescription.ModelMetadata.ModelType == typeof(string))
{
continue;
}
var idFor = _schemaIdManager.IdFor(paramDescription.Type);
var schema = context.SchemaRegistry.Definitions[idFor];
UpdateSchema(schema, (SchemaRegistry)context.SchemaRegistry, paramDescription.ModelMetadata);
}
}
catch (Exception e)
{
//eat because above is broken
}
}
private void UpdateSchema(Schema schema, SchemaRegistry schemaRegistry, ModelMetadata modelMetadata)
{
if (schema.Ref != null)
{
var schemaReference = schema.Ref.Replace("#/definitions/", "");
UpdateSchema(schemaRegistry.Definitions[schemaReference], schemaRegistry, modelMetadata);
return;
}
if (schema.Type == "array")
{
if (schema.Items.Ref != null)
{
var schemaReference = schema.Items.Ref.Replace("#/definitions/", "");
var modelTypeGenericTypeArgument = modelMetadata.ModelType.GenericTypeArguments[0];
modelMetadata = _metadataProvider.GetMetadataForType(modelTypeGenericTypeArgument);
UpdateSchema(schemaRegistry.Definitions[schemaReference], schemaRegistry, modelMetadata);
}
return;
}
if (schema.Properties == null) return;
foreach (var properties in modelMetadata.Properties)
{
if (properties is DefaultModelMetadata defaultModelMetadata)
{
//not sure right now how to get the right key for the schema.Properties...
var name = defaultModelMetadata.Name;
name = Char.ToLowerInvariant(name[0]) + name.Substring(1);
if (schema.Properties.ContainsKey(name) == false) return;
var subSchema = schema.Properties[name];
var attributes = defaultModelMetadata.Attributes.Attributes.Select(x => (Attribute)x).ToList();
var descriptionAttribute =
(DescriptionAttribute)attributes.FirstOrDefault(x => x is DescriptionAttribute);
if (descriptionAttribute != null)
subSchema.Description = descriptionAttribute.Description;
var displayAttribute = (DisplayAttribute)attributes.FirstOrDefault(x => x is DisplayAttribute);
if (displayAttribute != null)
subSchema.Title = displayAttribute.Name;
if (defaultModelMetadata.ModelType.IsPrimitive) return;
UpdateSchema(subSchema, schemaRegistry, defaultModelMetadata);
}
}
}
}
答案 0 :(得分:0)
因此,经过一些故障排除后,这似乎对我有用,但可能需要修改其他情况。
public class SwashbuckleAttributeReaderDocumentFilter : IDocumentFilter
{
private readonly SchemaIdManager _schemaIdManager;
private readonly IModelMetadataProvider _metadataProvider;
private List<string> _updatedSchemeList;
public SwashbuckleAttributeReaderDocumentFilter(IModelMetadataProvider metadataProvider, SchemaRegistrySettings settings = null)
{
_metadataProvider = metadataProvider;
var registrySettings = settings ?? new SchemaRegistrySettings();
_schemaIdManager = new SchemaIdManager(registrySettings.SchemaIdSelector);
}
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
_updatedSchemeList = new List<string>();
foreach (var apiDescription in context.ApiDescriptions)
{
foreach (var responseTypes in apiDescription.SupportedResponseTypes)
{
ProcessModelMetadata(context, responseTypes.ModelMetadata);
}
foreach (var paramDescription in apiDescription.ParameterDescriptions)
{
ProcessModelMetadata(context, paramDescription.ModelMetadata);
}
}
}
private void ProcessModelMetadata(DocumentFilterContext context, ModelMetadata currentModelMetadata)
{
if (currentModelMetadata?.Properties == null)
{
return;
}
if (currentModelMetadata.ModelType.IsValueType)
{
return;
}
if (currentModelMetadata.ModelType == typeof(string))
{
return;
}
if (currentModelMetadata.ModelType.IsGenericType)
{
foreach (var modelType in currentModelMetadata.ModelType.GenericTypeArguments)
{
var modelMetadata = _metadataProvider.GetMetadataForType(modelType);
UpdateSchema(context.SchemaRegistry, modelMetadata);
}
}
else if (currentModelMetadata.IsCollectionType)
{
var modelType = currentModelMetadata.ModelType.GetElementType();
var modelMetadata = _metadataProvider.GetMetadataForType(modelType);
UpdateSchema(context.SchemaRegistry, modelMetadata);
}
else
{
UpdateSchema(context.SchemaRegistry, currentModelMetadata);
}
}
public static void SetSchema(Schema schema, ModelMetadata modelMetadata)
{
if (!(modelMetadata is DefaultModelMetadata metadata)) return;
var attributes = GetAtributes(metadata);
SetDescription(attributes, schema);
SetTitle(attributes, schema);
}
private static List<Attribute> GetAtributes(DefaultModelMetadata modelMetadata)
{
return modelMetadata.Attributes.Attributes.Select(x => (Attribute)x).ToList();
}
private static void SetTitle(List<Attribute> attributes, Schema schema)
{
//LastOrDefault because we want the attribute from the dervived class.
var displayAttribute = (DisplayNameAttribute)attributes.LastOrDefault(x => x is DisplayNameAttribute);
if (displayAttribute != null)
schema.Title = displayAttribute.DisplayName;
}
private static void SetDescription(List<Attribute> attributes, Schema schema)
{
//LastOrDefault because we want the attribute from the dervived class. not sure if this works.
var descriptionAttribute = (DescriptionAttribute)attributes.LastOrDefault(x => x is DescriptionAttribute);
if (descriptionAttribute != null)
schema.Description = descriptionAttribute.Description;
}
private void UpdateSchema(ISchemaRegistry schemaRegistry, ModelMetadata modelMetadata, Schema schema = null)
{
if (modelMetadata.ModelType.IsValueType) return;
if (modelMetadata.ModelType == typeof(string)) return;
var idFor = _schemaIdManager.IdFor(modelMetadata.ModelType);
if (_updatedSchemeList.Contains(idFor))
return;
if (schema == null || schema.Ref != null)
{
if (schemaRegistry.Definitions.ContainsKey(idFor) == false) return;
schema = schemaRegistry.Definitions[idFor];
}
_updatedSchemeList.Add(idFor);
SetSchema(schema, modelMetadata);
if (schema.Type == "array")//Array Schema
{
var metaData = _metadataProvider.GetMetadataForType(modelMetadata.ModelType.GenericTypeArguments[0]);
UpdateSchema(schemaRegistry, metaData);
}
else//object schema
{
if (schema.Properties == null)
{
return;
}
foreach (var properties in modelMetadata.Properties)
{
if (!(properties is DefaultModelMetadata defaultModelMetadata))
{
continue;
}
var name = ToLowerCamelCase(defaultModelMetadata.Name);
if (schema.Properties.ContainsKey(name) == false)
{
//when this doesn't match the json object naming.
return;
}
var subSchema = schema.Properties[name];
SetSchema(subSchema, defaultModelMetadata);
UpdateSchema(schemaRegistry, defaultModelMetadata, subSchema);
}
}
}
private static string ToLowerCamelCase(string inputString)
{
if (inputString == null) return null;
if (inputString == string.Empty) return string.Empty;
if (char.IsLower(inputString[0])) return inputString;
return inputString.Substring(0, 1).ToLower() + inputString.Substring(1);
}
}