Reputation: 158
I am looking into adding an OpenAPI spec to my web API project, but I am running into various obstacles that I am not able to resolve.
API endpoint: /api/some_controller/some_method/id
The content body needs to come from the http body, but I do not want automatic binding using [FromBody]
as I need to stream and process the data as-is (auditing, etc).
I added swagger to my project but as expected it does not show a body parameter.
The following DOES generate a proper swagger API definition:
public void some_method([FromBody]MyType mytype);
But as stated before, I need the raw data without model binding.
I am at a loss on how to solve this. Do I need to augment the API explorer somehow? Do I need to add options to swagger? Is there some way to have the [FromBody]
attribute that does not actually bind? How can I do this?
Upvotes: 2
Views: 2455
Reputation: 158
I managed to get this to work using an extra custom attribute and an IOperationFilter
[AttributeUsage(AttributeTargets.Method)]
public class OpenApiRequestBodyType: Attribute
{
public Type BodyType { get; }
public string [] ContentTypes { get; }
public OpenApiRequestBodyType(Type type, string[] contentTypes = null)
{
BodyType = type;
ContentTypes = contentTypes;
}
}
public class SwaggerBodyTypeOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var bodyTypeAttribute = context.ApiDescription.CustomAttributes().OfType<OpenApiRequestBodyType>().FirstOrDefault();
if (bodyTypeAttribute != null)
{
var schema = context.SchemaGenerator.GenerateSchema(bodyTypeAttribute.BodyType, context.SchemaRepository);
operation.RequestBody = new OpenApiRequestBody();
string[] contentTypes;
if (bodyTypeAttribute.ContentTypes != null)
contentTypes = bodyTypeAttribute.ContentTypes;
else
contentTypes = operation.Responses.Where(x => x.Key =="200").SelectMany(x=>x.Value.Content).Select(x=>x.Key).ToArray();
foreach (var contentType in contentTypes)
{
operation.RequestBody.Content.Add(KeyValuePair.Create(contentType, new OpenApiMediaType { Schema = schema }));
}
}
}
}
Then I simply tag the method:
[OpenApiRequestBodyType(typeof(my_custom_type))]
and in the Startup:
services.AddSwaggerGen(c =>
{
c.OperationFilter<SwaggerBodyTypeOperationFilter>();
}
I am still not sure if there is no better way to do this.... but at least it works for me...
Upvotes: 4