I highly encourage you to reconsider your initial idea to replace the response content you are getting from ASP.NET core with your own custom response content.
ASP.NET core is opinionated about API controllers error handling. They have decided to handle some client errors (for instance the 415 status code) by using the Problem Details specification.
Quotes from the Problem Details RFC:
This document defines a "problem detail" as a way to carry machine-
readable details of errors in a HTTP response to avoid the need to
define new error response formats for HTTP APIs.
HTTP [RFC7230] status codes are sometimes not sufficient to convey
enough information about an error to be helpful. While humans behind
Web browsers can be informed about the nature of the problem with an
HTML [W3C.REC-html5-20141028] response body, non-human consumers of
so-called "HTTP APIs" are usually not.
This specification defines simple JSON [RFC7159] and XML
[W3C.REC-xml-20081126] document formats to suit this purpose. They
are designed to be reused by HTTP APIs, which can identify distinct
"problem types" specific to their needs.
This is a web standard to communicate errors between machines, so it is a choice which makes sense in the context of API controllers error handling.
Notice that this standard has been designed to avoid reinventing the wheel each time, by defining custom error contracts to communicate errors from web apis and having to document them for clients.
This documentation explains that for some HTTP status codes indicating a client error (e.g.: your 415 Unsopported Media Type status code) a JSON response compliant with the Problem Details specification is automatically created by the ASP.NET core framework.
You have a few choices here:
- you can decide to suppress this behavior by setting
ApiBehaviorOptions.SuppressMapClientErrors = true;. This way you'll get a plain status code (the 415 status code in your case) as the response, with no response content at all.
- you can decide to customize some properties of the returned Problem Details JSON payload, as documented here
- you can decide to take control of the generation of the Problem Details JSON response, by providing your own implementation of
ProblemDetailsFactory as documented here
I would avoid option 3.
In my opinion the best thing you can do is sticking with option 2, if you really want to customize the error message contained inside the problem details JSON object.
Here is an example:
public void ConfigureServices(IServiceCollection services)
{
services
.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.ClientErrorMapping[415].Title = "My custom message";
});
}
The response content you will get (for an invalid request, a request with no content for example) is the following:

Another option you can evaluate is option 1: suppressing the default client error handling done by ASP.NET core and take full control of the response in case of a 415 response status code.
This is an example:
public void ConfigureServices(IServiceCollection services)
{
services
.AddControllers()
.ConfigureApiBehaviorOptions(options =>
{
options.SuppressMapClientErrors = true;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Use(async (context, next) =>
{
await next();
// Handle the 415 response
if (context.Response.StatusCode == 415)
{
context.Response.ContentType = "application/json";
var json = JsonSerializer.Serialize(new { message = "You need to add a request body!" });
await context.Response.WriteAsync(json);
}
});
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
The response payaload you will get (for an invalid request, a request with no content for example) is the following:

Notice that here I'm able to take full control of the response content because, by disabling the default client error mapping done by ASP.NET core (options.SuppressMapClientErrors = true;), the response you get when sending an invalid POST request is a plain 415 status code, with no response content at all. So, when writing to the response stream (await context.Response.WriteAsync(json);), you are writing inside an empty response stream, so only our custom JSON is sent to the client as the response content.
In your first attempt you forgot to disable the automatic response created by ASP.NET core in case of 415 response status code, so the code await context.Response.WriteAsync("Unsupported media type handled"); is appending content to the response stream, after the response content already written by the ASP.NET core framework automatically.
context.Response.ReasonPhrase?ReasonPhraseproperty does not appear to be on thecontextobject. If it was I'm guessing its just text about 415, no?next.Invoke(), the JSON has already been written to the response, so you're just appending your text to that.ReasonPhraseis oncontext.Response