Search code examples
c#swaggerswashbuckleswashbuckle.aspnetcore

Generating format attribute for definition schemas using Swashbuckle.AspNetCore


I am facing an issue while trying to add a format attribute to each definition schema in my code using Swagger using Swashbuckle annotation [SwaggerSchema(Format = nameof(...))]. The value of the format attribute should be the name of the schema itself. Here's what I have tried:

[SwaggerSchema(Format = nameof(User))]
public class User {
// implementation
}

This works fine and results in the following schema output:

"User": {
  "format": "User",
  "type": "object",
  ...
}

However, I encountered a problem when working with classes that have generic parameters, such as ResponseList<T> which represents a list of response type. With the previous implementation, all schemas of ResponseList<...> have the same format name, which is "ResponseList", as shown below:

"ResponseList<User>: {
  "format": "ResponseList",
  ...
}

"ResponseList<Product>: {
  "format": "ResponseList",
  ...
}

But my desired goal is to have schemas like this:

"ResponseList<User>: {
  "format": "ResponseListUser",
  ...
}

"ResponseList<Product>: {
  "format": "ResponseListProduct",
  ...
}
```
Is there any way to achieve this? Any help would be appreciated.

Solution

  • Instead of using the [SwaggerSchema] attribute, define a custom ISchemaFilter that modifies the Schema object generated by Swashbuckle for each type. This is the implementation which handles nested generic types like ResponseList<Class<User>>:

    public class GenericTypeSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            if (context.Type.IsGenericType)
            {
                var typeName = context.Type.Name.Split('`')[0];
                var typeArgs = context.Type.GetGenericArguments();
                var formatValue = $"{typeName}";
                foreach (var typeArg in typeArgs)
                {
                    formatValue += $"{GetFormatValue(typeArg)}";
                }
                schema.Format = formatValue;
            }
        }
    
        private static string GetFormatValue(Type type)
        {
            if (!type.IsGenericType)
            {
                return type.Name.Replace(".", "_");
            }
    
            var typeName = type.Name.Split('`')[0];
            var typeArgs = type.GetGenericArguments();
            var formatValue = $"{typeName}";
            foreach (var typeArg in typeArgs)
            {
                formatValue += $"{GetFormatValue(typeArg)}";
            }
            return formatValue;
        }
    }
    

    In this implementation, we've added a new method called GetFormatValue that recursively processes each generic type parameter. If the type is not a generic type, we simply return its name. If it is a generic type, we extract its base name and process each type argument recursively.

    To use this updated filter with Swashbuckle, you can register it in your Swagger configuration like this:

    services.AddSwaggerGen(c =>
    {
        c.SchemaFilter<GenericTypeSchemaFilter>();
    });