Control de errores en aplicaciones de API mínimas

Nota:

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión .NET 8 de este artículo.

Con contribuciones de David Acker

En este artículo se describe cómo controlar los errores en aplicaciones de API mínimas.

Excepciones

En una aplicación de API mínima, hay dos mecanismos centralizados integrados diferentes para controlar excepciones no controladas:

En esta sección se hace referencia a la siguiente aplicación de API mínima para mostrar formas de controlar las excepciones. Produce una excepción cuando se solicita el punto de conexión /exception:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/exception", () => 
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

Página de excepciones para el desarrollador

La página de excepciones para el desarrollador muestra seguimientos de pila detallados de los errores del servidor. Usa DeveloperExceptionPageMiddleware para capturar excepciones sincrónicas y asincrónicas de la canalización HTTP y para generar respuestas de error.

De forma predeterminada, las aplicaciones de ASP.NET Core habilitan la página de excepciones del desarrollador cuando se cumplen las dos condiciones siguientes:

Para obtener más información sobre cómo configurar middleware, consulte Middleware en aplicaciones de API mínimas.

Con la aplicación de API mínima anterior, cuando Developer Exception Page detecta una excepción no controlada, genera una respuesta de texto sin formato predeterminada similar al ejemplo siguiente:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8
Date: Thu, 27 Oct 2022 18:00:59 GMT
Server: Kestrel
Transfer-Encoding: chunked

    System.InvalidOperationException: Sample Exception
    at Program.<>c.<<Main>$>b__0_1() in ....:line 17
    at lambda_method2(Closure, Object, HttpContext)
    at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
    --- End of stack trace from previous location ---
    at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Connection: keep-alive
Host: localhost:5239
Accept-Encoding: gzip, deflate, br

Advertencia

No habilite la página de excepciones para el desarrollador a menos que la aplicación se ejecute en el entorno de desarrollo. No comparta públicamente información detallada sobre las excepciones cuando la aplicación se ejecute en producción. Para más información sobre la configuración de los entornos, consulte Uso de varios entornos en ASP.NET Core.

Controlador de excepciones

En entornos que no son de desarrollo, use el middleware de control de excepciones para producir una carga de error. Para configurar Exception Handler Middleware, llame a UseExceptionHandler.

Por ejemplo, el código siguiente cambia la aplicación para responder con una carga compatible con RFC 7807 al cliente. Para más información, consulte la sección de detalles del problema.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp 
    => exceptionHandlerApp.Run(async context 
        => await Results.Problem()
                     .ExecuteAsync(context)));

app.MapGet("/exception", () => 
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

Respuestas de error de cliente y servidor

Tenga en cuenta la siguiente aplicación de API mínima.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

El punto de conexión /users genera 200 OK con una representación json de User cuando id es mayor que 0, de lo contrario, un código de estado 400 BAD REQUEST sin un cuerpo de respuesta. Para obtener más información sobre cómo crear una respuesta, consulte Creación de respuestas en aplicaciones de API mínimas.

Status Code Pages middleware se puede configurar para generar un contenido de cuerpo común, cuando está vacío, para todas las respuestas de cliente HTTP (400-499) o servidor (500 -599). El middleware se configura llamando al método de extensión UseStatusCodePages.

Por ejemplo, en el ejemplo siguiente se cambia la aplicación para que responda con una carga compatible con RFC 7807 al cliente para todas las respuestas de cliente y servidor, incluidos los errores de enrutamiento (por ejemplo, 404 NOT FOUND). Para más información, consulte la sección de detalles del problema.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseStatusCodePages(async statusCodeContext 
    => await Results.Problem(statusCode: statusCodeContext.HttpContext.Response.StatusCode)
                 .ExecuteAsync(statusCodeContext.HttpContext));

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

Detalles del problema

Los detalles del problema no son el único formato de respuesta para describir un error de la API HTTP; sin embargo, se usan normalmente para notificar errores para las API HTTP.

El servicio de detalles del problema implementa la interfaz IProblemDetailsService, que admite la creación de detalles del problema en ASP.NET Core. El método de extensión AddProblemDetails de IServiceCollection registra la implementación IProblemDetailsService predeterminada.

En aplicaciones de ASP.NET Core, el middleware siguiente genera respuestas HTTP de detalles del problema cuando se llama a AddProblemDetails, excepto cuando el encabezado HTTP de solicitud Accept no incluye uno de los tipos de contenido admitidos por el IProblemDetailsWriter registrado (valor predeterminado: application/json):

Las aplicaciones de API mínimas se pueden configurar para generar respuestas de detalles del problema para todas las respuestas de error de servidor y cliente HTTP que aún no tienen contenido del cuerpo mediante el método de extensión AddProblemDetails.

El código siguiente configura la aplicación para generar detalles del problema:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

app.MapGet("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

Para más información sobre el uso de AddProblemDetails, consulte Detalles del problema.

Reserva de IProblemDetailsService

En el código siguiente, httpContext.Response.WriteAsync("Fallback: An error occurred.") devuelve un error si la implementación de IProblemDetailsService no puede generar ProblemDetails:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async httpContext =>
    {
        var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
        if (pds == null
            || !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
        {
            // Fallback behavior
            await httpContext.Response.WriteAsync("Fallback: An error occurred.");
        }
    });
});

app.MapGet("/exception", () =>
{
    throw new InvalidOperationException("Sample Exception");
});

app.MapGet("/", () => "Test by calling /exception");

app.Run();

El código anterior:

