Skip to content

Instantly share code, notes, and snippets.

@fabiomaulo
Created October 14, 2021 20:26
Show Gist options
  • Save fabiomaulo/8a2ed6d1604147b9233e6f717b848946 to your computer and use it in GitHub Desktop.
Save fabiomaulo/8a2ed6d1604147b9233e6f717b848946 to your computer and use it in GitHub Desktop.
// Little extension to do what I need for OpenApi
/// <summary>
/// Specifies the type of the value, depending on status code and MIME-Types, returned by the action
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class ProducesResponseAttribute : Attribute
{
public ProducesResponseAttribute(Type type, int statusCode = 200)
{
Type = type;
StatusCode = statusCode;
}
public Type Type { get; }
public int StatusCode { get; set; }
public string[] MimeTypes { get; set; }
}
namespace Sarlanga.Public.Api.OpenApi
{
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Linq;
using System.Reflection;
public class ProducesResponseOpenApiFilter : IOperationFilter
{
private const string attributeClassName = "ProducesResponseAttribute";
private readonly IOptions<MvcOptions> mvcoptions;
public ProducesResponseOpenApiFilter(IOptions<MvcOptions> mvcoptions)
{
this.mvcoptions = mvcoptions ?? throw new ArgumentNullException(nameof(mvcoptions));
}
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var actionAttributes = context.MethodInfo
.GetCustomAttributes()
.Where(a => attributeClassName.Equals(a.GetType().Name))
.Select(x => (dynamic)x)
.ToArray();
if (actionAttributes.Length == 0)
{
// No interesting attributes found
return;
}
var attrs = actionAttributes.Select(sc => new
{
Type = (Type)sc.Type,
StatusCode = (int)sc.StatusCode,
MimeTypes = (string[])sc.MimeTypes
}).ToArray();
addUndefinedStatusCodes();
overrideResponseStatusCode();
void addUndefinedStatusCodes()
{
foreach (var st in attrs.Select(x => x.StatusCode).Where(x => !operation.Responses.ContainsKey(x.ToString())).Distinct())
{
operation.Responses.Add(st.ToString(), new OpenApiResponse { Description = "" });
}
}
void overrideResponseStatusCode()
{
foreach (var rs in operation.Responses)
{
foreach (var rsAttr in attrs.Where(a => a.StatusCode.ToString() == rs.Key))
{
var mimeTypes = rsAttr.MimeTypes ??
mvcoptions.Value.OutputFormatters
.OfType<IApiResponseTypeMetadataProvider>()
.SelectMany(x => x.GetSupportedContentTypes(null, rsAttr.Type))
.ToArray();
overrideSchema(rsAttr.Type, mimeTypes, rs.Value);
}
}
void overrideSchema(Type responseType, string[] mediaTypes, OpenApiResponse response)
{
if (responseType is null || mediaTypes is null)
{
return;
}
foreach (var mt in mediaTypes)
{
var schema = context.SchemaGenerator.GenerateSchema(responseType, context.SchemaRepository);
if (response.Content.TryGetValue(mt, out var content))
{
content.Schema = schema;
}
else
{
response.Content.Add(mt, new OpenApiMediaType { Schema = schema });
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment