Search code examples
c#swagger-uiswagger-codegenswashbuckle.aspnetcore

How to hide a specific schema in the Swagger UI?


I've implemented the Swagger Documentation in a web API made with C# (.NET Core 5).

I have 3 classes: DefaultCommandResult, SuccessCommandResult and ErrorCommandResult.

The DefaultCommandResult is the base class witch the other two inherit from. The object returned in the endpoint is either a SuccessCommandResult or ErrorCommandResult, and DefaultCommandResult is never returned. What I need to do is hide the schema generated in the endpoints' responses related to the base class DefaultCommandResult: Swagger UI

The request in the controller:

public ActionResult<DefaultCommandResult<ServerIdCommand>> Get([FromQuery] RecoverServerIdCommand command)
{
    try
    {
        var id = handler.RecoverServerId(command);

        if (id is null)
            return NotFound();

        var output = new ServerIdCommand { Id = id };
        var result = new SuccessCommandResult<ServerIdCommand>(output);

        return Ok(result);
    }
    catch (ObjectNotFoundException ex)
    {
        var result = new ErrorCommandResult<string>(ex.Message);
        return NotFound(result);
    }
    catch (Exception ex)
    {
        var result = new ErrorCommandResult<string>(ex.Message);
        return BadRequest(result);
    }
}

The DefaultCommandResult class:

[SwaggerSubType(typeof(SuccessCommandResult<object>))]
[SwaggerSubType(typeof(ErrorCommandResult<object>))]
    
public class DefaultCommandResult<T> where T : class
{
    public bool Success { get; set; }

    protected DefaultCommandResult(bool success)
    {
        Success = success;
    }
}

The SuccessCommandResult class (ErrorCommandResult follows the same structure, but for errors):

[SwaggerDiscriminator("successCommandResult")]
public class SuccessCommandResult<T> : DefaultCommandResult<T> where T : class
{
    public List<T> Data { get; private set; }

    public SuccessCommandResult() : base(true) { }

    public SuccessCommandResult(List<T> data) : base(true)
    {
        Data = data;
    }

    public SuccessCommandResult(T @object) : base(true)
    {
        if (Data is null)
            Data = new List<T> { @object };
        else
            Data.Add(@object);
    }

}

How can I achieve that using the Swashbuckle framework for C#? Thanks in advance.


Solution

  • The way I found a resolution for that was creating a Document Filter for the name of the schema:

    public class SchemaNameFilter : IDocumentFilter
    {
        // w/o the filter: ServerIdCommandSuccessCommandResult. / with the filter: SuccessComandResult
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            foreach (var schema in swaggerDoc.Components.Schemas)
            {
                foreach (var schemaRepository in context.SchemaRepository.Schemas)
                {
                    if (schema.Key.Equals(schemaRepository.Key))
                    {
                        switch (schema.Key)
                        {
                            case string x when !x.Equals(nameof(DefaultCommandResult)) && x.Contains(nameof(DefaultCommandResult)):
                                schema.Value.Title = nameof(DefaultCommandResult);
                                break;
                            case string x when !x.Equals(nameof(SuccessCommandResult<string>)) && x.Contains(nameof(SuccessCommandResult<string>)):
                                schema.Value.Title = nameof(SuccessCommandResult<string>);
                                break;
                            case string a when !a.Equals(nameof(OutputCommandResult)) && a.Contains(nameof(OutputCommandResult)):
                                schema.Value.Title = nameof(OutputCommandResult);
                                break;
                        }
                    }
                }
                    
            }
        }
    }
    

    What it does, basically, is rename the Swagger schema (with the incorrect name) to the name of the class.

    Then, in the Startup class, add the document filter:

    services.AddSwaggerGen(c => 
    {
       c.DocumentFilter<SchemaNameFilter>();
    });