Closed pmcevoy closed 7 years ago
When you have actions that return a type that doesn't quite correspond to the actual API contract (e.g. HttpResponseMessage, IHttpActionResult or in your case HttpResponseMessage<T>, WebAPI provides the [ResponseType] attribute to inform it's metadata layer (ApiExplorer) of the "acctual" response type. If you use this for the success case and just use [SwaggerResponse] for the error cases, you won't get the redundant definition:
[ResponseType(typeof(Product))]
[SwaggerResponse(400, "Validation Error", typeof(HttpError))]
public HttpResponseMessage<Product> Create(Product product)
{
....
Right now, this would be the recommended approach for your case. It does mean you have to mix and match framework attributes with Swashbuckle attributes to cover success and error response codes which isn't ideal. However, in ASP.NET Core (next-gen), the framework introduces a new attribute [ProducesResponseType] for covering the error cases. So, in the next major version of SB (targeting ASP.NET Core) the SwaggerResponse attributes will be removed in favor of the built-in ones.
Interesting - we'll give that a shot. If I can remove that custom filter, I'll be happy.
There is now a separate project specifically for ASP.NET Core and this allows you to describe all response codes purely with attributes that are native to that framework:
https://github.com/domaindrivendev/Swashbuckle.AspNetCore
Of course this is only available if you've moved your application to ASP.NET Core. If this isn't on the cards, then you're stuck with the workarounds described above
It works for me, @pmcevoy. Thank you!
@pmcevoy Thanks for posting this. I was wondering if you have similar solution for ASP.NET Core?
Simpler solution (works with ASP.NET Core):
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Services;
using Swashbuckle.AspNetCore.SwaggerGen;
/// <summary>
/// Removes unreferenced component schemas from the OpenAPI document.
/// </summary>
internal sealed class UnusedComponentSchemaCleaner : IDocumentFilter
{
public void Apply(OpenApiDocument document, DocumentFilterContext context)
{
var visitor = new OpenApiReferenceVisitor();
var walker = new OpenApiWalker(visitor);
walker.Walk(document);
HashSet<string> unusedSchemaNames = [];
foreach (string schemaId in document.Components.Schemas
.Select(schema => schema.Key)
.Where(schemaId => !visitor.UsedSchemaNames.Contains(schemaId)))
{
unusedSchemaNames.Add(schemaId);
}
foreach (string schemaId in unusedSchemaNames)
{
document.Components.Schemas.Remove(schemaId);
}
}
private sealed class OpenApiReferenceVisitor : OpenApiVisitorBase
{
private const string ComponentSchemaPrefix = "#/components/schemas/";
public HashSet<string> UsedSchemaNames { get; } = [];
public override void Visit(IOpenApiReferenceable referenceable)
{
UsedSchemaNames.Add(referenceable.Reference.Id);
}
public override void Visit(OpenApiSchema schema)
{
if (schema.Discriminator != null)
{
foreach (string discriminatorValue in schema.Discriminator.Mapping.Values)
{
if (discriminatorValue.StartsWith(ComponentSchemaPrefix, StringComparison.Ordinal))
{
string schemaId = discriminatorValue[ComponentSchemaPrefix.Length..];
UsedSchemaNames.Add(schemaId);
}
}
}
}
}
}
I recently updated our ApiController action methods to return a generic type that inherits from
HttpResponseMessage
(we call itTypedHttpResponseMessage<T>
) and to use the[SwaggerResponse]
attribute to document the return types as the<T>
. However when I pasted the generated swagger into http://editor.swagger.io it warned me about unused defintions relating to theTypedHttpResponseMessage
. (Seems that this does not happen if I use a rawHttpResponseMessage
)So I wrote an IDocumentFilter that will remove unused defintions and thought I post it here in case someone else gets the same issue (and if anyone cares to code-review I won't mind either!)
YMMV - we don't use all the features of a swagger file, so this filter may need to be updated if you use other schemas