aspnetcore: Customize error handling for [ApiController]

Is this a Bug or Feature request?:

Feature Request / Question

Steps to reproduce or link to a repro project:

[ApiController]
[Produces("application/json")]
[Route("api/v1/value")]
public class ValueApiController : Controller
{
    [HttpGet]
    public ActionResult<string[]> Get()
    {
        throw new Exception("bam");
    }
}

Startup.cs

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseStatusCodePagesWithReExecute("/Error");
}

Description of the problem:

When an error occurs in Controller classes marked with [ApiController], I don’t want them to return HTML but JSON instead:

  • In development, instead of Developer Exception Page, ideally the API Controller should return stack trace in plain text / JSON.

  • In production, instead of a custom HTML error response, ideally the API Controller should return a plain text / JSON with like "An error occurred while processing your request."

When using Core MVC < 2.1, I have been able to do this by using code like:

app.UseWhen(context => context.Request.Path.Value.StartsWith("/api", StringComparison.OrdinalIgnoreCase), builder =>
{
    builder.UseExceptionHandler(configure =>
    {
        configure.UseMiddleware<ApiExceptionHandlerMiddleware>(env.IsDevelopment());
    });
});

app.UseWhen(context => context.Request.Path.Value.StartsWith("/api", StringComparison.OrdinalIgnoreCase) == false, builder =>
{
    if (env.IsDevelopment())
    {
        builder.UseDeveloperExceptionPage();
    }
    else
    {
        builder.UseExceptionHandler("/error");
        builder.UseStatusCodePagesWithReExecute("/error");
    }
});

However, seeing that the purpose of the [ApiController] is: (written on the XML doc)

Indicates that a type and all derived types are used to serve HTTP API responses. The presence of this attribute can be used to target conventions, filters and other behaviors based on the purpose of the controller.

, I would like to know how to alter the error behavior when this attribute is encountered. (Instead of checking whether the request starts with /api)

Version of Microsoft.AspNetCore.Mvc or Microsoft.AspNetCore.App or Microsoft.AspNetCore.All:

<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-rc1-final" />

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 15 (10 by maintainers)

Most upvoted comments

All behavior related to the ApiControllerAttribute is defined in the ApiBehaviorApplicationModelProvider:

https://github.com/aspnet/Mvc/blob/504da3c565a9a159365b4564dba5f2cd7d059e7f/src/Microsoft.AspNetCore.Mvc.Core/Internal/ApiBehaviorApplicationModelProvider.cs#L57-L91

AFAIK, the ApiControllerAttribute doesn’t affect exception handling. You might be referring to the ModelStateInvalidFilter, which lets you remove the usual MVC validation boilerplate:

https://github.com/aspnet/Mvc/blob/504da3c565a9a159365b4564dba5f2cd7d059e7f/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/ModelStateInvalidFilter.cs#L51-L58

The default implementation of InvalidModelStateResponseFactory just creates a new BadRequestObjectResult with the ModelState dictionary:

https://github.com/aspnet/Mvc/blob/ec31ff0c28961be64ad0b8228eba3a084086f60b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ApiBehaviorOptionsSetup.cs#L25-L33

If you want to customize this behavior, you can set InvalidModelStateResponseFactory:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        // Do whatever you want in here, i.e. return an IActionResult or throw an exception.
    };
});

ideally should be a part of the official libraries

Those are official libraries. Not official from Microsoft, but official from the .NET community. Not good enough?

so we don’t have to look for the solution every time when starting a new project.

You don’t. Look no further. The functionality has been provided by the community. You’re welcome 😉

I like this. Please allow us to customize the response. Today we are using a custom Middleware that adds a global exception filter. The exception filter then creates a response that conforms with RFC 7807. It all gets setup with a builder like this: app.UseApiExceptionResponse(bool devEnv);

If we can do something like this nativly in MVC that will be great.

Like @ryanelian points out, the model state response factory addresses one aspect of the error handling viz model validation. Handling errors is just as interesting. Speaking to @rynowak, to support Api Controllers over routing would involve

a) Set a flag (maybe a Http feature) as part of executing an API controller b) Use this in the error handler (Template update)

Speaking to @rynowak, dispatcher should solve the first requirement. This would make for a pretty nice enhancement with the rest of the API work we’d do for 2.2