Created
October 14, 2021 20:26
-
-
Save fabiomaulo/8a2ed6d1604147b9233e6f717b848946 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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