Search code examples
c#swaggerasp.net-core-webapiswagger-uiswashbuckle

Why isn't Swashbuckle adhering to casing policy?


I have an ASP.Net Core API with Swagger and Swashbuckle code attributes to generate documentation for the UI. The API's naming policy is snake_case throughout, which on most of the documentation is fine, however where I have a data model being used in an endpoint method which has the [FromForm] attribute, i.e. it's uploaded as multipart/form-data, Swagger/Swashbuckle just WILL NOT convert the casing:

enter image description here

The Swashbuckle documentation is useless, I've tried ISchemaFilter, IOperationFilter and IDocumentFilter and can't find anything, I've also asked Github Copilot and that has no idea what it's doing either, it keeps telling me to use an IOperationFilter to set the operation parameter names, which I'm already doing and that's not working.

How do I do this? All I want is the following:

enter image description here


Solution

  • You can use IOperationFilter like below. And please check test result first.

    enter image description here

    SnakeCaseRequestBodyFilter.cs

    using Microsoft.OpenApi.Models;
    using Swashbuckle.AspNetCore.SwaggerGen;
    using System.Text.RegularExpressions;
    
    namespace WebApplication2
    {
        public class SnakeCaseRequestBodyFilter : IOperationFilter
        {
            public void Apply(OpenApiOperation operation, OperationFilterContext context)
            {
                Regex regex = new Regex("([a-z])([A-Z])");
    
                foreach (var parameter in operation.Parameters.Where(p => p.In == ParameterLocation.Query || p.In == ParameterLocation.Header || p.In == ParameterLocation.Path || p.In == ParameterLocation.Cookie))
                {
                    parameter.Name = regex.Replace(parameter.Name, "$1_$2").ToLower();
                }
    
                if (operation.RequestBody != null && operation.RequestBody.Content.ContainsKey("multipart/form-data"))
                {
                    var formDataContent = operation.RequestBody.Content["multipart/form-data"];
                    if (formDataContent.Schema?.Properties != null)
                    {
                        var properties = formDataContent.Schema.Properties;
    
                        foreach (var prop in properties.Keys.ToList())
                        {
                            var snakeCaseKey = regex.Replace(prop, "$1_$2").ToLower();
                            if (snakeCaseKey != prop)
                            {
                                var schema = properties[prop];
                                properties.Remove(prop);
                                properties[snakeCaseKey] = schema;
                            }
                        }
                    }
                }
            }
        }
    }
    

    Register it.

    builder.Services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
        c.OperationFilter<SnakeCaseRequestBodyFilter>();  
    });