Swashbuckle Swagger - 从Attributes中提取信息并将其放入Schema定义中

时间:2018-06-13 00:24:35

标签: asp.net-core swashbuckle

我试图从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);
                }
            }
    }
}

1 个答案:

答案 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);
    }
}