El ejemplo siguiente es similar al anterior, excepto que llama a Status Code Pages middleware.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseStatusCodePages(statusCodeHandlerApp =>
{
    statusCodeHandlerApp.Run(async httpContext =>
    {
        var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
        if (pds == null
            || !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
        {
            // Fallback behavior
            await httpContext.Response.WriteAsync("Fallback: An error occurred.");
        }
    });
});

app.MapGet("/users/{id:int}", (int id) =>
{
    return id <= 0 ? Results.BadRequest() : Results.Ok(new User(id));
});

app.MapGet("/", () => "Test by calling /users/{id:int}");

app.Run();

public record User(int Id);

En este artículo se describe cómo controlar los errores en aplicaciones de API mínimas.

Excepciones

En una aplicación de API mínima, hay dos mecanismos centralizados integrados diferentes para controlar excepciones no controladas:

En esta sección se hace referencia a la siguiente aplicación de API mínima para mostrar formas de controlar las excepciones. Produce una excepción cuando se solicita el punto de conexión /exception:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/exception", () 
    => { throw new InvalidOperationException("Sample Exception"); });

app.Run();

Página de excepciones para el desarrollador

La página de excepciones para el desarrollador muestra seguimientos de pila detallados de los errores del servidor. Usa DeveloperExceptionPageMiddleware para capturar excepciones sincrónicas y asincrónicas de la canalización HTTP y para generar respuestas de error.

De forma predeterminada, las aplicaciones de ASP.NET Core habilitan la página de excepciones del desarrollador cuando se cumplen las dos condiciones siguientes:

Para obtener más información sobre cómo configurar middleware, consulte Middleware en aplicaciones de API mínimas.

Con la aplicación de API mínima anterior, cuando Developer Exception Page detecta una excepción no controlada, genera una respuesta de texto sin formato predeterminada similar al ejemplo siguiente:

HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8
Date: Thu, 27 Oct 2022 18:00:59 GMT
Server: Kestrel
Transfer-Encoding: chunked

    System.InvalidOperationException: Sample Exception
    at Program.<>c.<<Main>$>b__0_1() in ....:line 17
    at lambda_method2(Closure, Object, HttpContext)
    at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
    --- End of stack trace from previous location ---
    at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Connection: keep-alive
Host: localhost:5239
Accept-Encoding: gzip, deflate, br

Advertencia

No habilite la página de excepciones para el desarrollador a menos que la aplicación se ejecute en el entorno de desarrollo. No comparta públicamente información detallada sobre las excepciones cuando la aplicación se ejecute en producción. Para más información sobre la configuración de los entornos, consulte Uso de varios entornos en ASP.NET Core.

Controlador de excepciones

En entornos que no son de desarrollo, use el middleware de control de excepciones para producir una carga de error. Para configurar Exception Handler Middleware, llame a UseExceptionHandler.

Por ejemplo, el código siguiente cambia la aplicación para responder con una carga compatible con RFC 7807 al cliente. Para más información, consulte la sección de detalles del problema.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseExceptionHandler(exceptionHandlerApp 
    => exceptionHandlerApp.Run(async context 
        => await Results.Problem()
                     .ExecuteAsync(context)));

app.Map("/exception", () 
    => { throw new InvalidOperationException("Sample Exception"); });

app.Run();

Respuestas de error de cliente y servidor

Tenga en cuenta la siguiente aplicación de API mínima.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Map("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.Run();

public record User(int Id);

El punto de conexión /users genera 200 OK con una representación json de User cuando id es mayor que 0, de lo contrario, un código de estado 400 BAD REQUEST sin un cuerpo de respuesta. Para obtener más información sobre cómo crear una respuesta, consulte Creación de respuestas en aplicaciones de API mínimas.

Status Code Pages middleware se puede configurar para generar un contenido de cuerpo común, cuando está vacío, para todas las respuestas de cliente HTTP (400-499) o servidor (500 -599). El middleware se configura llamando al método de extensión UseStatusCodePages.

Por ejemplo, en el ejemplo siguiente se cambia la aplicación para que responda con una carga compatible con RFC 7807 al cliente para todas las respuestas de cliente y servidor, incluidos los errores de enrutamiento (por ejemplo, 404 NOT FOUND). Para más información, consulte la sección de detalles del problema.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseStatusCodePages(async statusCodeContext 
    =>  await Results.Problem(statusCode: statusCodeContext.HttpContext.Response.StatusCode)
                 .ExecuteAsync(statusCodeContext.HttpContext));

app.Map("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.Run();

public record User(int Id);

Detalles del problema

Los detalles del problema no son el único formato de respuesta para describir un error de la API HTTP; sin embargo, se usan normalmente para notificar errores para las API HTTP.

El servicio de detalles del problema implementa la interfaz IProblemDetailsService, que admite la creación de detalles del problema en ASP.NET Core. El método de extensión AddProblemDetails de IServiceCollection registra la implementación IProblemDetailsService predeterminada.

En aplicaciones de ASP.NET Core, el middleware siguiente genera respuestas HTTP de detalles del problema cuando se llama a AddProblemDetails, excepto cuando el encabezado HTTP de solicitud Accept no incluye uno de los tipos de contenido admitidos por el IProblemDetailsWriter registrado (valor predeterminado: application/json):

Las aplicaciones de API mínimas se pueden configurar para generar respuestas de detalles del problema para todas las respuestas de error de servidor y cliente HTTP que aún no tienen contenido del cuerpo mediante el método de extensión AddProblemDetails.

El código siguiente configura la aplicación para generar detalles del problema:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

app.Map("/users/{id:int}", (int id) 
    => id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );

app.Map("/exception", () 
    => { throw new InvalidOperationException("Sample Exception"); });

app.Run();

Para más información sobre el uso de AddProblemDetails, consulte Detalles del problema.