Compartir vía


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, consulta la versión .NET 8 de este artículo.

Advertencia

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta 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. Para obtener información sobre el control de errores en las API basadas en controladores, consulte Controlar errores en ASP.NET Core y Controlar errores en las API web basadas en controladores de ASP.NET Core.

Excepciones

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

Esta sección hace referencia a la siguiente aplicación de ejemplo para demostrar las formas de controlar las excepciones en una API mínima. 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

En la Página de excepciones para el desarrollador se muestra información detallada sobre las excepciones de la solicitud no controladas. Usa DeveloperExceptionPageMiddleware para capturar excepciones sincrónicas y asincrónicas de la canalización HTTP y para generar respuestas de error. La página de excepciones del desarrollador se ejecuta al principio de la canalización del middleware, de modo que puede capturar las excepciones no controladas que se producen en el middleware que sigue.

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

Las aplicaciones creadas con plantillas anteriores, es decir, con WebHost.CreateDefaultBuilder, pueden habilitar la página de excepciones del desarrollador llamando a app.UseDeveloperExceptionPage.

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.

La página de excepciones para el desarrollador puede incluir la siguiente información sobre la excepción y la solicitud:

  • Seguimiento de la pila
  • Parámetros de cadena de consulta (si existen)
  • Cookies, si existen
  • encabezados
  • Metadatos del punto de conexión, si los hay

No se garantiza que la página de excepciones del desarrollador proporcione alguna información. Use el registro obtener información completa sobre el error.

En la imagen siguiente se muestra una página de excepción de desarrollador de ejemplo con animación para mostrar las pestañas y la información mostrada:

Página de excepción de desarrollador animada para mostrar cada pestaña seleccionada.

En respuesta a una solicitud con un encabezado Accept: text/plain, la página de excepciones del desarrollador devuelve texto sin formato en lugar de HTML. Por ejemplo:

Status: 500 Internal Server Error
Time: 9.39 msSize: 480 bytes
FormattedRawHeadersRequest
Body
text/plain; charset=utf-8, 480 bytes
System.InvalidOperationException: Sample Exception
   at WebApplicationMinimal.Program.<>c.<Main>b__0_0() in C:\Source\WebApplicationMinimal\Program.cs:line 12
   at lambda_method1(Closure, Object, HttpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS
=======
Accept: text/plain
Host: localhost:7267
traceparent: 00-0eab195ea19d07b90a46cd7d6bf2f

Para ver la página de excepciones del desarrollador:

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 Detalles del problema más adelante en este artículo.

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(IServiceCollection) 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(IServiceCollection) 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.