Search code examples
c#swaggerswagger-uiasp.net-core-6.0

Why is Swagger showing endpoints of imported project, instead of main/startup project


I have one main API project (called CustomerLayer) that contains only one controller with one endpoint (UserController/GetAll). The controller inherits from ControllerBase and has all DataAnnotations in place (Route, HttpGet, etc). This project has imported onto the solution another API project (called ImporterLayer).

When I run the solution, Swagger is successfully showing "CustomerLayer" title, but it shows all the endpoints of the controllers on the imported project (ImporterLayer) instead of showing my one only existing endpoint on the main project.

The solution startup properties are setted as: Single startup project with "CustomerLayer" selected. Also the CustomerLayer project is selected as startup project.

Here's the Program.cs relevant code of the main project (CustomerLayer)

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI(options =>
{
    options.SwaggerEndpoint("/swagger/v1/swagger.json", typeof(Program).Assembly.GetName().Name);
    options.RoutePrefix = "swagger";
    options.DisplayRequestDuration();
});

app.Run();

I'm expecting to see only the endpoint (UserController/GetAll) of the main project CustomerLayer.


Solution

  • After a few hours of investigation, I've came up with a solution (with the help of ChatGPT :)

    I changed:

    builder.Services.AddSwaggerGen();
    

    To:

    builder.Services.AddSwaggerGen(c =>
    {
        c.DocumentFilter<SwaggerIgnoreFilter>();
    });
    

    Created the Class 'SwaggerIgnoreFilter':

        public class SwaggerIgnoreFilter : IDocumentFilter
        {
            public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
            {
                foreach (var contextApiDescription in context.ApiDescriptions)
                {
                    var actionDescriptor = (ControllerActionDescriptor)contextApiDescription.ActionDescriptor;
    
                    if (!actionDescriptor.ControllerTypeInfo.GetCustomAttributes<LayerTwo>().Any() &&
                       !actionDescriptor.MethodInfo.GetCustomAttributes<LayerTwo>().Any())
                    {
                        var key = "/" + contextApiDescription.RelativePath.TrimEnd('/');
                        swaggerDoc.Paths.Remove(key);
                    }
                }
            }
        }
    

    And created the attribute:

        [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
        public class LayerTwo : Attribute
        {
        }
    

    And finally added the attribute [LayerTwo] to every Controller or Endpoint that I needed to.

    With this workaround you'll be able to show only the controllers/endpoints that are marked with [LayerTwo]