I decorate a controller (action method) with the ProducesAttribute to limit the response media type to application/json. If I return a ProblemDetails instance from the controller (or the framework returns a ValidationProblemDetails instance), the returned media type is application/json. If I leave out the ProducesAttribute, the response has media type application/problem+json.
I am unsure if this is a bug or is by design.
What I am finally trying to achieve is to have a generated OpenAPI spec (using Swashbuckle.AspNetCore) indicating application/problem+json for my 400 responses, and application/json for my 200 responses. Swashbuckle gets its information from the API Explorer from ASP.NET Core itself, this led me to this issue (or question).
Similar issue found in this SO question (unanswered).
See minimalistic repo with contrived example: https://github.com/ralphhendriks/aspnet-core-problem-details-media-type-issue
I am using ASP.NET Core version 3.1
Output of dotnet --info:
$ dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 3.1.102
Commit: 573d158fea
Runtime Environment:
OS Name: Windows
OS Version: 10.0.18363
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\3.1.102\
Host (useful for support):
Version: 3.1.2
Commit: 916b5cba26
.NET Core SDKs installed:
2.1.802 [C:\Program Files\dotnet\sdk]
3.1.101 [C:\Program Files\dotnet\sdk]
3.1.102 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 3.1.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
To install additional .NET Core runtimes or SDKs:
https://aka.ms/dotnet-download
IDE used seems irrelevant (using both VS 2019 Enterprise Edition and VS Code with OmniSharp plugin).
We are running into this as well now, with a client trying to generate NSwag proxy from our swashbuckle generated swagger document. We are also unable to get the 200's to just return application json and the 400s application problem json
I have same issue. For now I have resolved it by creating a derived ProducesAttribute with special handling for ProblemDetail:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ProducesAttribute : Microsoft.AspNetCore.Mvc.ProducesAttribute
{
public ProducesAttribute(Type type) : base(type)
{
}
public ProducesAttribute(string contentType, params string[] additionalContentTypes) : base(contentType, additionalContentTypes)
{
}
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is ObjectResult result && result.Value is ProblemDetails) return;
base.OnResultExecuting(context);
}
}
Though I still haven't found a way how to configure different content type for 4xx and 5xx responses for Swagger.
@ralphhendriks with latest Swashbuckle 5.2.1 I was able to configure application/problem+json for problem details with IOperationFilter. I guess it will work with earlier 5.x versions too, though I haven't checked.
services.AddSwaggerGen(c =>
{
c.OperationFilter<ProblemDetailsOperationFilter>();
});
public class ProblemDetailsOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
operation.Responses.Add("400", new OpenApiResponse
{
Content =
{
["application/problem+json"] = new OpenApiMediaType
{
Schema = context.SchemaGenerator.GenerateSchema(typeof(ProblemDetails), context.SchemaRepository)
}
}
});
}
}
@ralphhendriks with inspiration of @andrii-litvinov answer we fixed our issue by doing this:
public class ProblemDetailsOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
foreach (var operationResponse in operation.Responses)
{
if (operationResponse.Key.StartsWith("2"))
{
operationResponse.Value.Content.Remove("application/problem+json");
operationResponse.Value.Content.Remove("application/problem+xml");
}
if (operationResponse.Key.StartsWith("4") || operationResponse.Key.StartsWith("5"))
{
operationResponse.Value.Content.Remove("application/json");
operationResponse.Value.Content.Remove("application/xml");
}
}
}
}
Our controller has an [Produces] attribute with all possible content-types.
Change at your convenience.
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
Most helpful comment
We are running into this as well now, with a client trying to generate NSwag proxy from our swashbuckle generated swagger document. We are also unable to get the 200's to just return application json and the 400s application problem json