Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Note
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Warning
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Questo articolo illustra gli approcci comuni alla gestione degli errori nelle app Web di ASP.NET Core. Vedere anche Gestire gli errori nelle API di base di ASP.NET.
Per Blazor indicazioni sulla gestione degli errori, che aggiungono o sostituiscono quelle fornite in questo articolo, vedere Gestire gli errori nelle app di ASP.NET Core Blazor.
Pagina delle eccezioni per gli sviluppatori
Nella pagina Eccezioni sviluppatore vengono visualizzate informazioni dettagliate sulle eccezioni di richiesta non gestite. Usa DeveloperExceptionPageMiddleware per acquisire eccezioni sincrone e asincrone dalla pipeline HTTP e per generare risposte di errore. La pagina delle eccezioni per sviluppatori interviene all'inizio della pipeline middleware, in modo da poter intercettare le eccezioni non gestite generate dal middleware successivo.
Le app ASP.NET Core attivano per impostazione predefinita la pagina delle eccezioni per sviluppatori quando sono soddisfatte entrambe le condizioni:
- Esecuzione nell'ambiente di
Development. - L'app è stata creata con i modelli correnti, ovvero usando WebApplication.CreateBuilder.
Le app create usando i modelli precedenti, ovvero usando WebHost.CreateDefaultBuilder, possono abilitare la pagina delle eccezioni per sviluppatori chiamando app.UseDeveloperExceptionPage.
Warning
Non abilitare la pagina eccezioni per sviluppatori a meno che l'app non sia in esecuzione nell'ambienteDevelopment. Non condividere pubblicamente informazioni dettagliate sulle eccezioni quando l'app viene eseguita nell'ambiente di produzione. Per altre informazioni sulla configurazione degli ambienti, vedere ASP.NET Ambienti di runtime di base.
La pagina Delle eccezioni per sviluppatori può includere le informazioni seguenti sull'eccezione e sulla richiesta:
- Analisi dello stack
- Parametri della stringa di query, se presenti
- Cookie, se presenti
- Headers
- Metadati dell'endpoint, se presenti
Non è garantito che la pagina delle eccezioni per sviluppatori fornisca alcuna informazione. Usare Registrazione per informazioni complete sull'errore.
L'immagine seguente mostra una pagina di eccezioni per sviluppatori di esempio con animazione per visualizzare le schede e le informazioni visualizzate:
In risposta a una richiesta con un'intestazione Accept: text/plain , la pagina eccezioni sviluppatore restituisce testo normale anziché HTML. Per esempio:
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
Pagina del gestore di eccezioni
Per configurare una pagina di gestione degli errori personalizzata per l'ambienteProduction, chiamare UseExceptionHandler. Questo middleware per la gestione delle eccezioni:
- Rileva e registra eccezioni non gestite.
- Esegue nuovamente la richiesta in una pipeline alternativa usando il percorso indicato. La richiesta non viene eseguita nuovamente se la risposta è stata avviata. Il codice generato dal modello esegue nuovamente la richiesta usando il
/Errorpercorso.
Warning
Se la pipeline alternativa genera una propria eccezione, il middleware per la gestione delle eccezioni rilancia l'eccezione originale.
Poiché questo middleware può eseguire nuovamente la pipeline di richiesta:
- I middleware devono gestire la reentrancy con la stessa richiesta. Questo in genere significa o ripulire il proprio stato dopo aver chiamato
_nextoppure memorizzare nella cache l'elaborazione sulHttpContextper evitare di ripeterla. Nel gestire il corpo della richiesta, questo significa memorizzare temporaneamente i risultati nel buffer oppure nella cache, come fa il lettore di moduli. - Per l'overload UseExceptionHandler(IApplicationBuilder, String) usato nei modelli, viene modificato solo il percorso della richiesta e i dati della route vengono cancellati. I dati delle richieste, ad esempio intestazioni, metodo ed elementi, vengono riutilizzati così come sono.
- I servizi con ambito definito rimangono invariati.
Nell'esempio seguente, si UseExceptionHandler aggiunge il middleware per la gestione delle eccezioni in ambienti non Development.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Il Razor modello di app Pagine fornisce una pagina di errore (.cshtml) e una classe PageModel(ErrorModel) nella cartella Pages. Per un'app MVC, il modello di progetto include un Error metodo di azione e una visualizzazione Errore per il Home controller.
Il middleware di gestione delle eccezioni esegue nuovamente la richiesta usando il metodo HTTP originale . Se un endpoint del gestore errori è limitato a un set specifico di metodi HTTP, viene eseguito solo per tali metodi HTTP. Ad esempio, un'azione del controller MVC che usa l'attributo [HttpGet] viene eseguita solo per le richieste GET. Per assicurarsi che tutte le richieste raggiungano la pagina di gestione degli errori personalizzata, non limitarle a un set specifico di metodi HTTP.
Per gestire le eccezioni in modo diverso in base al metodo HTTP originale:
- Per le pagine Razor, crea più metodi handler. Ad esempio, usare
OnGetper gestire le eccezioni GET e usareOnPostper gestire le eccezioni POST. - In MVC, applicare gli attributi dei verbi HTTP a più azioni. Ad esempio, usare
[HttpGet]per gestire le eccezioni GET e usare[HttpPost]per gestire le eccezioni POST.
Per consentire agli utenti non autenticati di visualizzare la pagina di gestione degli errori personalizzata, assicurarsi che supporti l'accesso anonimo.
Accedere all'eccezione
Usare IExceptionHandlerPathFeature per accedere all'eccezione e al percorso della richiesta originale in un gestore errori. Nell'esempio seguente viene IExceptionHandlerPathFeature usato per ottenere altre informazioni sull'eccezione generata:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Lambda del gestore delle eccezioni
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di una funzione lambda consente di accedere all'errore prima di restituire la risposta.
Il codice seguente usa un'espressione lambda per la gestione delle eccezioni:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
// using static System.Net.Mime.MediaTypeNames;
context.Response.ContentType = Text.Plain;
await context.Response.WriteAsync("An exception was thrown.");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(" The file was not found.");
}
if (exceptionHandlerPathFeature?.Path == "/")
{
await context.Response.WriteAsync(" Page: Home.");
}
});
});
app.UseHsts();
}
Un altro modo per usare un'espressione lambda consiste nell'impostare il codice di stato in base al tipo di eccezione, come nell'esempio seguente:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError
});
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
IExceptionHandler
IExceptionHandler è un'interfaccia che offre allo sviluppatore un callback per la gestione delle eccezioni note in una posizione centrale. L'interfaccia contiene un singolo metodo, TryHandleAsync, che riceve un HttpContext e un Exception parametro .
IExceptionHandler le implementazioni vengono registrate chiamando IServiceCollection.AddExceptionHandler<T>. Il ciclo di vita di un'istanza IExceptionHandler è di tipo singleton. È possibile aggiungere più implementazioni e vengono chiamate nell'ordine registrato.
Il middleware per la gestione delle eccezioni esegue l'iterazione dei gestori di eccezioni registrati nell'ordine fino a quando non viene restituito true da TryHandleAsync, a indicare che l'eccezione è stata gestita. Se un gestore delle eccezioni gestisce un'eccezione, può restituire true per interrompere l'elaborazione. Se un'eccezione non viene gestita da alcun gestore di eccezioni, si torna al comportamento predefinito e alle opzioni del middleware.
A partire da .NET 10, il comportamento predefinito consiste nell'eliminare le emissioni di diagnostica, ad esempio log e metriche per le eccezioni gestite (quando TryHandleAsync restituisce true). Ciò differisce dalle versioni precedenti (.NET 8 e 9) in cui la diagnostica è sempre stata generata indipendentemente dal fatto che l'eccezione sia stata gestita. Il comportamento predefinito può essere modificato impostando SuppressDiagnosticsCallback.
L'esempio seguente illustra un'implementazione IExceptionHandler :
using Microsoft.AspNetCore.Diagnostics;
namespace ErrorHandlingSample
{
public class CustomExceptionHandler : IExceptionHandler
{
private readonly ILogger<CustomExceptionHandler> logger;
public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
{
this.logger = logger;
}
public ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
var exceptionMessage = exception.Message;
logger.LogError(
"Error Message: {exceptionMessage}, Time of occurrence {time}",
exceptionMessage, DateTime.UtcNow);
// Return false to continue with the default behavior
// - or - return true to signal that this exception is handled
return ValueTask.FromResult(false);
}
}
}
L'esempio seguente mostra come registrare un'implementazione IExceptionHandler per l'iniezione delle dipendenze:
using ErrorHandlingSample;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
builder.Services.AddExceptionHandler<CustomExceptionHandler>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// Remaining Program.cs code omitted for brevity
Quando il codice precedente viene eseguito nell'ambiente Development :
- Il
CustomExceptionHandlerviene chiamato per primo per gestire un'eccezione. - Dopo la registrazione dell'eccezione, il
TryHandleAsyncmetodo restituiscefalse, in modo che venga visualizzata la pagina delle eccezioni per gli sviluppatori.
In altri ambienti:
- Il
CustomExceptionHandlerviene chiamato per primo per gestire un'eccezione. - Dopo la registrazione dell'eccezione, il
TryHandleAsyncmetodo restituiscefalse, in modo che venga visualizzata la/Errorpagina .
SuppressDiagnosticsCallback
A partire da .NET 10, è possibile controllare se il middleware di gestione delle eccezioni scrive la diagnostica per le eccezioni gestite configurando la SuppressDiagnosticsCallback proprietà in ExceptionHandlerOptions. Questo callback riceve il contesto dell'eccezione e consente di determinare se la diagnostica deve essere eliminata in base all'eccezione o alla richiesta specifica.
Per ripristinare il comportamento di .NET 8 e 9, in cui la diagnostica viene sempre generata per le eccezioni gestite, impostare il callback per far sì che restituisca sempre false:
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => false
});
È anche possibile sopprimere in modo condizionale la diagnostica in base al tipo di eccezione o ad altro contesto:
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => context.Exception is ArgumentException
});
Quando un'eccezione non viene gestita da alcuna IExceptionHandler implementazione (tutti i gestori restituiscono falseTryHandleAsync), il controllo esegue il fallback al comportamento predefinito e alle opzioni del middleware e la diagnostica viene generata in base al comportamento standard del middleware.
UseStatusCodePages
Per impostazione predefinita, un'app ASP.NET Core non fornisce una tabella codici di stato per i codici di stato degli errori HTTP, ad esempio 404 - Non trovato. Quando l'app imposta un codice di stato di errore HTTP 400-599 che non ha un corpo, restituisce il codice di stato e un corpo della risposta vuoto. Per abilitare i gestori predefiniti di solo testo per i codici di stato di errore comuni, chiamare UseStatusCodePages in Program.cs:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Chiama UseStatusCodePages prima del middleware per la gestione delle richieste. Ad esempio, richiamare UseStatusCodePages prima del middleware per i file statici e del middleware degli endpoint.
Quando UseStatusCodePages non viene usato, passare a un URL senza un endpoint restituisce un messaggio di errore dipendente dal browser che indica che l'endpoint non è stato trovato. Quando UseStatusCodePages viene chiamato, il browser restituisce la risposta seguente:
Status Code: 404; Not Found
UseStatusCodePages non viene in genere usato nell'ambiente di produzione perché restituisce un messaggio che non è utile per gli utenti.
Note
Il middleware delle pagine dei codici di stato non intercetta le eccezioni. Per fornire una pagina personalizzata per la gestione degli errori, utilizzare la pagina del gestore delle eccezioni.
UseStatusCodePages con una stringa di formato
Per personalizzare il tipo di contenuto della risposta e il testo, usare l'overload di UseStatusCodePages che accetta un tipo di contenuto e una stringa di formato:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");
Nel codice {0} precedente è un segnaposto per il codice di errore.
UseStatusCodePages con una stringa di formato non viene in genere usata nell'ambiente di produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePages con una lambda
Per specificare la gestione degli errori personalizzata e il codice per la scrittura delle risposte, usare l'overload di UseStatusCodePages che accetta un'espressione lambda:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages(async statusCodeContext =>
{
// using static System.Net.Mime.MediaTypeNames;
statusCodeContext.HttpContext.Response.ContentType = Text.Plain;
await statusCodeContext.HttpContext.Response.WriteAsync(
$"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});
UseStatusCodePages con una funzione lambda in genere non viene usato in produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePagesWithRedirects
Metodo di estensione UseStatusCodePagesWithRedirects:
- Invia un codice di stato 302 - Trovato al client.
- Reindirizza il client all'endpoint di gestione degli errori fornito nel modello di URL. L'endpoint di gestione degli errori visualizza in genere informazioni sugli errori e restituisce HTTP 200.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Il modello di URL può includere un {0} segnaposto per il codice di stato, come illustrato nel codice precedente. Se il modello di URL inizia con ~ (tilde), ~ viene sostituito con PathBase dell'app. Quando si specifica un endpoint nell'app, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app:
- Deve reindirizzare il client a un endpoint diverso, in genere nei casi in cui un'altra app elabora l'errore. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint reindirizzato.
- Non deve conservare e restituire il codice di stato originale con la risposta di reindirizzamento iniziale.
UseStatusCodePagesWithReExecute
Metodo di estensione UseStatusCodePagesWithReExecute:
- Genera il corpo della risposta eseguendo nuovamente la pipeline delle richieste con un percorso alternativo.
- Non modifica il codice di stato prima o dopo l'esecuzione di nuovo della pipeline.
La nuova esecuzione della pipeline può modificare il codice di stato della risposta, perché la nuova pipeline ha il controllo completo del codice di stato. Se la nuova pipeline non modifica il codice di stato, il codice di stato originale verrà inviato al client.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Se all'interno dell'app è specificato un endpoint, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app deve:
- Elaborare la richiesta senza il reindirizzamento a un endpoint diverso. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint richiesto in origine.
- Conservare e restituire il codice di stato originale con la risposta.
Il modello di URL deve iniziare con / e può includere un segnaposto {0} per il codice di stato. Per passare il codice di stato come parametro di stringa di query, passare un secondo argomento in UseStatusCodePagesWithReExecute. Per esempio:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
L'endpoint che elabora l'errore può ottenere l'URL originale che ha generato l'errore, come illustrato nell'esempio seguente:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
public int OriginalStatusCode { get; set; }
public string? OriginalPathAndQuery { get; set; }
public void OnGet(int statusCode)
{
OriginalStatusCode = statusCode;
var statusCodeReExecuteFeature =
HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature is not null)
{
OriginalPathAndQuery = $"{statusCodeReExecuteFeature.OriginalPathBase}"
+ $"{statusCodeReExecuteFeature.OriginalPath}"
+ $"{statusCodeReExecuteFeature.OriginalQueryString}";
}
}
}
Poiché questo middleware può eseguire nuovamente la pipeline di richiesta:
- I middleware devono gestire la reentrancy con la stessa richiesta. Questo in genere significa o ripulire il proprio stato dopo aver chiamato
_nextoppure memorizzare nella cache l'elaborazione sulHttpContextper evitare di ripeterla. Nel gestire il corpo della richiesta, questo significa memorizzare temporaneamente i risultati nel buffer oppure nella cache, come fa il lettore di moduli. - I servizi con ambito definito rimangono invariati.
Disabilita le pagine dei codici di stato
Per disabilitare le tabelle codici di stato per un controller MVC o un metodo di azione, usare l'attributo [SkipStatusCodePages].
Per disabilitare le pagine del codice di stato per richieste specifiche in un metodo gestore di Razor Pages o in un controller MVC, usare IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Codice di gestione delle eccezioni
Il codice nelle pagine di gestione delle eccezioni può anche generare eccezioni. Le pagine di errore di produzione devono essere testate accuratamente e prestare particolare attenzione per evitare di generare eccezioni proprie.
Intestazioni di risposta
Una volta inviate le intestazioni di una risposta:
- L'app non può modificare il codice di stato della risposta.
- Non è possibile eseguire pagine o handler di eccezione. La risposta deve essere completata o la connessione deve essere interrotta.
Gestione delle eccezioni del server
Oltre alla logica di gestione delle eccezioni in un'app, l'implementazione del server HTTP può gestire alcune eccezioni. Se il server rileva un'eccezione prima dell'invio delle intestazioni di risposta, il server invia una 500 - Internal Server Error risposta senza un corpo della risposta. Se il server rileva un'eccezione dopo l'invio delle intestazioni di risposta, il server chiude la connessione. Le richieste non gestite dall'app vengono gestite dal server. Qualsiasi eccezione che si verifica quando il server sta gestendo la richiesta viene gestita dalla gestione delle eccezioni del server. Eventuali pagine di errore personalizzate dell'app, middleware di gestione delle eccezioni e filtri non hanno effetto su questo comportamento.
Gestione delle eccezioni durante l'avvio
Solo il livello di hosting può gestire le eccezioni che si verificano durante l'avvio dell'app. L'host può essere configurato per rilevare gli errori di avvio e rilevare errori dettagliati.
Il livello di hosting può visualizzare una pagina di errore per un errore di avvio acquisito solo se l'errore si verifica dopo l'associazione indirizzo host/porta. Se si verifica un errore di associazione:
- Il livello di hosting registra un'eccezione critica.
- Il processo dotnet si arresta in modo anomalo.
- Non viene visualizzata alcuna pagina di errore quando il server HTTP è Kestrel.
Se durante l'esecuzione in IIS (o servizio app di Azure) o in IIS Express il processo non può essere avviato, viene restituito l'errore 502.5 Errore del processo dal modulo ASP.NET Core. Per altre informazioni, vedere Risolvere i problemi relativi a ASP.NET Core nel servizio app Azure e IIS.
Pagina di errore del database
Il filtro AddDatabaseDeveloperPageExceptionFilter eccezioni della pagina Sviluppo database acquisisce le eccezioni correlate al database che possono essere risolte usando le migrazioni di Entity Framework Core. Quando si verificano queste eccezioni, viene generata una risposta HTML con i dettagli delle possibili azioni per risolvere il problema. Questa pagina è abilitata solo nell'ambiente Development . Il codice seguente aggiunge il filtro delle eccezioni della pagina per sviluppatori di database:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Filtri di eccezione
Nelle app MVC i filtri delle eccezioni possono essere configurati a livello globale o per ogni controller o azione singolarmente. Nelle Razor app Pages possono essere configurate a livello globale o per modello di pagina. Questi filtri gestiscono eventuali eccezioni non gestite che si verificano durante l'esecuzione di un'azione del controller o di un altro filtro. Per altre informazioni, vedere Filtri in ASP.NET Core.
I filtri delle eccezioni sono utili per intercettare le eccezioni che si verificano all'interno delle azioni MVC, ma non sono flessibili quanto il middleware di gestione delle eccezioni predefinito, UseExceptionHandler. È consigliabile usare UseExceptionHandler, a meno che non sia necessario eseguire la gestione degli errori in modo diverso in base all'azione MVC scelta.
Errori di stato del modello
Per informazioni su come gestire gli errori dello stato del modello, vedere Associazione di modelli e Convalida del modello.
Dettagli del problema
I dettagli del problema non sono l'unico formato di risposta per descrivere un errore dell'API HTTP, ma vengono comunemente usati per segnalare errori per le API HTTP.
Il servizio per i dettagli del problema implementa l'interfaccia IProblemDetailsService, che consente di creare dettagli del problema in ASP.NET Core. Il AddProblemDetails(IServiceCollection) metodo di estensione in IServiceCollection registra l'implementazione predefinita IProblemDetailsService .
Nelle app ASP.NET Core, il middleware seguente genera risposte HTTP con dettagli del problema quando viene chiamato AddProblemDetails, tranne quando l'intestazione HTTP della richiesta Accept non include uno dei tipi di contenuto supportati dall'elemento IProblemDetailsWriter registrato (predefinito: application/json):
- ExceptionHandlerMiddleware: genera una risposta ai dettagli del problema quando un gestore personalizzato non è definito.
- StatusCodePagesMiddleware: genera una risposta ai dettagli del problema per impostazione predefinita.
-
DeveloperExceptionPageMiddleware: Genera una risposta con i dettagli del problema in fase di sviluppo quando l'intestazione HTTP della richiesta
Acceptnon includetext/html.
Il codice seguente configura l'app per generare una risposta ai dettagli del problema per tutte le risposte di errore del client HTTP e del server che non hanno ancora contenuto del corpo:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Nella sezione successiva viene illustrato come personalizzare il corpo della risposta ai dettagli del problema.
Personalizzare i dettagli del problema
La creazione automatica di un ProblemDetails oggetto può essere personalizzata usando una delle opzioni seguenti:
- Utilizzare
ProblemDetailsOptions.CustomizeProblemDetails. - Usa un elemento personalizzato
IProblemDetailsWriter - Chiamare in
IProblemDetailsServiceun middleware
CustomizeProblemDetails operazione
I dettagli del problema generati possono essere personalizzati usando CustomizeProblemDetailse le personalizzazioni vengono applicate a tutti i dettagli del problema generati automaticamente.
Il codice seguente usa ProblemDetailsOptions per impostare CustomizeProblemDetails:
builder.Services.AddProblemDetails(options =>
options.CustomizeProblemDetails = ctx =>
ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Ad esempio, il risultato di un endpoint HTTP Status 400 Bad Request produce il seguente corpo della risposta con i dettagli del problema:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
personalizzato IProblemDetailsWriter
È possibile creare un'implementazione IProblemDetailsWriter per le personalizzazioni avanzate.
public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
// Indicates that only responses with StatusCode == 400
// are handled by this writer. All others are
// handled by different registered writers if available.
public bool CanWrite(ProblemDetailsContext context)
=> context.HttpContext.Response.StatusCode == 400;
public ValueTask WriteAsync(ProblemDetailsContext context)
{
// Additional customizations.
// Write to the response.
var response = context.HttpContext.Response;
return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
}
}
Nota: quando si usa un oggetto personalizzato IProblemDetailsWriter, l'oggetto personalizzato IProblemDetailsWriter deve essere registrato prima di chiamare AddRazorPages, AddControllersAddControllersWithViews, o AddMvc:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();
var app = builder.Build();
// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
await next(context);
var mathErrorFeature = context.Features.Get<MathErrorFeature>();
if (mathErrorFeature is not null)
{
if (context.RequestServices.GetService<IProblemDetailsWriter>() is
{ } problemDetailsService)
{
if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
{
(string Detail, string Type) details = mathErrorFeature.MathError switch
{
MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
"https://en.wikipedia.org/wiki/Division_by_zero"),
_ => ("Negative or complex numbers are not valid input.",
"https://en.wikipedia.org/wiki/Square_root")
};
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = "Bad Input",
Detail = details.Detail,
Type = details.Type
}
});
}
}
}
});
// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
if (denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(numerator / denominator);
});
// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(Math.Sqrt(radicand));
});
app.Run();
Dettagli del problema dal middleware
Un approccio alternativo all'uso di ProblemDetailsOptions con CustomizeProblemDetails consiste nell'impostare il ProblemDetails nel middleware. Una risposta ai dettagli del problema può essere scritta chiamando IProblemDetailsService.WriteAsync:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStatusCodePages();
// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
await next(context);
var mathErrorFeature = context.Features.Get<MathErrorFeature>();
if (mathErrorFeature is not null)
{
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
(string Detail, string Type) details = mathErrorFeature.MathError switch
{
MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
"https://en.wikipedia.org/wiki/Division_by_zero"),
_ => ("Negative or complex numbers are not valid input.",
"https://en.wikipedia.org/wiki/Square_root")
};
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = "Bad Input",
Detail = details.Detail,
Type = details.Type
}
});
}
}
});
// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
if (denominator == 0)
{
var errorType = new MathErrorFeature { MathError =
MathErrorType.DivisionByZeroError };
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(numerator / denominator);
});
// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
if (radicand < 0)
{
var errorType = new MathErrorFeature { MathError =
MathErrorType.NegativeRadicandError };
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(Math.Sqrt(radicand));
});
app.MapControllers();
app.Run();
Nel codice precedente, gli endpoint API minimali /divide e /squareroot restituiscono la risposta del problema personalizzata prevista all'input dell'errore.
Gli endpoint del controller API restituiscono la risposta del problema predefinita all'input dell'errore, non la risposta personalizzata al problema. Viene restituita la risposta predefinita relativa al problema perché il controller API ha scritto nel flusso della risposta Dettagli del problema per i codici di stato di errore prima che venga chiamato IProblemDetailsService.WriteAsync, e la risposta non viene scritta di nuovo.
Il seguente ValuesController restituisce BadRequestResult, che scrive nel flusso della risposta e pertanto impedisce la restituzione della risposta di errore personalizzata.
[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
// /api/values/divide/1/2
[HttpGet("{Numerator}/{Denominator}")]
public IActionResult Divide(double Numerator, double Denominator)
{
if (Denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
HttpContext.Features.Set(errorType);
return BadRequest();
}
return Ok(Numerator / Denominator);
}
// /api/values/squareroot/4
[HttpGet("{radicand}")]
public IActionResult Squareroot(double radicand)
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
HttpContext.Features.Set(errorType);
return BadRequest();
}
return Ok(Math.Sqrt(radicand));
}
}
Il seguente Values3Controller restituisce ControllerBase.Problem, quindi viene restituito il risultato personalizzato del problema previsto:
[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
// /api/values3/divide/1/2
[HttpGet("{Numerator}/{Denominator}")]
public IActionResult Divide(double Numerator, double Denominator)
{
if (Denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
HttpContext.Features.Set(errorType);
return Problem(
title: "Bad Input",
detail: "Divison by zero is not defined.",
type: "https://en.wikipedia.org/wiki/Division_by_zero",
statusCode: StatusCodes.Status400BadRequest
);
}
return Ok(Numerator / Denominator);
}
// /api/values3/squareroot/4
[HttpGet("{radicand}")]
public IActionResult Squareroot(double radicand)
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
HttpContext.Features.Set(errorType);
return Problem(
title: "Bad Input",
detail: "Negative or complex numbers are not valid input.",
type: "https://en.wikipedia.org/wiki/Square_root",
statusCode: StatusCodes.Status400BadRequest
);
}
return Ok(Math.Sqrt(radicand));
}
}
Generare un payload ProblemDetails per le eccezioni
Si consideri l'app seguente:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.MapControllers();
app.Run();
In ambienti non di sviluppo, quando si verifica un'eccezione, di seguito è riportata una risposta Standard ProblemDetails restituita al client:
{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}
Per la maggior parte delle app, il codice precedente è tutto ciò che è necessario per le eccezioni. Tuttavia, la sezione seguente illustra come ottenere risposte più dettagliate ai problemi.
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di un'espressione lambda consente l'accesso all'errore e la scrittura di una risposta ai dettagli del problema con IProblemDetailsService.WriteAsync:
using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = Text.Plain;
var title = "Bad Input";
var detail = "Invalid input";
var type = "https://errors.example.com/badInput";
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
var exceptionHandlerFeature =
context.Features.Get<IExceptionHandlerFeature>();
var exceptionType = exceptionHandlerFeature?.Error;
if (exceptionType != null &&
exceptionType.Message.Contains("infinity"))
{
title = "Argument exception";
detail = "Invalid input";
type = "https://errors.example.com/argumentException";
}
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = title,
Detail = detail,
Type = type
}
});
}
});
});
}
app.MapControllers();
app.Run();
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Un approccio alternativo alla generazione dei dettagli del problema consiste nell'usare il pacchetto NuGet di terze parti Hellang.Middleware.ProblemDetails che può essere usato per eseguire il mapping delle eccezioni e degli errori del client ai dettagli del problema.
Risorse aggiuntive
- Visualizzare o scaricare il codice di esempio (procedura per il download)
- Risolvere i problemi di ASP.NET Core in Servizio app di Azure e IIS
- Risoluzione di errori comuni di Servizio app di Azure e IIS con ASP.NET Core
- Gestire gli errori nelle API core di ASP.NET
-
Modifica che causa un'interruzione: la diagnostica delle eccezioni viene eliminata quando
IExceptionHandler.TryHandleAsyncrestituisce true
Questo articolo illustra gli approcci comuni alla gestione degli errori nelle app Web di ASP.NET Core. Vedere anche Gestire gli errori nelle API di base di ASP.NET.
Per Blazor indicazioni sulla gestione degli errori, che aggiungono o sostituiscono quelle fornite in questo articolo, vedere Gestire gli errori nelle app di ASP.NET Core Blazor.
Pagina delle eccezioni per gli sviluppatori
Nella pagina Eccezioni sviluppatore vengono visualizzate informazioni dettagliate sulle eccezioni di richiesta non gestite. Usa DeveloperExceptionPageMiddleware per acquisire eccezioni sincrone e asincrone dalla pipeline HTTP e per generare risposte di errore. La pagina delle eccezioni per sviluppatori interviene all'inizio della pipeline middleware, in modo da poter intercettare le eccezioni non gestite generate dal middleware successivo.
Le app ASP.NET Core attivano per impostazione predefinita la pagina delle eccezioni per sviluppatori quando sono soddisfatte entrambe le condizioni:
- Esecuzione nell'ambiente di
Development. - L'app è stata creata con i modelli correnti, ovvero usando WebApplication.CreateBuilder.
Le app create usando i modelli precedenti, ovvero usando WebHost.CreateDefaultBuilder, possono abilitare la pagina delle eccezioni per sviluppatori chiamando app.UseDeveloperExceptionPage.
Warning
Non abilitare la pagina eccezioni per sviluppatori a meno che l'app non sia in esecuzione nell'ambienteDevelopment. Non condividere pubblicamente informazioni dettagliate sulle eccezioni quando l'app viene eseguita nell'ambiente di produzione. Per altre informazioni sulla configurazione degli ambienti, vedere ASP.NET Ambienti di runtime di base.
La pagina Delle eccezioni per sviluppatori può includere le informazioni seguenti sull'eccezione e sulla richiesta:
- Analisi dello stack
- Parametri della stringa di query, se presenti
- Cookie, se presenti
- Headers
- Metadati dell'endpoint, se presenti
Non è garantito che la pagina delle eccezioni per sviluppatori fornisca alcuna informazione. Usare Registrazione per informazioni complete sull'errore.
L'immagine seguente mostra una pagina di eccezioni per sviluppatori di esempio con animazione per visualizzare le schede e le informazioni visualizzate:
In risposta a una richiesta con un'intestazione Accept: text/plain , la pagina eccezioni sviluppatore restituisce testo normale anziché HTML. Per esempio:
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
Pagina del gestore di eccezioni
Per configurare una pagina di gestione degli errori personalizzata per l'ambienteProduction, chiamare UseExceptionHandler. Questo middleware per la gestione delle eccezioni:
- Rileva e registra eccezioni non gestite.
- Esegue nuovamente la richiesta in una pipeline alternativa usando il percorso indicato. La richiesta non viene eseguita nuovamente se la risposta è stata avviata. Il codice generato dal modello esegue nuovamente la richiesta usando il
/Errorpercorso.
Warning
Se la pipeline alternativa genera una propria eccezione, il middleware di gestione delle eccezioni rilancia l'eccezione originale.
Poiché questo middleware può eseguire nuovamente la pipeline di richiesta:
- I middleware devono gestire la reentrancy con la stessa richiesta. Questo in genere significa o ripulire il proprio stato dopo aver chiamato
_nextoppure memorizzare nella cache l'elaborazione sulHttpContextper evitare di ripeterla. Nel gestire il corpo della richiesta, questo significa memorizzare temporaneamente i risultati nel buffer oppure nella cache, come fa il lettore di moduli. - Per l'overload UseExceptionHandler(IApplicationBuilder, String) usato nei modelli, viene modificato solo il percorso della richiesta e i dati della route vengono cancellati. I dati delle richieste, ad esempio intestazioni, metodo ed elementi, vengono riutilizzati così come sono.
- I servizi con ambito definito rimangono invariati.
Nell'esempio seguente, si UseExceptionHandler aggiunge il middleware per la gestione delle eccezioni in ambienti non Development.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Il Razor modello di app Pagine fornisce una pagina di errore (.cshtml) e una classe PageModel(ErrorModel) nella cartella Pages. Per un'app MVC, il modello di progetto include un Error metodo di azione e una visualizzazione Errore per il Home controller.
Il middleware di gestione delle eccezioni esegue nuovamente la richiesta usando il metodo HTTP originale . Se un endpoint del gestore errori è limitato a un set specifico di metodi HTTP, viene eseguito solo per tali metodi HTTP. Ad esempio, un'azione del controller MVC che usa l'attributo [HttpGet] viene eseguita solo per le richieste GET. Per assicurarsi che tutte le richieste raggiungano la pagina di gestione degli errori personalizzata, non limitarle a un set specifico di metodi HTTP.
Per gestire le eccezioni in modo diverso in base al metodo HTTP originale:
- Per le pagine Razor, crea più metodi handler. Ad esempio, usare
OnGetper gestire le eccezioni GET e usareOnPostper gestire le eccezioni POST. - In MVC, applicare gli attributi dei verbi HTTP a più azioni. Ad esempio, usare
[HttpGet]per gestire le eccezioni GET e usare[HttpPost]per gestire le eccezioni POST.
Per consentire agli utenti non autenticati di visualizzare la pagina di gestione degli errori personalizzata, assicurarsi che supporti l'accesso anonimo.
Accedere all'eccezione
Usare IExceptionHandlerPathFeature per accedere all'eccezione e al percorso della richiesta originale in un gestore errori. Nell'esempio seguente viene IExceptionHandlerPathFeature usato per ottenere altre informazioni sull'eccezione generata:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Lambda del gestore delle eccezioni
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di una funzione lambda consente di accedere all'errore prima di restituire la risposta.
Il codice seguente usa un'espressione lambda per la gestione delle eccezioni:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
// using static System.Net.Mime.MediaTypeNames;
context.Response.ContentType = Text.Plain;
await context.Response.WriteAsync("An exception was thrown.");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(" The file was not found.");
}
if (exceptionHandlerPathFeature?.Path == "/")
{
await context.Response.WriteAsync(" Page: Home.");
}
});
});
app.UseHsts();
}
Un altro modo per usare un'espressione lambda consiste nell'impostare il codice di stato in base al tipo di eccezione, come nell'esempio seguente:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex is TimeoutException
? StatusCodes.Status503ServiceUnavailable
: StatusCodes.Status500InternalServerError
});
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
IExceptionHandler
IExceptionHandler è un'interfaccia che offre allo sviluppatore un callback per la gestione delle eccezioni note in una posizione centrale.
IExceptionHandler le implementazioni vengono registrate chiamando IServiceCollection.AddExceptionHandler<T>. Il ciclo di vita di un'istanza IExceptionHandler è di tipo singleton. È possibile aggiungere più implementazioni e vengono chiamate nell'ordine registrato.
Se un gestore di eccezioni gestisce una richiesta, può restituire true per interrompere l'elaborazione. Se un'eccezione non viene gestita da alcun gestore di eccezioni, si torna al comportamento predefinito e alle opzioni del middleware. Metriche e log diversi vengono generati per le eccezioni gestite e non gestite.
L'esempio seguente illustra un'implementazione IExceptionHandler :
using Microsoft.AspNetCore.Diagnostics;
namespace ErrorHandlingSample
{
public class CustomExceptionHandler : IExceptionHandler
{
private readonly ILogger<CustomExceptionHandler> logger;
public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
{
this.logger = logger;
}
public ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
var exceptionMessage = exception.Message;
logger.LogError(
"Error Message: {exceptionMessage}, Time of occurrence {time}",
exceptionMessage, DateTime.UtcNow);
// Return false to continue with the default behavior
// - or - return true to signal that this exception is handled
return ValueTask.FromResult(false);
}
}
}
L'esempio seguente mostra come registrare un'implementazione IExceptionHandler per l'iniezione delle dipendenze:
using ErrorHandlingSample;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
builder.Services.AddExceptionHandler<CustomExceptionHandler>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// Remaining Program.cs code omitted for brevity
Quando il codice precedente viene eseguito nell'ambiente Development :
- Il
CustomExceptionHandlerviene chiamato per primo per gestire un'eccezione. - Dopo la registrazione dell'eccezione, il
TryHandleAsyncmetodo restituiscefalse, in modo che venga visualizzata la pagina delle eccezioni per gli sviluppatori.
In altri ambienti:
- Il
CustomExceptionHandlerviene chiamato per primo per gestire un'eccezione. - Dopo la registrazione dell'eccezione, il
TryHandleAsyncmetodo restituiscefalse, in modo che venga visualizzata la/Errorpagina .
UseStatusCodePages
Per impostazione predefinita, un'app ASP.NET Core non fornisce una tabella codici di stato per i codici di stato degli errori HTTP, ad esempio 404 - Non trovato. Quando l'app imposta un codice di stato di errore HTTP 400-599 che non ha un corpo, restituisce il codice di stato e un corpo della risposta vuoto. Per abilitare i gestori predefiniti di solo testo per i codici di stato di errore comuni, chiamare UseStatusCodePages in Program.cs:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Chiama UseStatusCodePages prima del middleware per la gestione delle richieste. Ad esempio, chiama UseStatusCodePages prima del middleware per i file statici e del middleware degli endpoint.
Quando UseStatusCodePages non viene usato, passare a un URL senza un endpoint restituisce un messaggio di errore dipendente dal browser che indica che l'endpoint non è stato trovato. Quando UseStatusCodePages viene chiamato, il browser restituisce la risposta seguente:
Status Code: 404; Not Found
UseStatusCodePages non viene in genere usato nell'ambiente di produzione perché restituisce un messaggio che non è utile per gli utenti.
Note
Il middleware delle pagine dei codici di stato non intercetta le eccezioni. Per fornire una pagina personalizzata per la gestione degli errori, utilizzare la pagina del gestore delle eccezioni.
UseStatusCodePages con una stringa di formato
Per personalizzare il tipo di contenuto della risposta e il testo, usare l'overload di UseStatusCodePages che accetta un tipo di contenuto e una stringa di formato:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");
Nel codice {0} precedente è un segnaposto per il codice di errore.
UseStatusCodePages con una stringa di formato non viene in genere usata nell'ambiente di produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePages con una lambda
Per specificare la gestione degli errori personalizzata e il codice per la scrittura delle risposte, usare l'overload di UseStatusCodePages che accetta un'espressione lambda:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages(async statusCodeContext =>
{
// using static System.Net.Mime.MediaTypeNames;
statusCodeContext.HttpContext.Response.ContentType = Text.Plain;
await statusCodeContext.HttpContext.Response.WriteAsync(
$"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});
UseStatusCodePages con una funzione lambda in genere non viene usato in produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePagesWithRedirects
Metodo di estensione UseStatusCodePagesWithRedirects:
- Invia un codice di stato 302 - Trovato al client.
- Reindirizza il client all'endpoint di gestione degli errori fornito nel modello di URL. L'endpoint di gestione degli errori visualizza in genere informazioni sugli errori e restituisce HTTP 200.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Il modello di URL può includere un {0} segnaposto per il codice di stato, come illustrato nel codice precedente. Se il modello di URL inizia con ~ (tilde), ~ viene sostituito con PathBase dell'app. Quando si specifica un endpoint nell'app, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app:
- Deve reindirizzare il client a un endpoint diverso, in genere nei casi in cui un'altra app elabora l'errore. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint reindirizzato.
- Non deve conservare e restituire il codice di stato originale con la risposta di reindirizzamento iniziale.
UseStatusCodePagesWithReExecute
Metodo di estensione UseStatusCodePagesWithReExecute:
- Genera il corpo della risposta eseguendo nuovamente la pipeline delle richieste con un percorso alternativo.
- Non modifica il codice di stato prima o dopo l'esecuzione di nuovo della pipeline.
La nuova esecuzione della pipeline può modificare il codice di stato della risposta, perché la nuova pipeline ha il controllo completo del codice di stato. Se la nuova pipeline non modifica il codice di stato, il codice di stato originale verrà inviato al client.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Se all'interno dell'app è specificato un endpoint, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app deve:
- Elaborare la richiesta senza il reindirizzamento a un endpoint diverso. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint richiesto in origine.
- Conservare e restituire il codice di stato originale con la risposta.
Il modello di URL deve iniziare con / e può includere un segnaposto {0} per il codice di stato. Per passare il codice di stato come parametro di stringa di query, passare un secondo argomento in UseStatusCodePagesWithReExecute. Per esempio:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
L'endpoint che elabora l'errore può ottenere l'URL originale che ha generato l'errore, come illustrato nell'esempio seguente:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
public int OriginalStatusCode { get; set; }
public string? OriginalPathAndQuery { get; set; }
public void OnGet(int statusCode)
{
OriginalStatusCode = statusCode;
var statusCodeReExecuteFeature =
HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature is not null)
{
OriginalPathAndQuery = $"{statusCodeReExecuteFeature.OriginalPathBase}"
+ $"{statusCodeReExecuteFeature.OriginalPath}"
+ $"{statusCodeReExecuteFeature.OriginalQueryString}";
}
}
}
Poiché questo middleware può eseguire nuovamente la pipeline di richiesta:
- I middleware devono gestire la reentrancy con la stessa richiesta. Questo in genere significa o ripulire il proprio stato dopo aver chiamato
_nextoppure memorizzare nella cache l'elaborazione sulHttpContextper evitare di ripeterla. Nel gestire il corpo della richiesta, questo significa memorizzare temporaneamente i risultati nel buffer oppure nella cache, come fa il lettore di moduli. - I servizi con ambito definito rimangono invariati.
Disabilita le pagine dei codici di stato
Per disabilitare le tabelle codici di stato per un controller MVC o un metodo di azione, usare l'attributo [SkipStatusCodePages].
Per disabilitare le pagine del codice di stato per richieste specifiche in un metodo gestore di Razor Pages o in un controller MVC, usare IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Codice di gestione delle eccezioni
Il codice nelle pagine di gestione delle eccezioni può anche generare eccezioni. Le pagine di errore di produzione devono essere testate accuratamente e prestare particolare attenzione per evitare di generare eccezioni proprie.
Intestazioni di risposta
Una volta inviate le intestazioni di una risposta:
- L'app non può modificare il codice di stato della risposta.
- Non è possibile eseguire pagine o handler di eccezione. La risposta deve essere completata o la connessione deve essere interrotta.
Gestione delle eccezioni del server
Oltre alla logica di gestione delle eccezioni in un'app, l'implementazione del server HTTP può gestire alcune eccezioni. Se il server rileva un'eccezione prima dell'invio delle intestazioni di risposta, il server invia una 500 - Internal Server Error risposta senza un corpo della risposta. Se il server rileva un'eccezione dopo l'invio delle intestazioni di risposta, il server chiude la connessione. Le richieste non gestite dall'app vengono gestite dal server. Qualsiasi eccezione che si verifica quando il server sta gestendo la richiesta viene gestita dalla gestione delle eccezioni del server. Eventuali pagine di errore personalizzate dell'app, middleware di gestione delle eccezioni e filtri non hanno effetto su questo comportamento.
Gestione delle eccezioni durante l'avvio
Solo il livello di hosting può gestire le eccezioni che si verificano durante l'avvio dell'app. L'host può essere configurato per rilevare gli errori di avvio e rilevare errori dettagliati.
Il livello di hosting può visualizzare una pagina di errore per un errore di avvio acquisito solo se l'errore si verifica dopo l'associazione indirizzo host/porta. Se si verifica un errore di associazione:
- Il livello di hosting registra un'eccezione critica.
- Il processo dotnet si arresta in modo anomalo.
- Non viene visualizzata alcuna pagina di errore quando il server HTTP è Kestrel.
Se durante l'esecuzione in IIS (o servizio app di Azure) o in IIS Express il processo non può essere avviato, viene restituito l'errore 502.5 Errore del processo dal modulo ASP.NET Core. Per altre informazioni, vedere Risolvere i problemi relativi a ASP.NET Core nel servizio app Azure e IIS.
Pagina di errore del database
Il filtro AddDatabaseDeveloperPageExceptionFilter eccezioni della pagina Sviluppo database acquisisce le eccezioni correlate al database che possono essere risolte usando le migrazioni di Entity Framework Core. Quando si verificano queste eccezioni, viene generata una risposta HTML con i dettagli delle possibili azioni per risolvere il problema. Questa pagina è abilitata solo nell'ambiente Development . Il codice seguente aggiunge il filtro delle eccezioni della pagina per sviluppatori di database:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Filtri di eccezione
Nelle app MVC i filtri delle eccezioni possono essere configurati a livello globale o per ogni controller o azione singolarmente. Nelle Razor app Pages possono essere configurate a livello globale o per modello di pagina. Questi filtri gestiscono eventuali eccezioni non gestite che si verificano durante l'esecuzione di un'azione del controller o di un altro filtro. Per altre informazioni, vedere Filtri in ASP.NET Core.
I filtri delle eccezioni sono utili per intercettare le eccezioni che si verificano all'interno delle azioni MVC, ma non sono flessibili quanto il middleware di gestione delle eccezioni predefinito, UseExceptionHandler. È consigliabile usare UseExceptionHandler, a meno che non sia necessario eseguire la gestione degli errori in modo diverso in base all'azione MVC scelta.
Errori di stato del modello
Per informazioni su come gestire gli errori dello stato del modello, vedere Associazione di modelli e Convalida del modello.
Dettagli del problema
I dettagli del problema non sono l'unico formato di risposta per descrivere un errore dell'API HTTP, ma vengono comunemente usati per segnalare errori per le API HTTP.
Il servizio per i dettagli del problema implementa l'interfaccia IProblemDetailsService, che consente di creare dettagli del problema in ASP.NET Core. Il AddProblemDetails(IServiceCollection) metodo di estensione in IServiceCollection registra l'implementazione predefinita IProblemDetailsService .
Nelle app ASP.NET Core, il middleware seguente genera risposte HTTP con dettagli del problema quando viene chiamato AddProblemDetails, tranne quando l'intestazione HTTP della richiesta Accept non include uno dei tipi di contenuto supportati dall'elemento IProblemDetailsWriter registrato (predefinito: application/json):
- ExceptionHandlerMiddleware: genera una risposta ai dettagli del problema quando un gestore personalizzato non è definito.
- StatusCodePagesMiddleware: genera una risposta ai dettagli del problema per impostazione predefinita.
-
DeveloperExceptionPageMiddleware: Genera una risposta con i dettagli del problema in fase di sviluppo quando l'intestazione HTTP della richiesta
Acceptnon includetext/html.
Il codice seguente configura l'app per generare una risposta ai dettagli del problema per tutte le risposte di errore del client HTTP e del server che non hanno ancora contenuto del corpo:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Nella sezione successiva viene illustrato come personalizzare il corpo della risposta ai dettagli del problema.
Personalizzare i dettagli del problema
La creazione automatica di un ProblemDetails oggetto può essere personalizzata usando una delle opzioni seguenti:
- Utilizzare
ProblemDetailsOptions.CustomizeProblemDetails. - Usa un elemento personalizzato
IProblemDetailsWriter - Chiamare in
IProblemDetailsServiceun middleware
CustomizeProblemDetails operazione
I dettagli del problema generati possono essere personalizzati usando CustomizeProblemDetailse le personalizzazioni vengono applicate a tutti i dettagli del problema generati automaticamente.
Il codice seguente usa ProblemDetailsOptions per impostare CustomizeProblemDetails:
builder.Services.AddProblemDetails(options =>
options.CustomizeProblemDetails = ctx =>
ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Ad esempio, il risultato di un endpoint HTTP Status 400 Bad Request produce il seguente corpo della risposta con i dettagli del problema:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
personalizzato IProblemDetailsWriter
È possibile creare un'implementazione IProblemDetailsWriter per le personalizzazioni avanzate.
public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
// Indicates that only responses with StatusCode == 400
// are handled by this writer. All others are
// handled by different registered writers if available.
public bool CanWrite(ProblemDetailsContext context)
=> context.HttpContext.Response.StatusCode == 400;
public ValueTask WriteAsync(ProblemDetailsContext context)
{
// Additional customizations.
// Write to the response.
var response = context.HttpContext.Response;
return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
}
}
Nota: quando si usa un oggetto personalizzato IProblemDetailsWriter, l'oggetto personalizzato IProblemDetailsWriter deve essere registrato prima di chiamare AddRazorPages, AddControllersAddControllersWithViews, o AddMvc:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();
var app = builder.Build();
// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
await next(context);
var mathErrorFeature = context.Features.Get<MathErrorFeature>();
if (mathErrorFeature is not null)
{
if (context.RequestServices.GetService<IProblemDetailsWriter>() is
{ } problemDetailsService)
{
if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
{
(string Detail, string Type) details = mathErrorFeature.MathError switch
{
MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
"https://en.wikipedia.org/wiki/Division_by_zero"),
_ => ("Negative or complex numbers are not valid input.",
"https://en.wikipedia.org/wiki/Square_root")
};
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = "Bad Input",
Detail = details.Detail,
Type = details.Type
}
});
}
}
}
});
// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
if (denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(numerator / denominator);
});
// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(Math.Sqrt(radicand));
});
app.Run();
Dettagli del problema dal middleware
Un approccio alternativo all'uso di ProblemDetailsOptions con CustomizeProblemDetails consiste nell'impostare il ProblemDetails nel middleware. Una risposta ai dettagli del problema può essere scritta chiamando IProblemDetailsService.WriteAsync:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStatusCodePages();
// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
await next(context);
var mathErrorFeature = context.Features.Get<MathErrorFeature>();
if (mathErrorFeature is not null)
{
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
(string Detail, string Type) details = mathErrorFeature.MathError switch
{
MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
"https://en.wikipedia.org/wiki/Division_by_zero"),
_ => ("Negative or complex numbers are not valid input.",
"https://en.wikipedia.org/wiki/Square_root")
};
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = "Bad Input",
Detail = details.Detail,
Type = details.Type
}
});
}
}
});
// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
if (denominator == 0)
{
var errorType = new MathErrorFeature { MathError =
MathErrorType.DivisionByZeroError };
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(numerator / denominator);
});
// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
if (radicand < 0)
{
var errorType = new MathErrorFeature { MathError =
MathErrorType.NegativeRadicandError };
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(Math.Sqrt(radicand));
});
app.MapControllers();
app.Run();
Nel codice precedente, gli endpoint API minimali /divide e /squareroot restituiscono la risposta del problema personalizzata prevista all'input dell'errore.
Gli endpoint del controller API restituiscono la risposta del problema predefinita all'input dell'errore, non la risposta personalizzata al problema. Viene restituita la risposta predefinita relativa al problema perché il controller API ha scritto nel flusso della risposta Dettagli del problema per i codici di stato di errore prima che venga chiamato IProblemDetailsService.WriteAsync, e la risposta non viene scritta di nuovo.
Il seguente ValuesController restituisce BadRequestResult, che scrive nel flusso della risposta e pertanto impedisce la restituzione della risposta di errore personalizzata.
[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
// /api/values/divide/1/2
[HttpGet("{Numerator}/{Denominator}")]
public IActionResult Divide(double Numerator, double Denominator)
{
if (Denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
HttpContext.Features.Set(errorType);
return BadRequest();
}
return Ok(Numerator / Denominator);
}
// /api/values/squareroot/4
[HttpGet("{radicand}")]
public IActionResult Squareroot(double radicand)
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
HttpContext.Features.Set(errorType);
return BadRequest();
}
return Ok(Math.Sqrt(radicand));
}
}
Il seguente Values3Controller restituisce ControllerBase.Problem, quindi viene restituito il risultato personalizzato del problema previsto:
[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
// /api/values3/divide/1/2
[HttpGet("{Numerator}/{Denominator}")]
public IActionResult Divide(double Numerator, double Denominator)
{
if (Denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
HttpContext.Features.Set(errorType);
return Problem(
title: "Bad Input",
detail: "Divison by zero is not defined.",
type: "https://en.wikipedia.org/wiki/Division_by_zero",
statusCode: StatusCodes.Status400BadRequest
);
}
return Ok(Numerator / Denominator);
}
// /api/values3/squareroot/4
[HttpGet("{radicand}")]
public IActionResult Squareroot(double radicand)
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
HttpContext.Features.Set(errorType);
return Problem(
title: "Bad Input",
detail: "Negative or complex numbers are not valid input.",
type: "https://en.wikipedia.org/wiki/Square_root",
statusCode: StatusCodes.Status400BadRequest
);
}
return Ok(Math.Sqrt(radicand));
}
}
Generare un payload ProblemDetails per le eccezioni
Si consideri l'app seguente:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.MapControllers();
app.Run();
In ambienti non di sviluppo, quando si verifica un'eccezione, di seguito è riportata una risposta Standard ProblemDetails restituita al client:
{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}
Per la maggior parte delle app, il codice precedente è tutto ciò che è necessario per le eccezioni. Tuttavia, la sezione seguente illustra come ottenere risposte più dettagliate ai problemi.
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di un'espressione lambda consente l'accesso all'errore e la scrittura di una risposta ai dettagli del problema con IProblemDetailsService.WriteAsync:
using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = Text.Plain;
var title = "Bad Input";
var detail = "Invalid input";
var type = "https://errors.example.com/badInput";
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
var exceptionHandlerFeature =
context.Features.Get<IExceptionHandlerFeature>();
var exceptionType = exceptionHandlerFeature?.Error;
if (exceptionType != null &&
exceptionType.Message.Contains("infinity"))
{
title = "Argument exception";
detail = "Invalid input";
type = "https://errors.example.com/argumentException";
}
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = title,
Detail = detail,
Type = type
}
});
}
});
});
}
app.MapControllers();
app.Run();
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Un approccio alternativo alla generazione dei dettagli del problema consiste nell'usare il pacchetto NuGet di terze parti Hellang.Middleware.ProblemDetails che può essere usato per eseguire il mapping delle eccezioni e degli errori del client ai dettagli del problema.
Risorse aggiuntive
Questo articolo illustra gli approcci comuni alla gestione degli errori nelle app Web di ASP.NET Core. Vedere anche Gestire gli errori nelle API di base di ASP.NET.
Pagina delle eccezioni per gli sviluppatori
Nella pagina Eccezioni sviluppatore vengono visualizzate informazioni dettagliate sulle eccezioni di richiesta non gestite. Le app ASP.NET Core attivano per impostazione predefinita la pagina delle eccezioni per sviluppatori quando sono soddisfatte entrambe le condizioni:
- Esecuzione nell'ambiente di
Development. - App creata con i modelli correnti, ovvero usando WebApplication.CreateBuilder. Le app create usando
WebHost.CreateDefaultBuilderdevono abilitare la pagina delle eccezioni per sviluppatori chiamandoapp.UseDeveloperExceptionPageinConfigure.
La pagina delle eccezioni per sviluppatori interviene all'inizio della pipeline middleware, in modo da poter intercettare le eccezioni non gestite generate dal middleware successivo.
Le informazioni dettagliate sulle eccezioni non devono essere visualizzate pubblicamente quando l'app viene eseguita nell'ambiente Production . Per altre informazioni sulla configurazione degli ambienti, vedere ASP.NET Ambienti di runtime di base.
La pagina Delle eccezioni per sviluppatori può includere le informazioni seguenti sull'eccezione e sulla richiesta:
- Analisi dello stack
- Parametri della stringa di query, se presenti
- Cookie, se presenti
- Headers
Non è garantito che la pagina delle eccezioni per sviluppatori fornisca alcuna informazione. Usare Registrazione per informazioni complete sull'errore.
Pagina del gestore di eccezioni
Per configurare una pagina di gestione degli errori personalizzata per l'ambienteProduction, chiamare UseExceptionHandler. Questo middleware per la gestione delle eccezioni:
- Rileva e registra eccezioni non gestite.
- Esegue nuovamente la richiesta in una pipeline alternativa usando il percorso indicato. La richiesta non viene eseguita nuovamente se la risposta è stata avviata. Il codice generato dal modello esegue nuovamente la richiesta usando il
/Errorpercorso.
Warning
Se la pipeline alternativa solleva una propria eccezione, il middleware per la gestione delle eccezioni rilancia l'eccezione originale.
Poiché questo middleware può eseguire nuovamente la pipeline di richiesta:
- I middleware devono gestire la reentrancy con la stessa richiesta. Questo in genere significa o ripulire il proprio stato dopo aver chiamato
_nextoppure memorizzare nella cache l'elaborazione sulHttpContextper evitare di ripeterla. Nel gestire il corpo della richiesta, questo significa memorizzare temporaneamente i risultati nel buffer oppure nella cache, come fa il lettore di moduli. - Per l'overload UseExceptionHandler(IApplicationBuilder, String) usato nei modelli, viene modificato solo il percorso della richiesta e i dati della route vengono cancellati. I dati delle richieste, ad esempio intestazioni, metodo ed elementi, vengono riutilizzati così come sono.
- I servizi con ambito definito rimangono invariati.
Nell'esempio seguente, si UseExceptionHandler aggiunge il middleware per la gestione delle eccezioni in ambienti non Development.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Il Razor modello di app Pagine fornisce una pagina di errore (.cshtml) e una classe PageModel(ErrorModel) nella cartella Pages. Per un'app MVC, il modello di progetto include un Error metodo di azione e una visualizzazione Errore per il Home controller.
Il middleware di gestione delle eccezioni esegue nuovamente la richiesta usando il metodo HTTP originale . Se un endpoint del gestore errori è limitato a un set specifico di metodi HTTP, viene eseguito solo per tali metodi HTTP. Ad esempio, un'azione del controller MVC che usa l'attributo [HttpGet] viene eseguita solo per le richieste GET. Per assicurarsi che tutte le richieste raggiungano la pagina di gestione degli errori personalizzata, non limitarle a un set specifico di metodi HTTP.
Per gestire le eccezioni in modo diverso in base al metodo HTTP originale:
- Per le pagine Razor, crea più metodi handler. Ad esempio, usare
OnGetper gestire le eccezioni GET e usareOnPostper gestire le eccezioni POST. - In MVC, applicare gli attributi dei verbi HTTP a più azioni. Ad esempio, usare
[HttpGet]per gestire le eccezioni GET e usare[HttpPost]per gestire le eccezioni POST.
Per consentire agli utenti non autenticati di visualizzare la pagina di gestione degli errori personalizzata, assicurarsi che supporti l'accesso anonimo.
Accedere all'eccezione
Usare IExceptionHandlerPathFeature per accedere all'eccezione e al percorso della richiesta originale in un gestore errori. Nell'esempio seguente viene IExceptionHandlerPathFeature usato per ottenere altre informazioni sull'eccezione generata:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Lambda del gestore delle eccezioni
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di una funzione lambda consente di accedere all'errore prima di restituire la risposta.
Il codice seguente usa un'espressione lambda per la gestione delle eccezioni:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
// using static System.Net.Mime.MediaTypeNames;
context.Response.ContentType = Text.Plain;
await context.Response.WriteAsync("An exception was thrown.");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(" The file was not found.");
}
if (exceptionHandlerPathFeature?.Path == "/")
{
await context.Response.WriteAsync(" Page: Home.");
}
});
});
app.UseHsts();
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
IExceptionHandler
IExceptionHandler è un'interfaccia che fornisce allo sviluppatore un callback per la gestione delle eccezioni note in una posizione centrale.
IExceptionHandler implementazioni vengono registrate chiamando IServiceCollection.AddExceptionHandler<T> [IServiceCollection.AddExceptionHandler<T>]. Il ciclo di vita di un'istanza IExceptionHandler è di tipo singleton. È possibile aggiungere più implementazioni e vengono chiamate nell'ordine registrato.
Se un gestore di eccezioni gestisce una richiesta, può restituire true per interrompere l'elaborazione. Se un'eccezione non viene gestita da alcun gestore di eccezioni, si torna al comportamento predefinito e alle opzioni del middleware. Metriche e log diversi vengono generati per le eccezioni gestite e non gestite.
L'esempio seguente illustra un'implementazione IExceptionHandler :
using Microsoft.AspNetCore.Diagnostics;
namespace ErrorHandlingSample
{
public class CustomExceptionHandler : IExceptionHandler
{
private readonly ILogger<CustomExceptionHandler> logger;
public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
{
this.logger = logger;
}
public ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
var exceptionMessage = exception.Message;
logger.LogError(
"Error Message: {exceptionMessage}, Time of occurrence {time}",
exceptionMessage, DateTime.UtcNow);
// Return false to continue with the default behavior
// - or - return true to signal that this exception is handled
return ValueTask.FromResult(false);
}
}
}
L'esempio seguente mostra come registrare un'implementazione IExceptionHandler per l'iniezione delle dipendenze:
using ErrorHandlingSample;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
builder.Services.AddExceptionHandler<CustomExceptionHandler>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// Remaining Program.cs code omitted for brevity
Quando il codice precedente viene eseguito nell'ambiente Development :
- Il
CustomExceptionHandlerviene chiamato per primo per gestire un'eccezione. - Dopo la registrazione dell'eccezione, il
TryHandleExceptionmetodo restituiscefalse, in modo che venga visualizzata la pagina delle eccezioni per gli sviluppatori.
In altri ambienti:
- Il
CustomExceptionHandlerviene chiamato per primo per gestire un'eccezione. - Dopo la registrazione dell'eccezione, il
TryHandleExceptionmetodo restituiscefalse, in modo che venga visualizzata la/Errorpagina .
UseStatusCodePages
Per impostazione predefinita, un'app ASP.NET Core non fornisce una tabella codici di stato per i codici di stato degli errori HTTP, ad esempio 404 - Non trovato. Quando l'app imposta un codice di stato di errore HTTP 400-599 che non ha un corpo, restituisce il codice di stato e un corpo della risposta vuoto. Per abilitare i gestori predefiniti di solo testo per i codici di stato di errore comuni, chiamare UseStatusCodePages in Program.cs:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Chiama UseStatusCodePages prima del middleware per la gestione delle richieste. Ad esempio, chiama UseStatusCodePages prima del middleware per i file statici e del middleware degli endpoint.
Quando UseStatusCodePages non viene usato, passare a un URL senza un endpoint restituisce un messaggio di errore dipendente dal browser che indica che l'endpoint non è stato trovato. Quando UseStatusCodePages viene chiamato, il browser restituisce la risposta seguente:
Status Code: 404; Not Found
UseStatusCodePages non viene in genere usato nell'ambiente di produzione perché restituisce un messaggio che non è utile per gli utenti.
Note
Il middleware delle pagine dei codici di stato non intercetta le eccezioni. Per fornire una pagina personalizzata per la gestione degli errori, utilizzare la pagina del gestore delle eccezioni.
UseStatusCodePages con una stringa di formato
Per personalizzare il tipo di contenuto della risposta e il testo, usare l'overload di UseStatusCodePages che accetta un tipo di contenuto e una stringa di formato:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");
Nel codice {0} precedente è un segnaposto per il codice di errore.
UseStatusCodePages con una stringa di formato non viene in genere usata nell'ambiente di produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePages con una lambda
Per specificare la gestione degli errori personalizzata e il codice per la scrittura delle risposte, usare l'overload di UseStatusCodePages che accetta un'espressione lambda:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages(async statusCodeContext =>
{
// using static System.Net.Mime.MediaTypeNames;
statusCodeContext.HttpContext.Response.ContentType = Text.Plain;
await statusCodeContext.HttpContext.Response.WriteAsync(
$"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});
UseStatusCodePages con una funzione lambda in genere non viene usato in produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePagesWithRedirects
Metodo di estensione UseStatusCodePagesWithRedirects:
- Invia un codice di stato 302 - Trovato al client.
- Reindirizza il client all'endpoint di gestione degli errori fornito nel modello di URL. L'endpoint di gestione degli errori visualizza in genere informazioni sugli errori e restituisce HTTP 200.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Il modello di URL può includere un {0} segnaposto per il codice di stato, come illustrato nel codice precedente. Se il modello di URL inizia con ~ (tilde), ~ viene sostituito con PathBase dell'app. Quando si specifica un endpoint nell'app, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app:
- Deve reindirizzare il client a un endpoint diverso, in genere nei casi in cui un'altra app elabora l'errore. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint reindirizzato.
- Non deve conservare e restituire il codice di stato originale con la risposta di reindirizzamento iniziale.
UseStatusCodePagesWithReExecute
Metodo di estensione UseStatusCodePagesWithReExecute:
- Genera il corpo della risposta eseguendo nuovamente la pipeline delle richieste con un percorso alternativo.
- Non modifica il codice di stato prima o dopo l'esecuzione di nuovo della pipeline.
La nuova esecuzione della pipeline può modificare il codice di stato della risposta, perché la nuova pipeline ha il controllo completo del codice di stato. Se la nuova pipeline non modifica il codice di stato, il codice di stato originale verrà inviato al client.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Se all'interno dell'app è specificato un endpoint, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app deve:
- Elaborare la richiesta senza il reindirizzamento a un endpoint diverso. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint richiesto in origine.
- Conservare e restituire il codice di stato originale con la risposta.
Il modello di URL deve iniziare con / e può includere un segnaposto {0} per il codice di stato. Per passare il codice di stato come parametro di stringa di query, passare un secondo argomento in UseStatusCodePagesWithReExecute. Per esempio:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
L'endpoint che elabora l'errore può ottenere l'URL originale che ha generato l'errore, come illustrato nell'esempio seguente:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
public int OriginalStatusCode { get; set; }
public string? OriginalPathAndQuery { get; set; }
public void OnGet(int statusCode)
{
OriginalStatusCode = statusCode;
var statusCodeReExecuteFeature =
HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature is not null)
{
OriginalPathAndQuery = $"{statusCodeReExecuteFeature.OriginalPathBase}"
+ $"{statusCodeReExecuteFeature.OriginalPath}"
+ $"{statusCodeReExecuteFeature.OriginalQueryString}";
}
}
}
Poiché questo middleware può eseguire nuovamente la pipeline di richiesta:
- I middleware devono gestire la reentrancy con la stessa richiesta. Questo in genere significa o ripulire il proprio stato dopo aver chiamato
_nextoppure memorizzare nella cache l'elaborazione sulHttpContextper evitare di ripeterla. Nel gestire il corpo della richiesta, questo significa memorizzare temporaneamente i risultati nel buffer oppure nella cache, come fa il lettore di moduli. - I servizi con ambito definito rimangono invariati.
Disabilita le pagine dei codici di stato
Per disabilitare le tabelle codici di stato per un controller MVC o un metodo di azione, usare l'attributo [SkipStatusCodePages].
Per disabilitare le pagine del codice di stato per richieste specifiche in un metodo gestore di Razor Pages o in un controller MVC, usare IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Codice di gestione delle eccezioni
Il codice nelle pagine di gestione delle eccezioni può anche generare eccezioni. Le pagine di errore di produzione devono essere testate accuratamente e prestare particolare attenzione per evitare di generare eccezioni proprie.
Intestazioni di risposta
Una volta inviate le intestazioni di una risposta:
- L'app non può modificare il codice di stato della risposta.
- Non è possibile eseguire pagine o handler di eccezione. La risposta deve essere completata o la connessione deve essere interrotta.
Gestione delle eccezioni del server
Oltre alla logica di gestione delle eccezioni in un'app, l'implementazione del server HTTP può gestire alcune eccezioni. Se il server rileva un'eccezione prima dell'invio delle intestazioni di risposta, il server invia una 500 - Internal Server Error risposta senza un corpo della risposta. Se il server rileva un'eccezione dopo l'invio delle intestazioni di risposta, il server chiude la connessione. Le richieste non gestite dall'app vengono gestite dal server. Qualsiasi eccezione che si verifica quando il server sta gestendo la richiesta viene gestita dalla gestione delle eccezioni del server. Eventuali pagine di errore personalizzate dell'app, middleware di gestione delle eccezioni e filtri non hanno effetto su questo comportamento.
Gestione delle eccezioni durante l'avvio
Solo il livello di hosting può gestire le eccezioni che si verificano durante l'avvio dell'app. L'host può essere configurato per rilevare gli errori di avvio e rilevare errori dettagliati.
Il livello di hosting può visualizzare una pagina di errore per un errore di avvio acquisito solo se l'errore si verifica dopo l'associazione indirizzo host/porta. Se si verifica un errore di associazione:
- Il livello di hosting registra un'eccezione critica.
- Il processo dotnet si arresta in modo anomalo.
- Non viene visualizzata alcuna pagina di errore quando il server HTTP è Kestrel.
Se durante l'esecuzione in IIS (o servizio app di Azure) o in IIS Express il processo non può essere avviato, viene restituito l'errore 502.5 Errore del processo dal modulo ASP.NET Core. Per altre informazioni, vedere Risolvere i problemi relativi a ASP.NET Core nel servizio app Azure e IIS.
Pagina di errore del database
Il filtro AddDatabaseDeveloperPageExceptionFilter eccezioni della pagina Sviluppo database acquisisce le eccezioni correlate al database che possono essere risolte usando le migrazioni di Entity Framework Core. Quando si verificano queste eccezioni, viene generata una risposta HTML con i dettagli delle possibili azioni per risolvere il problema. Questa pagina è abilitata solo nell'ambiente Development . Il codice seguente aggiunge il filtro delle eccezioni della pagina per sviluppatori di database:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Filtri di eccezione
Nelle app MVC i filtri delle eccezioni possono essere configurati a livello globale o per ogni controller o azione singolarmente. Nelle Razor app Pages possono essere configurate a livello globale o per modello di pagina. Questi filtri gestiscono eventuali eccezioni non gestite che si verificano durante l'esecuzione di un'azione del controller o di un altro filtro. Per altre informazioni, vedere Filtri in ASP.NET Core.
I filtri delle eccezioni sono utili per intercettare le eccezioni che si verificano all'interno delle azioni MVC, ma non sono flessibili quanto il middleware di gestione delle eccezioni predefinito, UseExceptionHandler. È consigliabile usare UseExceptionHandler, a meno che non sia necessario eseguire la gestione degli errori in modo diverso in base all'azione MVC scelta.
Errori di stato del modello
Per informazioni su come gestire gli errori dello stato del modello, vedere Associazione di modelli e Convalida del modello.
Dettagli del problema
I dettagli del problema non sono l'unico formato di risposta per descrivere un errore dell'API HTTP, ma vengono comunemente usati per segnalare errori per le API HTTP.
Il servizio per i dettagli del problema implementa l'interfaccia IProblemDetailsService, che consente di creare dettagli del problema in ASP.NET Core. Il AddProblemDetails(IServiceCollection) metodo di estensione in IServiceCollection registra l'implementazione predefinita IProblemDetailsService .
Nelle app ASP.NET Core, il middleware seguente genera risposte HTTP con dettagli del problema quando viene chiamato AddProblemDetails, tranne quando l'intestazione HTTP della richiesta Accept non include uno dei tipi di contenuto supportati dall'elemento IProblemDetailsWriter registrato (predefinito: application/json):
- ExceptionHandlerMiddleware: genera una risposta ai dettagli del problema quando un gestore personalizzato non è definito.
- StatusCodePagesMiddleware: genera una risposta ai dettagli del problema per impostazione predefinita.
-
DeveloperExceptionPageMiddleware: Genera una risposta con i dettagli del problema in fase di sviluppo quando l'intestazione HTTP della richiesta
Acceptnon includetext/html.
Il codice seguente configura l'app per generare una risposta ai dettagli del problema per tutte le risposte di errore del client HTTP e del server che non hanno ancora un contenuto del corpo:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Nella sezione successiva viene illustrato come personalizzare il corpo della risposta ai dettagli del problema.
Personalizzare i dettagli del problema
La creazione automatica di un ProblemDetails oggetto può essere personalizzata usando una delle opzioni seguenti:
- Utilizzare
ProblemDetailsOptions.CustomizeProblemDetails. - Usa un elemento personalizzato
IProblemDetailsWriter - Chiamare in
IProblemDetailsServiceun middleware
CustomizeProblemDetails operazione
I dettagli del problema generati possono essere personalizzati usando CustomizeProblemDetailse le personalizzazioni vengono applicate a tutti i dettagli del problema generati automaticamente.
Il codice seguente usa ProblemDetailsOptions per impostare CustomizeProblemDetails:
builder.Services.AddProblemDetails(options =>
options.CustomizeProblemDetails = ctx =>
ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Ad esempio, il risultato di un endpoint HTTP Status 400 Bad Request produce il seguente corpo della risposta con i dettagli del problema:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
personalizzato IProblemDetailsWriter
È possibile creare un'implementazione IProblemDetailsWriter per le personalizzazioni avanzate.
public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
// Indicates that only responses with StatusCode == 400
// are handled by this writer. All others are
// handled by different registered writers if available.
public bool CanWrite(ProblemDetailsContext context)
=> context.HttpContext.Response.StatusCode == 400;
public ValueTask WriteAsync(ProblemDetailsContext context)
{
// Additional customizations.
// Write to the response.
var response = context.HttpContext.Response;
return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
}
}
Nota: quando si usa un oggetto personalizzato IProblemDetailsWriter, l'oggetto personalizzato IProblemDetailsWriter deve essere registrato prima di chiamare AddRazorPages, AddControllersAddControllersWithViews, o AddMvc:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();
var app = builder.Build();
// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
await next(context);
var mathErrorFeature = context.Features.Get<MathErrorFeature>();
if (mathErrorFeature is not null)
{
if (context.RequestServices.GetService<IProblemDetailsWriter>() is
{ } problemDetailsService)
{
if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
{
(string Detail, string Type) details = mathErrorFeature.MathError switch
{
MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
"https://en.wikipedia.org/wiki/Division_by_zero"),
_ => ("Negative or complex numbers are not valid input.",
"https://en.wikipedia.org/wiki/Square_root")
};
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = "Bad Input",
Detail = details.Detail,
Type = details.Type
}
});
}
}
}
});
// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
if (denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(numerator / denominator);
});
// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(Math.Sqrt(radicand));
});
app.Run();
Dettagli del problema dal middleware
Un approccio alternativo all'uso di ProblemDetailsOptions con CustomizeProblemDetails consiste nell'impostare il ProblemDetails nel middleware. Una risposta ai dettagli del problema può essere scritta chiamando IProblemDetailsService.WriteAsync:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStatusCodePages();
// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
await next(context);
var mathErrorFeature = context.Features.Get<MathErrorFeature>();
if (mathErrorFeature is not null)
{
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
(string Detail, string Type) details = mathErrorFeature.MathError switch
{
MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
"https://en.wikipedia.org/wiki/Division_by_zero"),
_ => ("Negative or complex numbers are not valid input.",
"https://en.wikipedia.org/wiki/Square_root")
};
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = "Bad Input",
Detail = details.Detail,
Type = details.Type
}
});
}
}
});
// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
if (denominator == 0)
{
var errorType = new MathErrorFeature { MathError =
MathErrorType.DivisionByZeroError };
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(numerator / denominator);
});
// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
if (radicand < 0)
{
var errorType = new MathErrorFeature { MathError =
MathErrorType.NegativeRadicandError };
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(Math.Sqrt(radicand));
});
app.MapControllers();
app.Run();
Nel codice precedente, gli endpoint API minimali /divide e /squareroot restituiscono la risposta del problema personalizzata prevista all'input dell'errore.
Gli endpoint del controller API restituiscono la risposta del problema predefinita all'input dell'errore, non la risposta personalizzata al problema. Viene restituita la risposta predefinita relativa al problema perché il controller API ha scritto nel flusso della risposta Dettagli del problema per i codici di stato di errore prima che venga chiamato IProblemDetailsService.WriteAsync, e la risposta non viene scritta di nuovo.
Il seguente ValuesController restituisce BadRequestResult, che scrive nel flusso della risposta e pertanto impedisce la restituzione della risposta di errore personalizzata.
[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
// /api/values/divide/1/2
[HttpGet("{Numerator}/{Denominator}")]
public IActionResult Divide(double Numerator, double Denominator)
{
if (Denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
HttpContext.Features.Set(errorType);
return BadRequest();
}
return Ok(Numerator / Denominator);
}
// /api/values/squareroot/4
[HttpGet("{radicand}")]
public IActionResult Squareroot(double radicand)
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
HttpContext.Features.Set(errorType);
return BadRequest();
}
return Ok(Math.Sqrt(radicand));
}
}
Il seguente Values3Controller restituisce ControllerBase.Problem, quindi viene restituito il risultato personalizzato del problema previsto:
[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
// /api/values3/divide/1/2
[HttpGet("{Numerator}/{Denominator}")]
public IActionResult Divide(double Numerator, double Denominator)
{
if (Denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
HttpContext.Features.Set(errorType);
return Problem(
title: "Bad Input",
detail: "Divison by zero is not defined.",
type: "https://en.wikipedia.org/wiki/Division_by_zero",
statusCode: StatusCodes.Status400BadRequest
);
}
return Ok(Numerator / Denominator);
}
// /api/values3/squareroot/4
[HttpGet("{radicand}")]
public IActionResult Squareroot(double radicand)
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
HttpContext.Features.Set(errorType);
return Problem(
title: "Bad Input",
detail: "Negative or complex numbers are not valid input.",
type: "https://en.wikipedia.org/wiki/Square_root",
statusCode: StatusCodes.Status400BadRequest
);
}
return Ok(Math.Sqrt(radicand));
}
}
Generare un payload ProblemDetails per le eccezioni
Si consideri l'app seguente:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.MapControllers();
app.Run();
In ambienti non di sviluppo, quando si verifica un'eccezione, di seguito è riportata una risposta Standard ProblemDetails restituita al client:
{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}
Per la maggior parte delle app, il codice precedente è tutto ciò che è necessario per le eccezioni. Tuttavia, la sezione seguente illustra come ottenere risposte più dettagliate ai problemi.
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di un'espressione lambda consente l'accesso all'errore e la scrittura di una risposta ai dettagli del problema con IProblemDetailsService.WriteAsync:
using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = Text.Plain;
var title = "Bad Input";
var detail = "Invalid input";
var type = "https://errors.example.com/badInput";
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
var exceptionHandlerFeature =
context.Features.Get<IExceptionHandlerFeature>();
var exceptionType = exceptionHandlerFeature?.Error;
if (exceptionType != null &&
exceptionType.Message.Contains("infinity"))
{
title = "Argument exception";
detail = "Invalid input";
type = "https://errors.example.com/argumentException";
}
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = title,
Detail = detail,
Type = type
}
});
}
});
});
}
app.MapControllers();
app.Run();
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Un approccio alternativo alla generazione dei dettagli del problema consiste nell'usare il pacchetto NuGet di terze parti Hellang.Middleware.ProblemDetails che può essere usato per eseguire il mapping delle eccezioni e degli errori del client ai dettagli del problema.
Risorse aggiuntive
- Visualizzare o scaricare il codice di esempio (procedura per il download)
- Risolvere i problemi di ASP.NET Core in Servizio app di Azure e IIS
- Risoluzione di errori comuni di Servizio app di Azure e IIS con ASP.NET Core
- Gestire gli errori nelle API core di ASP.NET
- Gestire gli errori nelle API core ASP.NET.
Questo articolo illustra gli approcci comuni alla gestione degli errori nelle app Web di ASP.NET Core. Vedere anche Gestire gli errori nelle API di base di ASP.NET.
Pagina delle eccezioni per gli sviluppatori
Nella pagina Eccezioni sviluppatore vengono visualizzate informazioni dettagliate sulle eccezioni di richiesta non gestite. Le app ASP.NET Core attivano per impostazione predefinita la pagina delle eccezioni per sviluppatori quando sono soddisfatte entrambe le condizioni:
- Esecuzione nell'ambiente di
Development. - App creata con i modelli correnti, ovvero usando WebApplication.CreateBuilder. Le app create usando
WebHost.CreateDefaultBuilderdevono abilitare la pagina delle eccezioni per sviluppatori chiamandoapp.UseDeveloperExceptionPageinConfigure.
La pagina delle eccezioni per sviluppatori interviene all'inizio della pipeline middleware, in modo da poter intercettare le eccezioni non gestite generate dal middleware successivo.
Le informazioni dettagliate sulle eccezioni non devono essere visualizzate pubblicamente quando l'app viene eseguita nell'ambiente Production . Per altre informazioni sulla configurazione degli ambienti, vedere ASP.NET Ambienti di runtime di base.
La pagina Delle eccezioni per sviluppatori può includere le informazioni seguenti sull'eccezione e sulla richiesta:
- Analisi dello stack
- Parametri della stringa di query, se presenti
- Cookie, se presenti
- Headers
Non è garantito che la pagina delle eccezioni per sviluppatori fornisca alcuna informazione. Usare Registrazione per informazioni complete sull'errore.
Pagina del gestore di eccezioni
Per configurare una pagina di gestione degli errori personalizzata per l'ambienteProduction, chiamare UseExceptionHandler. Questo middleware per la gestione delle eccezioni:
- Rileva e registra eccezioni non gestite.
- Esegue nuovamente la richiesta in una pipeline alternativa usando il percorso indicato. La richiesta non viene eseguita nuovamente se la risposta è stata avviata. Il codice generato dal modello esegue nuovamente la richiesta usando il
/Errorpercorso.
Warning
Se la pipeline alternativa solleva una propria eccezione, il middleware di gestione delle eccezioni rilancia l'eccezione originale.
Poiché questo middleware può eseguire nuovamente la pipeline di richiesta:
- I middleware devono gestire la reentrancy con la stessa richiesta. Questo in genere significa o ripulire il proprio stato dopo aver chiamato
_nextoppure memorizzare nella cache l'elaborazione sulHttpContextper evitare di ripeterla. Nel gestire il corpo della richiesta, questo significa memorizzare temporaneamente i risultati nel buffer oppure nella cache, come fa il lettore di moduli. - Per l'overload UseExceptionHandler(IApplicationBuilder, String) usato nei modelli, viene modificato solo il percorso della richiesta e i dati della route vengono cancellati. I dati delle richieste, ad esempio intestazioni, metodo ed elementi, vengono riutilizzati così come sono.
- I servizi con ambito definito rimangono invariati.
Nell'esempio seguente, si UseExceptionHandler aggiunge il middleware per la gestione delle eccezioni in ambienti non Development.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Il Razor modello di app Pagine fornisce una pagina di errore (.cshtml) e una classe PageModel(ErrorModel) nella cartella Pages. Per un'app MVC, il modello di progetto include un Error metodo di azione e una visualizzazione Errore per il Home controller.
Il middleware di gestione delle eccezioni esegue nuovamente la richiesta usando il metodo HTTP originale . Se un endpoint del gestore errori è limitato a un set specifico di metodi HTTP, viene eseguito solo per tali metodi HTTP. Ad esempio, un'azione del controller MVC che usa l'attributo [HttpGet] viene eseguita solo per le richieste GET. Per assicurarsi che tutte le richieste raggiungano la pagina di gestione degli errori personalizzata, non limitarle a un set specifico di metodi HTTP.
Per gestire le eccezioni in modo diverso in base al metodo HTTP originale:
- Per le pagine Razor, crea più metodi handler. Ad esempio, usare
OnGetper gestire le eccezioni GET e usareOnPostper gestire le eccezioni POST. - In MVC, applicare gli attributi dei verbi HTTP a più azioni. Ad esempio, usare
[HttpGet]per gestire le eccezioni GET e usare[HttpPost]per gestire le eccezioni POST.
Per consentire agli utenti non autenticati di visualizzare la pagina di gestione degli errori personalizzata, assicurarsi che supporti l'accesso anonimo.
Accedere all'eccezione
Usare IExceptionHandlerPathFeature per accedere all'eccezione e al percorso della richiesta originale in un gestore errori. Nell'esempio seguente viene IExceptionHandlerPathFeature usato per ottenere altre informazioni sull'eccezione generata:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Lambda del gestore delle eccezioni
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di una funzione lambda consente di accedere all'errore prima di restituire la risposta.
Il codice seguente usa un'espressione lambda per la gestione delle eccezioni:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
// using static System.Net.Mime.MediaTypeNames;
context.Response.ContentType = Text.Plain;
await context.Response.WriteAsync("An exception was thrown.");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(" The file was not found.");
}
if (exceptionHandlerPathFeature?.Path == "/")
{
await context.Response.WriteAsync(" Page: Home.");
}
});
});
app.UseHsts();
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
UseStatusCodePages
Per impostazione predefinita, un'app ASP.NET Core non fornisce una tabella codici di stato per i codici di stato degli errori HTTP, ad esempio 404 - Non trovato. Quando l'app imposta un codice di stato di errore HTTP 400-599 che non ha un corpo, restituisce il codice di stato e un corpo della risposta vuoto. Per abilitare i gestori predefiniti di solo testo per i codici di stato di errore comuni, chiamare UseStatusCodePages in Program.cs:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Chiama UseStatusCodePages prima del middleware per la gestione delle richieste. Ad esempio, richiamare UseStatusCodePages prima del middleware per i file statici e del middleware per gli endpoint.
Quando UseStatusCodePages non viene usato, passare a un URL senza un endpoint restituisce un messaggio di errore dipendente dal browser che indica che l'endpoint non è stato trovato. Quando UseStatusCodePages viene chiamato, il browser restituisce la risposta seguente:
Status Code: 404; Not Found
UseStatusCodePages non viene in genere usato nell'ambiente di produzione perché restituisce un messaggio che non è utile per gli utenti.
Note
Il middleware delle pagine dei codici di stato non intercetta le eccezioni. Per fornire una pagina personalizzata per la gestione degli errori, utilizzare la pagina del gestore delle eccezioni.
UseStatusCodePages con una stringa di formato
Per personalizzare il tipo di contenuto della risposta e il testo, usare l'overload di UseStatusCodePages che accetta un tipo di contenuto e una stringa di formato:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");
Nel codice {0} precedente è un segnaposto per il codice di errore.
UseStatusCodePages con una stringa di formato non viene in genere usata nell'ambiente di produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePages con una lambda
Per specificare la gestione degli errori personalizzata e il codice per la scrittura delle risposte, usare l'overload di UseStatusCodePages che accetta un'espressione lambda:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages(async statusCodeContext =>
{
// using static System.Net.Mime.MediaTypeNames;
statusCodeContext.HttpContext.Response.ContentType = Text.Plain;
await statusCodeContext.HttpContext.Response.WriteAsync(
$"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});
UseStatusCodePages con una funzione lambda in genere non viene usato in produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePagesWithRedirects
Metodo di estensione UseStatusCodePagesWithRedirects:
- Invia un codice di stato 302 - Trovato al client.
- Reindirizza il client all'endpoint di gestione degli errori fornito nel modello di URL. L'endpoint di gestione degli errori visualizza in genere informazioni sugli errori e restituisce HTTP 200.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Il modello di URL può includere un {0} segnaposto per il codice di stato, come illustrato nel codice precedente. Se il modello di URL inizia con ~ (tilde), ~ viene sostituito con PathBase dell'app. Quando si specifica un endpoint nell'app, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app:
- Deve reindirizzare il client a un endpoint diverso, in genere nei casi in cui un'altra app elabora l'errore. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint reindirizzato.
- Non deve conservare e restituire il codice di stato originale con la risposta di reindirizzamento iniziale.
UseStatusCodePagesWithReExecute
Metodo di estensione UseStatusCodePagesWithReExecute:
- Genera il corpo della risposta eseguendo nuovamente la pipeline delle richieste con un percorso alternativo.
- Non modifica il codice di stato prima o dopo l'esecuzione di nuovo della pipeline.
La nuova esecuzione della pipeline può modificare il codice di stato della risposta, perché la nuova pipeline ha il controllo completo del codice di stato. Se la nuova pipeline non modifica il codice di stato, il codice di stato originale verrà inviato al client.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Se all'interno dell'app è specificato un endpoint, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app deve:
- Elaborare la richiesta senza il reindirizzamento a un endpoint diverso. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint richiesto in origine.
- Conservare e restituire il codice di stato originale con la risposta.
Il modello di URL deve iniziare con / e può includere un segnaposto {0} per il codice di stato. Per passare il codice di stato come parametro di stringa di query, passare un secondo argomento in UseStatusCodePagesWithReExecute. Per esempio:
var app = builder.Build();
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
L'endpoint che elabora l'errore può ottenere l'URL originale che ha generato l'errore, come illustrato nell'esempio seguente:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
public int OriginalStatusCode { get; set; }
public string? OriginalPathAndQuery { get; set; }
public void OnGet(int statusCode)
{
OriginalStatusCode = statusCode;
var statusCodeReExecuteFeature =
HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature is not null)
{
OriginalPathAndQuery = $"{statusCodeReExecuteFeature.OriginalPathBase}"
+ $"{statusCodeReExecuteFeature.OriginalPath}"
+ $"{statusCodeReExecuteFeature.OriginalQueryString}";
}
}
}
Poiché questo middleware può eseguire nuovamente la pipeline di richiesta:
- I middleware devono gestire la reentrancy con la stessa richiesta. Questo in genere significa o ripulire il proprio stato dopo aver chiamato
_nextoppure memorizzare nella cache l'elaborazione sulHttpContextper evitare di ripeterla. Nel gestire il corpo della richiesta, questo significa memorizzare temporaneamente i risultati nel buffer oppure nella cache, come fa il lettore di moduli. - I servizi con ambito definito rimangono invariati.
Disabilita le pagine dei codici di stato
Per disabilitare le tabelle codici di stato per un controller MVC o un metodo di azione, usare l'attributo [SkipStatusCodePages].
Per disabilitare le pagine del codice di stato per richieste specifiche in un metodo gestore di Razor Pages o in un controller MVC, usare IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Codice di gestione delle eccezioni
Il codice nelle pagine di gestione delle eccezioni può anche generare eccezioni. Le pagine di errore di produzione devono essere testate accuratamente e prestare particolare attenzione per evitare di generare eccezioni proprie.
Intestazioni di risposta
Una volta inviate le intestazioni di una risposta:
- L'app non può modificare il codice di stato della risposta.
- Non è possibile eseguire pagine o handler di eccezione. La risposta deve essere completata o la connessione deve essere interrotta.
Gestione delle eccezioni del server
Oltre alla logica di gestione delle eccezioni in un'app, l'implementazione del server HTTP può gestire alcune eccezioni. Se il server rileva un'eccezione prima dell'invio delle intestazioni di risposta, il server invia una 500 - Internal Server Error risposta senza un corpo della risposta. Se il server rileva un'eccezione dopo l'invio delle intestazioni di risposta, il server chiude la connessione. Le richieste non gestite dall'app vengono gestite dal server. Qualsiasi eccezione che si verifica quando il server sta gestendo la richiesta viene gestita dalla gestione delle eccezioni del server. Eventuali pagine di errore personalizzate dell'app, middleware di gestione delle eccezioni e filtri non hanno effetto su questo comportamento.
Gestione delle eccezioni durante l'avvio
Solo il livello di hosting può gestire le eccezioni che si verificano durante l'avvio dell'app. L'host può essere configurato per rilevare gli errori di avvio e rilevare errori dettagliati.
Il livello di hosting può visualizzare una pagina di errore per un errore di avvio acquisito solo se l'errore si verifica dopo l'associazione indirizzo host/porta. Se si verifica un errore di associazione:
- Il livello di hosting registra un'eccezione critica.
- Il processo dotnet si arresta in modo anomalo.
- Non viene visualizzata alcuna pagina di errore quando il server HTTP è Kestrel.
Se durante l'esecuzione in IIS (o servizio app di Azure) o in IIS Express il processo non può essere avviato, viene restituito l'errore 502.5 Errore del processo dal modulo ASP.NET Core. Per altre informazioni, vedere Risolvere i problemi relativi a ASP.NET Core nel servizio app Azure e IIS.
Pagina di errore del database
Il filtro AddDatabaseDeveloperPageExceptionFilter eccezioni della pagina Sviluppo database acquisisce le eccezioni correlate al database che possono essere risolte usando le migrazioni di Entity Framework Core. Quando si verificano queste eccezioni, viene generata una risposta HTML con i dettagli delle possibili azioni per risolvere il problema. Questa pagina è abilitata solo nell'ambiente Development . Il codice seguente aggiunge il filtro delle eccezioni della pagina per sviluppatori di database:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Filtri di eccezione
Nelle app MVC i filtri delle eccezioni possono essere configurati a livello globale o per ogni controller o azione singolarmente. Nelle Razor app Pages possono essere configurate a livello globale o per modello di pagina. Questi filtri gestiscono eventuali eccezioni non gestite che si verificano durante l'esecuzione di un'azione del controller o di un altro filtro. Per altre informazioni, vedere Filtri in ASP.NET Core.
I filtri delle eccezioni sono utili per intercettare le eccezioni che si verificano all'interno delle azioni MVC, ma non sono flessibili quanto il middleware di gestione delle eccezioni predefinito, UseExceptionHandler. È consigliabile usare UseExceptionHandler, a meno che non sia necessario eseguire la gestione degli errori in modo diverso in base all'azione MVC scelta.
Errori di stato del modello
Per informazioni su come gestire gli errori dello stato del modello, vedere Associazione di modelli e Convalida del modello.
Dettagli del problema
I dettagli del problema non sono l'unico formato di risposta per descrivere un errore dell'API HTTP, ma vengono comunemente usati per segnalare errori per le API HTTP.
Il servizio per i dettagli del problema implementa l'interfaccia IProblemDetailsService, che consente di creare dettagli del problema in ASP.NET Core. Il AddProblemDetails(IServiceCollection) metodo di estensione in IServiceCollection registra l'implementazione predefinita IProblemDetailsService .
Nelle app ASP.NET Core, il middleware seguente genera risposte HTTP con dettagli del problema quando viene chiamato AddProblemDetails, tranne quando l'intestazione HTTP della richiesta Accept non include uno dei tipi di contenuto supportati dall'elemento IProblemDetailsWriter registrato (predefinito: application/json):
- ExceptionHandlerMiddleware: genera una risposta ai dettagli del problema quando un gestore personalizzato non è definito.
- StatusCodePagesMiddleware: genera una risposta ai dettagli del problema per impostazione predefinita.
-
DeveloperExceptionPageMiddleware: Genera una risposta con i dettagli del problema in fase di sviluppo quando l'intestazione HTTP della richiesta
Acceptnon includetext/html.
Il codice seguente configura l'app per generare una risposta ai dettagli del problema per tutte le risposte di errore del client HTTP e del server che non hanno ancora un contenuto del corpo:
builder.Services.AddProblemDetails();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Nella sezione successiva viene illustrato come personalizzare il corpo della risposta ai dettagli del problema.
Personalizzare i dettagli del problema
La creazione automatica di un ProblemDetails oggetto può essere personalizzata usando una delle opzioni seguenti:
- Utilizzare
ProblemDetailsOptions.CustomizeProblemDetails. - Usa un elemento personalizzato
IProblemDetailsWriter - Chiamare in
IProblemDetailsServiceun middleware
CustomizeProblemDetails operazione
I dettagli del problema generati possono essere personalizzati usando CustomizeProblemDetailse le personalizzazioni vengono applicate a tutti i dettagli del problema generati automaticamente.
Il codice seguente usa ProblemDetailsOptions per impostare CustomizeProblemDetails:
builder.Services.AddProblemDetails(options =>
options.CustomizeProblemDetails = ctx =>
ctx.ProblemDetails.Extensions.Add("nodeId", Environment.MachineName));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseStatusCodePages();
Ad esempio, il risultato di un endpoint HTTP Status 400 Bad Request produce il seguente corpo della risposta con i dettagli del problema:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"nodeId": "my-machine-name"
}
personalizzato IProblemDetailsWriter
È possibile creare un'implementazione IProblemDetailsWriter per le personalizzazioni avanzate.
public class SampleProblemDetailsWriter : IProblemDetailsWriter
{
// Indicates that only responses with StatusCode == 400
// are handled by this writer. All others are
// handled by different registered writers if available.
public bool CanWrite(ProblemDetailsContext context)
=> context.HttpContext.Response.StatusCode == 400;
public ValueTask WriteAsync(ProblemDetailsContext context)
{
// Additional customizations.
// Write to the response.
var response = context.HttpContext.Response;
return new ValueTask(response.WriteAsJsonAsync(context.ProblemDetails));
}
}
Nota: quando si usa un oggetto personalizzato IProblemDetailsWriter, l'oggetto personalizzato IProblemDetailsWriter deve essere registrato prima di chiamare AddRazorPages, AddControllersAddControllersWithViews, o AddMvc:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IProblemDetailsWriter, SampleProblemDetailsWriter>();
var app = builder.Build();
// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
await next(context);
var mathErrorFeature = context.Features.Get<MathErrorFeature>();
if (mathErrorFeature is not null)
{
if (context.RequestServices.GetService<IProblemDetailsWriter>() is
{ } problemDetailsService)
{
if (problemDetailsService.CanWrite(new ProblemDetailsContext() { HttpContext = context }))
{
(string Detail, string Type) details = mathErrorFeature.MathError switch
{
MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
"https://en.wikipedia.org/wiki/Division_by_zero"),
_ => ("Negative or complex numbers are not valid input.",
"https://en.wikipedia.org/wiki/Square_root")
};
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = "Bad Input",
Detail = details.Detail,
Type = details.Type
}
});
}
}
}
});
// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
if (denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(numerator / denominator);
});
// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(Math.Sqrt(radicand));
});
app.Run();
Dettagli del problema dal middleware
Un approccio alternativo all'uso di ProblemDetailsOptions con CustomizeProblemDetails consiste nell'impostare il ProblemDetails nel middleware. Una risposta ai dettagli del problema può essere scritta chiamando IProblemDetailsService.WriteAsync:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStatusCodePages();
// Middleware to handle writing problem details to the response.
app.Use(async (context, next) =>
{
await next(context);
var mathErrorFeature = context.Features.Get<MathErrorFeature>();
if (mathErrorFeature is not null)
{
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
(string Detail, string Type) details = mathErrorFeature.MathError switch
{
MathErrorType.DivisionByZeroError => ("Divison by zero is not defined.",
"https://en.wikipedia.org/wiki/Division_by_zero"),
_ => ("Negative or complex numbers are not valid input.",
"https://en.wikipedia.org/wiki/Square_root")
};
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = "Bad Input",
Detail = details.Detail,
Type = details.Type
}
});
}
}
});
// /divide?numerator=2&denominator=4
app.MapGet("/divide", (HttpContext context, double numerator, double denominator) =>
{
if (denominator == 0)
{
var errorType = new MathErrorFeature { MathError =
MathErrorType.DivisionByZeroError };
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(numerator / denominator);
});
// /squareroot?radicand=16
app.MapGet("/squareroot", (HttpContext context, double radicand) =>
{
if (radicand < 0)
{
var errorType = new MathErrorFeature { MathError =
MathErrorType.NegativeRadicandError };
context.Features.Set(errorType);
return Results.BadRequest();
}
return Results.Ok(Math.Sqrt(radicand));
});
app.MapControllers();
app.Run();
Nel codice precedente, gli endpoint API minimali /divide e /squareroot restituiscono la risposta del problema personalizzata prevista all'input dell'errore.
Gli endpoint del controller API restituiscono la risposta del problema predefinita all'input dell'errore, non la risposta personalizzata al problema. Viene restituita la risposta predefinita relativa al problema perché il controller API ha scritto nel flusso della risposta Dettagli del problema per i codici di stato di errore prima che venga chiamato IProblemDetailsService.WriteAsync, e la risposta non viene scritta di nuovo.
Il seguente ValuesController restituisce BadRequestResult, che scrive nel flusso della risposta e pertanto impedisce la restituzione della risposta di errore personalizzata.
[Route("api/[controller]/[action]")]
[ApiController]
public class ValuesController : ControllerBase
{
// /api/values/divide/1/2
[HttpGet("{Numerator}/{Denominator}")]
public IActionResult Divide(double Numerator, double Denominator)
{
if (Denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
HttpContext.Features.Set(errorType);
return BadRequest();
}
return Ok(Numerator / Denominator);
}
// /api/values/squareroot/4
[HttpGet("{radicand}")]
public IActionResult Squareroot(double radicand)
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
HttpContext.Features.Set(errorType);
return BadRequest();
}
return Ok(Math.Sqrt(radicand));
}
}
Il seguente Values3Controller restituisce ControllerBase.Problem, quindi viene restituito il risultato personalizzato del problema previsto:
[Route("api/[controller]/[action]")]
[ApiController]
public class Values3Controller : ControllerBase
{
// /api/values3/divide/1/2
[HttpGet("{Numerator}/{Denominator}")]
public IActionResult Divide(double Numerator, double Denominator)
{
if (Denominator == 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.DivisionByZeroError
};
HttpContext.Features.Set(errorType);
return Problem(
title: "Bad Input",
detail: "Divison by zero is not defined.",
type: "https://en.wikipedia.org/wiki/Division_by_zero",
statusCode: StatusCodes.Status400BadRequest
);
}
return Ok(Numerator / Denominator);
}
// /api/values3/squareroot/4
[HttpGet("{radicand}")]
public IActionResult Squareroot(double radicand)
{
if (radicand < 0)
{
var errorType = new MathErrorFeature
{
MathError = MathErrorType.NegativeRadicandError
};
HttpContext.Features.Set(errorType);
return Problem(
title: "Bad Input",
detail: "Negative or complex numbers are not valid input.",
type: "https://en.wikipedia.org/wiki/Square_root",
statusCode: StatusCodes.Status400BadRequest
);
}
return Ok(Math.Sqrt(radicand));
}
}
Generare un payload ProblemDetails per le eccezioni
Si consideri l'app seguente:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.MapControllers();
app.Run();
In ambienti non di sviluppo, quando si verifica un'eccezione, di seguito è riportata una risposta Standard ProblemDetails restituita al client:
{
"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1",
"title":"An error occurred while processing your request.",
"status":500,"traceId":"00-b644<snip>-00"
}
Per la maggior parte delle app, il codice precedente è tutto ciò che è necessario per le eccezioni. Tuttavia, la sezione seguente illustra come ottenere risposte più dettagliate ai problemi.
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di un'espressione lambda consente l'accesso all'errore e la scrittura di una risposta ai dettagli del problema con IProblemDetailsService.WriteAsync:
using Microsoft.AspNetCore.Diagnostics;
using static System.Net.Mime.MediaTypeNames;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
context.Response.ContentType = Text.Plain;
var title = "Bad Input";
var detail = "Invalid input";
var type = "https://errors.example.com/badInput";
if (context.RequestServices.GetService<IProblemDetailsService>() is
{ } problemDetailsService)
{
var exceptionHandlerFeature =
context.Features.Get<IExceptionHandlerFeature>();
var exceptionType = exceptionHandlerFeature?.Error;
if (exceptionType != null &&
exceptionType.Message.Contains("infinity"))
{
title = "Argument exception";
detail = "Invalid input";
type = "https://errors.example.com/argumentException";
}
await problemDetailsService.WriteAsync(new ProblemDetailsContext
{
HttpContext = context,
ProblemDetails =
{
Title = title,
Detail = detail,
Type = type
}
});
}
});
});
}
app.MapControllers();
app.Run();
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Un approccio alternativo alla generazione dei dettagli del problema consiste nell'usare il pacchetto NuGet di terze parti Hellang.Middleware.ProblemDetails che può essere usato per eseguire il mapping delle eccezioni e degli errori del client ai dettagli del problema.
Risorse aggiuntive
Questo articolo illustra gli approcci comuni alla gestione degli errori nelle app Web di ASP.NET Core. Vedere Gestire gli errori nelle API di base ASP.NET per le API Web.
Pagina delle eccezioni per gli sviluppatori
Nella pagina Eccezioni sviluppatore vengono visualizzate informazioni dettagliate sulle eccezioni di richiesta non gestite. Le app ASP.NET Core attivano per impostazione predefinita la pagina delle eccezioni per sviluppatori quando sono soddisfatte entrambe le condizioni:
- Esecuzione nell'ambiente di
Development. - App creata con i modelli correnti, ovvero usando WebApplication.CreateBuilder. Le app create usando
WebHost.CreateDefaultBuilderdevono abilitare la pagina delle eccezioni per sviluppatori chiamandoapp.UseDeveloperExceptionPageinConfigure.
La pagina delle eccezioni per sviluppatori interviene all'inizio della pipeline middleware, in modo da poter intercettare le eccezioni non gestite generate dal middleware successivo.
Le informazioni dettagliate sulle eccezioni non devono essere visualizzate pubblicamente quando l'app viene eseguita nell'ambiente Production . Per altre informazioni sulla configurazione degli ambienti, vedere ASP.NET Ambienti di runtime di base.
La pagina Delle eccezioni per sviluppatori può includere le informazioni seguenti sull'eccezione e sulla richiesta:
- Analisi dello stack
- Parametri della stringa di query, se presenti
- Cookie, se presenti
- Headers
Non è garantito che la pagina delle eccezioni per sviluppatori fornisca alcuna informazione. Usare Registrazione per informazioni complete sull'errore.
Pagina del gestore di eccezioni
Per configurare una pagina di gestione degli errori personalizzata per l'ambienteProduction, chiamare UseExceptionHandler. Questo middleware per la gestione delle eccezioni:
- Rileva e registra eccezioni non gestite.
- Esegue nuovamente la richiesta in una pipeline alternativa usando il percorso indicato. La richiesta non viene eseguita nuovamente se la risposta è stata avviata. Il codice generato dal modello esegue nuovamente la richiesta usando il
/Errorpercorso.
Warning
Se la pipeline alternativa genera un'eccezione specifica, il middleware per la gestione delle eccezioni genera nuovamente l'eccezione originale.
Nell'esempio seguente, si UseExceptionHandler aggiunge il middleware per la gestione delle eccezioni in ambienti non Development.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Il Razor modello di app Pagine fornisce una pagina di errore (.cshtml) e una classe PageModel(ErrorModel) nella cartella Pages. Per un'app MVC, il modello di progetto include un Error metodo di azione e una visualizzazione Errore per il Home controller.
Il middleware di gestione delle eccezioni esegue nuovamente la richiesta usando il metodo HTTP originale . Se un endpoint del gestore errori è limitato a un set specifico di metodi HTTP, viene eseguito solo per tali metodi HTTP. Ad esempio, un'azione del controller MVC che usa l'attributo [HttpGet] viene eseguita solo per le richieste GET. Per assicurarsi che tutte le richieste raggiungano la pagina di gestione degli errori personalizzata, non limitarle a un set specifico di metodi HTTP.
Per gestire le eccezioni in modo diverso in base al metodo HTTP originale:
- Per le pagine Razor, crea più metodi handler. Ad esempio, usare
OnGetper gestire le eccezioni GET e usareOnPostper gestire le eccezioni POST. - In MVC, applicare gli attributi dei verbi HTTP a più azioni. Ad esempio, usare
[HttpGet]per gestire le eccezioni GET e usare[HttpPost]per gestire le eccezioni POST.
Per consentire agli utenti non autenticati di visualizzare la pagina di gestione degli errori personalizzata, assicurarsi che supporti l'accesso anonimo.
Accedere all'eccezione
Usare IExceptionHandlerPathFeature per accedere all'eccezione e al percorso della richiesta originale in un gestore errori. Nell'esempio seguente viene IExceptionHandlerPathFeature usato per ottenere altre informazioni sull'eccezione generata:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string? ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "The file was not found.";
}
if (exceptionHandlerPathFeature?.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
}
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Lambda del gestore delle eccezioni
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di una funzione lambda consente di accedere all'errore prima di restituire la risposta.
Il codice seguente usa un'espressione lambda per la gestione delle eccezioni:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
// using static System.Net.Mime.MediaTypeNames;
context.Response.ContentType = Text.Plain;
await context.Response.WriteAsync("An exception was thrown.");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(" The file was not found.");
}
if (exceptionHandlerPathFeature?.Path == "/")
{
await context.Response.WriteAsync(" Page: Home.");
}
});
});
app.UseHsts();
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
UseStatusCodePages
Per impostazione predefinita, un'app ASP.NET Core non fornisce una tabella codici di stato per i codici di stato degli errori HTTP, ad esempio 404 - Non trovato. Quando l'app imposta un codice di stato di errore HTTP 400-599 che non ha un corpo, restituisce il codice di stato e un corpo della risposta vuoto. Per abilitare i gestori predefiniti di solo testo per i codici di stato di errore comuni, chiamare UseStatusCodePages in Program.cs:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
Chiama UseStatusCodePages prima del middleware per la gestione delle richieste. Ad esempio, chiamare UseStatusCodePages prima del middleware per i file statici e del middleware degli endpoint.
Quando UseStatusCodePages non viene usato, passare a un URL senza un endpoint restituisce un messaggio di errore dipendente dal browser che indica che l'endpoint non è stato trovato. Quando UseStatusCodePages viene chiamato, il browser restituisce la risposta seguente:
Status Code: 404; Not Found
UseStatusCodePages non viene in genere usato nell'ambiente di produzione perché restituisce un messaggio che non è utile per gli utenti.
Note
Il middleware delle pagine dei codici di stato non intercetta le eccezioni. Per fornire una pagina personalizzata per la gestione degli errori, utilizzare la pagina del gestore delle eccezioni.
UseStatusCodePages con una stringa di formato
Per personalizzare il tipo di contenuto della risposta e il testo, usare l'overload di UseStatusCodePages che accetta un tipo di contenuto e una stringa di formato:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// using static System.Net.Mime.MediaTypeNames;
app.UseStatusCodePages(Text.Plain, "Status Code Page: {0}");
Nel codice {0} precedente è un segnaposto per il codice di errore.
UseStatusCodePages con una stringa di formato non viene in genere usata nell'ambiente di produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePages con una lambda
Per specificare la gestione degli errori personalizzata e il codice per la scrittura delle risposte, usare l'overload di UseStatusCodePages che accetta un'espressione lambda:
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages(async statusCodeContext =>
{
// using static System.Net.Mime.MediaTypeNames;
statusCodeContext.HttpContext.Response.ContentType = Text.Plain;
await statusCodeContext.HttpContext.Response.WriteAsync(
$"Status Code Page: {statusCodeContext.HttpContext.Response.StatusCode}");
});
UseStatusCodePages con una funzione lambda in genere non viene usato in produzione perché restituisce un messaggio che non è utile agli utenti.
UseStatusCodePagesWithRedirects
Metodo di estensione UseStatusCodePagesWithRedirects:
- Invia un codice di stato 302 - Trovato al client.
- Reindirizza il client all'endpoint di gestione degli errori fornito nel modello di URL. L'endpoint di gestione degli errori visualizza in genere informazioni sugli errori e restituisce HTTP 200.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
Il modello di URL può includere un {0} segnaposto per il codice di stato, come illustrato nel codice precedente. Se il modello di URL inizia con ~ (tilde), ~ viene sostituito con PathBase dell'app. Quando si specifica un endpoint nell'app, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app:
- Deve reindirizzare il client a un endpoint diverso, in genere nei casi in cui un'altra app elabora l'errore. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint reindirizzato.
- Non deve conservare e restituire il codice di stato originale con la risposta di reindirizzamento iniziale.
UseStatusCodePagesWithReExecute
Metodo di estensione UseStatusCodePagesWithReExecute:
- Restituisce il codice di stato originale al client.
- Genera il corpo della risposta eseguendo nuovamente la pipeline delle richieste con un percorso alternativo.
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
Se all'interno dell'app è specificato un endpoint, creare una vista MVC o una pagina Razor per l'endpoint.
Questo metodo viene usato comunemente quando l'app deve:
- Elaborare la richiesta senza il reindirizzamento a un endpoint diverso. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint richiesto in origine.
- Conservare e restituire il codice di stato originale con la risposta.
Il modello di URL deve iniziare con / e può includere un segnaposto {0} per il codice di stato. Per passare il codice di stato come parametro di stringa di query, passare un secondo argomento in UseStatusCodePagesWithReExecute. Per esempio:
app.UseStatusCodePagesWithReExecute("/StatusCode", "?statusCode={0}");
L'endpoint che elabora l'errore può ottenere l'URL originale che ha generato l'errore, come illustrato nell'esempio seguente:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class StatusCodeModel : PageModel
{
public int OriginalStatusCode { get; set; }
public string? OriginalPathAndQuery { get; set; }
public void OnGet(int statusCode)
{
OriginalStatusCode = statusCode;
var statusCodeReExecuteFeature =
HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature is not null)
{
OriginalPathAndQuery = string.Join(
statusCodeReExecuteFeature.OriginalPathBase,
statusCodeReExecuteFeature.OriginalPath,
statusCodeReExecuteFeature.OriginalQueryString);
}
}
}
Disabilita le pagine dei codici di stato
Per disabilitare le tabelle codici di stato per un controller MVC o un metodo di azione, usare l'attributo [SkipStatusCodePages].
Per disabilitare le pagine del codice di stato per richieste specifiche in un metodo gestore di Razor Pages o in un controller MVC, usare IStatusCodePagesFeature:
public void OnGet()
{
var statusCodePagesFeature =
HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature is not null)
{
statusCodePagesFeature.Enabled = false;
}
}
Codice di gestione delle eccezioni
Il codice nelle pagine di gestione delle eccezioni può anche generare eccezioni. Le pagine di errore di produzione devono essere testate accuratamente e prestare particolare attenzione per evitare di generare eccezioni proprie.
Intestazioni di risposta
Una volta inviate le intestazioni di una risposta:
- L'app non può modificare il codice di stato della risposta.
- Non è possibile eseguire pagine o handler di eccezione. La risposta deve essere completata o la connessione deve essere interrotta.
Gestione delle eccezioni del server
Oltre alla logica di gestione delle eccezioni in un'app, l'implementazione del server HTTP può gestire alcune eccezioni. Se il server rileva un'eccezione prima dell'invio delle intestazioni di risposta, il server invia una 500 - Internal Server Error risposta senza un corpo della risposta. Se il server rileva un'eccezione dopo l'invio delle intestazioni di risposta, il server chiude la connessione. Le richieste non gestite dall'app vengono gestite dal server. Qualsiasi eccezione che si verifica quando il server sta gestendo la richiesta viene gestita dalla gestione delle eccezioni del server. Eventuali pagine di errore personalizzate dell'app, middleware di gestione delle eccezioni e filtri non hanno effetto su questo comportamento.
Gestione delle eccezioni durante l'avvio
Solo il livello di hosting può gestire le eccezioni che si verificano durante l'avvio dell'app. L'host può essere configurato per rilevare gli errori di avvio e rilevare errori dettagliati.
Il livello di hosting può visualizzare una pagina di errore per un errore di avvio acquisito solo se l'errore si verifica dopo l'associazione indirizzo host/porta. Se si verifica un errore di associazione:
- Il livello di hosting registra un'eccezione critica.
- Il processo dotnet si arresta in modo anomalo.
- Non viene visualizzata alcuna pagina di errore quando il server HTTP è Kestrel.
Se durante l'esecuzione in IIS (o servizio app di Azure) o in IIS Express il processo non può essere avviato, viene restituito l'errore 502.5 Errore del processo dal modulo ASP.NET Core. Per altre informazioni, vedere Risolvere i problemi relativi a ASP.NET Core nel servizio app Azure e IIS.
Pagina di errore del database
Il filtro AddDatabaseDeveloperPageExceptionFilter eccezioni della pagina Sviluppo database acquisisce le eccezioni correlate al database che possono essere risolte usando le migrazioni di Entity Framework Core. Quando si verificano queste eccezioni, viene generata una risposta HTML con i dettagli delle possibili azioni per risolvere il problema. Questa pagina è abilitata solo nell'ambiente Development . Il codice seguente aggiunge il filtro delle eccezioni della pagina per sviluppatori di database:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddRazorPages();
Filtri di eccezione
Nelle app MVC i filtri delle eccezioni possono essere configurati a livello globale o per ogni controller o azione singolarmente. Nelle Razor app Pages possono essere configurate a livello globale o per modello di pagina. Questi filtri gestiscono eventuali eccezioni non gestite che si verificano durante l'esecuzione di un'azione del controller o di un altro filtro. Per altre informazioni, vedere Filtri in ASP.NET Core.
I filtri delle eccezioni sono utili per intercettare le eccezioni che si verificano all'interno delle azioni MVC, ma non sono flessibili quanto il middleware di gestione delle eccezioni predefinito, UseExceptionHandler. È consigliabile usare UseExceptionHandler, a meno che non sia necessario eseguire la gestione degli errori in modo diverso in base all'azione MVC scelta.
Errori di stato del modello
Per informazioni su come gestire gli errori dello stato del modello, vedere Associazione di modelli e Convalida del modello.
Risorse aggiuntive
Di Kirk Larkin, Tom Dykstra e Steve Smith
Questo articolo illustra gli approcci comuni alla gestione degli errori nelle app Web di ASP.NET Core. Vedere Gestire gli errori nelle API di base ASP.NET per le API Web.
Visualizzare o scaricare il codice di esempio. (Come scaricare). La scheda di rete negli strumenti di sviluppo del browser F12 è utile durante il test dell'app di esempio.
Pagina delle eccezioni per gli sviluppatori
Nella pagina Eccezioni sviluppatore vengono visualizzate informazioni dettagliate sulle eccezioni di richiesta non gestite. I modelli ASP.NET Core generano il codice seguente:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Il codice evidenziato precedente abilita la pagina delle eccezioni per sviluppatori quando l'app è in esecuzione nell'ambienteDevelopment.
I modelli posizionano UseDeveloperExceptionPage all'inizio della pipeline del middleware in modo che possa intercettare le eccezioni non gestite sollevate dal middleware successivo.
Il codice precedente abilita la pagina eccezioni per sviluppatori solo quando l'app viene eseguita nell'ambiente Development . Le informazioni dettagliate sulle eccezioni non devono essere visualizzate pubblicamente quando l'app viene eseguita nell'ambiente Production . Per altre informazioni sulla configurazione degli ambienti, vedere ASP.NET Ambienti di runtime di base.
La pagina Delle eccezioni per sviluppatori può includere le informazioni seguenti sull'eccezione e sulla richiesta:
- Analisi dello stack
- Parametri della stringa di query, se presenti
- Cookie se presenti
- Headers
Non è garantito che la pagina delle eccezioni per sviluppatori fornisca alcuna informazione. Usare Registrazione per informazioni complete sull'errore.
Pagina del gestore di eccezioni
Per configurare una pagina di gestione degli errori personalizzata per l'ambienteProduction, chiamare UseExceptionHandler. Questo middleware per la gestione delle eccezioni:
- Rileva e registra eccezioni non gestite.
- Esegue nuovamente la richiesta in una pipeline alternativa usando il percorso indicato. La richiesta non viene eseguita nuovamente se la risposta è stata avviata. Il codice generato dal modello esegue nuovamente la richiesta usando il
/Errorpercorso.
Warning
Se la pipeline alternativa genera una propria eccezione, il middleware per la gestione delle eccezioni rilancia l'eccezione originale.
Nell'esempio seguente, si UseExceptionHandler aggiunge il middleware per la gestione delle eccezioni in ambienti non Development.
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Il Razor modello di app Pagine fornisce una pagina di errore (.cshtml) e una classe PageModel(ErrorModel) nella cartella Pages. Per un'app MVC, il modello di progetto include un Error metodo di azione e una visualizzazione Errore per il Home controller.
Il middleware di gestione delle eccezioni esegue nuovamente la richiesta usando il metodo HTTP originale . Se un endpoint del gestore errori è limitato a un set specifico di metodi HTTP, viene eseguito solo per tali metodi HTTP. Ad esempio, un'azione del controller MVC che usa l'attributo [HttpGet] viene eseguita solo per le richieste GET. Per assicurarsi che tutte le richieste raggiungano la pagina di gestione degli errori personalizzata, non limitarle a un set specifico di metodi HTTP.
Per gestire le eccezioni in modo diverso in base al metodo HTTP originale:
- Per le pagine Razor, crea più metodi handler. Ad esempio, usare
OnGetper gestire le eccezioni GET e usareOnPostper gestire le eccezioni POST. - In MVC, applicare gli attributi dei verbi HTTP a più azioni. Ad esempio, usare
[HttpGet]per gestire le eccezioni GET e usare[HttpPost]per gestire le eccezioni POST.
Per consentire agli utenti non autenticati di visualizzare la pagina di gestione degli errori personalizzata, assicurarsi che supporti l'accesso anonimo.
Accedere all'eccezione
Usare IExceptionHandlerPathFeature per accedere all'eccezione e al percorso della richiesta originale in un gestore errori. Il codice seguente aggiunge ExceptionMessage all'impostazione predefinita Pages/Error.cshtml.cs generata dai modelli ASP.NET Core:
[ResponseCache(Duration=0, Location=ResponseCacheLocation.None, NoStore=true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string ExceptionMessage { get; set; }
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "File error thrown";
_logger.LogError(ExceptionMessage);
}
if (exceptionHandlerPathFeature?.Path == "/index")
{
ExceptionMessage += " from home page";
}
}
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Per testare l'eccezione nell'app di esempio:
- Impostare l'ambiente su produzione.
- Rimuovere i commenti da
webBuilder.UseStartup<Startup>();inProgram.cs. - Selezionare Attivare un'eccezione nella home page.
Lambda del gestore delle eccezioni
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di una funzione lambda consente di accedere all'errore prima di restituire la risposta.
Il codice seguente usa un'espressione lambda per la gestione delle eccezioni:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;;
context.Response.ContentType = "text/html";
await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
await context.Response.WriteAsync("ERROR!<br><br>\r\n");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync(
"File error thrown!<br><br>\r\n");
}
await context.Response.WriteAsync(
"<a href=\"/\">Home</a><br>\r\n");
await context.Response.WriteAsync("</body></html>\r\n");
await context.Response.WriteAsync(new string(' ', 512));
});
});
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Warning
Non fornire informazioni sugli errori sensibili da IExceptionHandlerFeature o IExceptionHandlerPathFeature ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Per testare la lambda per la gestione delle eccezioni nell'app di esempio:
- Impostare l'ambiente su produzione.
- Rimuovere i commenti da
webBuilder.UseStartup<StartupLambda>();inProgram.cs. - Selezionare Attivare un'eccezione nella home page.
UseStatusCodePages
Per impostazione predefinita, un'app ASP.NET Core non fornisce una tabella codici di stato per i codici di stato degli errori HTTP, ad esempio 404 - Non trovato. Quando l'app imposta un codice di stato di errore HTTP 400-599 che non ha un corpo, restituisce il codice di stato e un corpo della risposta vuoto. Per fornire pagine dei codici di stato, usare il middleware per le pagine dei codici di stato. Per abilitare i gestori di solo testo predefiniti per i codici di stato di errore comuni, chiamare UseStatusCodePages nel metodo Startup.Configure:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Chiama UseStatusCodePages prima del middleware per la gestione delle richieste. Ad esempio, chiama UseStatusCodePages prima del middleware per i file statici e del middleware degli endpoint.
Quando UseStatusCodePages non viene usato, passare a un URL senza un endpoint restituisce un messaggio di errore dipendente dal browser che indica che l'endpoint non è stato trovato. Ad esempio, passando a Home/Privacy2. Quando UseStatusCodePages viene chiamato, il browser restituisce:
Status Code: 404; Not Found
UseStatusCodePages non viene in genere usato nell'ambiente di produzione perché restituisce un messaggio che non è utile per gli utenti.
Per eseguire il test UseStatusCodePages nell'app di esempio:
- Impostare l'ambiente su produzione.
- Rimuovere i commenti da
webBuilder.UseStartup<StartupUseStatusCodePages>();inProgram.cs. - Selezionare i collegamenti nella home page.
Note
Il middleware delle pagine dei codici di stato non intercetta le eccezioni. Per fornire una pagina personalizzata per la gestione degli errori, utilizzare la pagina del gestore delle eccezioni.
UseStatusCodePages con una stringa di formato
Per personalizzare il tipo di contenuto della risposta e il testo, usare l'overload di UseStatusCodePages che accetta un tipo di contenuto e una stringa di formato:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages(
"text/plain", "Status code page, status code: {0}");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Nel codice {0} precedente è un segnaposto per il codice di errore.
UseStatusCodePages con una stringa di formato non viene in genere usata nell'ambiente di produzione perché restituisce un messaggio che non è utile agli utenti.
Per testare UseStatusCodePages nell'app di esempio, rimuovere i commenti da webBuilder.UseStartup<StartupFormat>(); in Program.cs.
UseStatusCodePages con una lambda
Per specificare la gestione degli errori personalizzata e il codice per la scrittura delle risposte, usare l'overload di UseStatusCodePages che accetta un'espressione lambda:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePages(async context =>
{
context.HttpContext.Response.ContentType = "text/plain";
await context.HttpContext.Response.WriteAsync(
"Status code page, status code: " +
context.HttpContext.Response.StatusCode);
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
UseStatusCodePages con una funzione lambda in genere non viene usato in produzione perché restituisce un messaggio che non è utile agli utenti.
Per testare UseStatusCodePages nell'app di esempio, rimuovere i commenti da webBuilder.UseStartup<StartupStatusLambda>(); in Program.cs.
UseStatusCodePagesWithRedirects
Metodo di estensione UseStatusCodePagesWithRedirects:
- Invia un codice di stato 302 - Trovato al client.
- Reindirizza il client all'endpoint di gestione degli errori fornito nel modello di URL. L'endpoint di gestione degli errori visualizza in genere informazioni sugli errori e restituisce HTTP 200.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithRedirects("/MyStatusCode?code={0}");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Il modello di URL può includere un {0} segnaposto per il codice di stato, come illustrato nel codice precedente. Se il modello di URL inizia con ~ (tilde), ~ viene sostituito con PathBase dell'app. Quando si specifica un endpoint nell'app, creare una vista MVC o una pagina Razor per l'endpoint. Per un Razor esempio di Pages, vedere Pages/MyStatusCode.cshtml nell'app di esempio.
Questo metodo viene usato comunemente quando l'app:
- Deve reindirizzare il client a un endpoint diverso, in genere nei casi in cui un'altra app elabora l'errore. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint reindirizzato.
- Non deve conservare e restituire il codice di stato originale con la risposta di reindirizzamento iniziale.
Per testare UseStatusCodePages nell'app di esempio, rimuovere i commenti da webBuilder.UseStartup<StartupSCredirect>(); in Program.cs.
UseStatusCodePagesWithReExecute
Metodo di estensione UseStatusCodePagesWithReExecute:
- Restituisce il codice di stato originale al client.
- Genera il corpo della risposta eseguendo nuovamente la pipeline delle richieste con un percorso alternativo.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/MyStatusCode2", "?code={0}");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Se all'interno dell'app è specificato un endpoint, creare una vista MVC o una pagina Razor per l'endpoint. Assicurarsi che UseStatusCodePagesWithReExecute sia posizionato prima di UseRouting in modo che la richiesta possa essere reindirizzata alla pagina di stato. Per un Razor esempio di Pages, vedere Pages/MyStatusCode2.cshtml nell'app di esempio.
Questo metodo viene usato comunemente quando l'app deve:
- Elaborare la richiesta senza il reindirizzamento a un endpoint diverso. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint richiesto in origine.
- Conservare e restituire il codice di stato originale con la risposta.
I modelli di URL e stringa di query possono includere un segnaposto {0} per il codice di stato. Il modello di URL deve iniziare con /.
L'endpoint che elabora l'errore può ottenere l'URL originale che ha generato l'errore, come illustrato nell'esempio seguente:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class MyStatusCode2Model : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string ErrorStatusCode { get; set; }
public string OriginalURL { get; set; }
public bool ShowOriginalURL => !string.IsNullOrEmpty(OriginalURL);
public void OnGet(string code)
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
ErrorStatusCode = code;
var statusCodeReExecuteFeature = HttpContext.Features.Get<
IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature != null)
{
OriginalURL =
statusCodeReExecuteFeature.OriginalPathBase
+ statusCodeReExecuteFeature.OriginalPath
+ statusCodeReExecuteFeature.OriginalQueryString;
}
}
}
Per un Razor esempio di Pages, vedere Pages/MyStatusCode2.cshtml nell'app di esempio.
Per testare UseStatusCodePages nell'app di esempio, rimuovere i commenti da webBuilder.UseStartup<StartupSCreX>(); in Program.cs.
Disabilita le pagine dei codici di stato
Per disabilitare le tabelle codici di stato per un controller MVC o un metodo di azione, usare l'attributo [SkipStatusCodePages].
Per disabilitare le pagine del codice di stato per richieste specifiche in un metodo gestore di Razor Pages o in un controller MVC, usare IStatusCodePagesFeature:
public void OnGet()
{
// using Microsoft.AspNetCore.Diagnostics;
var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature != null)
{
statusCodePagesFeature.Enabled = false;
}
}
Codice di gestione delle eccezioni
Il codice nelle pagine di gestione delle eccezioni può anche generare eccezioni. Le pagine di errore di produzione devono essere testate accuratamente e prestare particolare attenzione per evitare di generare eccezioni proprie.
Intestazioni di risposta
Una volta inviate le intestazioni di una risposta:
- L'app non può modificare il codice di stato della risposta.
- Non è possibile eseguire pagine o handler di eccezione. La risposta deve essere completata o la connessione deve essere interrotta.
Gestione delle eccezioni del server
Oltre alla logica di gestione delle eccezioni in un'app, l'implementazione del server HTTP può gestire alcune eccezioni. Se il server rileva un'eccezione prima dell'invio delle intestazioni di risposta, il server invia una 500 - Internal Server Error risposta senza un corpo della risposta. Se il server rileva un'eccezione dopo l'invio delle intestazioni di risposta, il server chiude la connessione. Le richieste non gestite dall'app vengono gestite dal server. Qualsiasi eccezione che si verifica quando il server sta gestendo la richiesta viene gestita dalla gestione delle eccezioni del server. Eventuali pagine di errore personalizzate dell'app, middleware di gestione delle eccezioni e filtri non hanno effetto su questo comportamento.
Gestione delle eccezioni durante l'avvio
Solo il livello di hosting può gestire le eccezioni che si verificano durante l'avvio dell'app. L'host può essere configurato per rilevare gli errori di avvio e rilevare errori dettagliati.
Il livello di hosting può visualizzare una pagina di errore per un errore di avvio acquisito solo se l'errore si verifica dopo l'associazione indirizzo host/porta. Se si verifica un errore di associazione:
- Il livello di hosting registra un'eccezione critica.
- Il processo dotnet si arresta in modo anomalo.
- Non viene visualizzata alcuna pagina di errore quando il server HTTP è Kestrel.
Se durante l'esecuzione in IIS (o servizio app di Azure) o in IIS Express il processo non può essere avviato, viene restituito l'errore 502.5 Errore del processo dal modulo ASP.NET Core. Per altre informazioni, vedere Risolvere i problemi relativi a ASP.NET Core nel servizio app Azure e IIS.
Pagina di errore del database
Il filtro AddDatabaseDeveloperPageExceptionFilter eccezioni della pagina Sviluppo database acquisisce le eccezioni correlate al database che possono essere risolte usando le migrazioni di Entity Framework Core. Quando si verificano queste eccezioni, viene generata una risposta HTML con i dettagli delle possibili azioni per risolvere il problema. Questa pagina è abilitata solo nell'ambiente Development . Il codice seguente è stato generato dai modelli ASP.NET Core Razor Pages quando sono stati specificati singoli account utente:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddRazorPages();
}
Filtri di eccezione
Nelle app MVC i filtri delle eccezioni possono essere configurati a livello globale o per ogni controller o azione singolarmente. Nelle Razor app Pages possono essere configurate a livello globale o per modello di pagina. Questi filtri gestiscono eventuali eccezioni non gestite che si verificano durante l'esecuzione di un'azione del controller o di un altro filtro. Per altre informazioni, vedere Filtri in ASP.NET Core.
I filtri delle eccezioni sono utili per intercettare le eccezioni che si verificano all'interno delle azioni MVC, ma non sono flessibili quanto il middleware di gestione delle eccezioni predefinito, UseExceptionHandler. È consigliabile usare UseExceptionHandler, a meno che non sia necessario eseguire la gestione degli errori in modo diverso in base all'azione MVC scelta.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Errori di stato del modello
Per informazioni su come gestire gli errori dello stato del modello, vedere Associazione di modelli e Convalida del modello.
Risorse aggiuntive
Di Tom Dykstra e Steve Smith
Questo articolo illustra gli approcci comuni alla gestione degli errori nelle app Web di ASP.NET Core. Vedere Gestire gli errori nelle API di base ASP.NET per le API Web.
Visualizzare o scaricare il codice di esempio. (Come scaricare.)
Pagina delle eccezioni per gli sviluppatori
In Pagina delle eccezioni per gli sviluppatori sono disponibili informazioni dettagliate sulle eccezioni delle richieste. I modelli ASP.NET Core generano il codice seguente:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Il codice precedente abilita la pagina delle eccezioni per sviluppatori quando l'app è in esecuzione nell'ambienteDevelopment.
I modelli collocano UseDeveloperExceptionPage prima di ogni middleware in modo che le eccezioni vengano intercettate dal middleware che segue.
Il codice precedente abilita la pagina eccezioni per sviluppatori solo quando l'app è in esecuzione nell'ambienteDevelopment. Le informazioni dettagliate sulle eccezioni non devono essere visualizzate pubblicamente quando l'app viene eseguita nell'ambiente di produzione. Per altre informazioni sulla configurazione degli ambienti, vedere ASP.NET Ambienti di runtime di base.
La pagina Eccezioni per sviluppatori include le informazioni seguenti sull'eccezione e sulla richiesta:
- Analisi dello stack
- Parametri della stringa di query, se presenti
- Cookie se presenti
- Headers
Pagina del gestore di eccezioni
Per configurare una pagina di gestione degli errori personalizzata per l'ambiente Production , usare il middleware di gestione delle eccezioni. Il middleware:
- Intercetta e registra le eccezioni.
- Esegue nuovamente la richiesta in una pipeline alternativa per la pagina o il controller indicati. La richiesta non viene eseguita nuovamente se la risposta è stata avviata. Il codice generato dal modello esegue nuovamente la richiesta a
/Error.
Nell'esempio seguente, si UseExceptionHandler aggiunge il middleware per la gestione delle eccezioni in ambienti non Development.
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
Il Razor modello di app Pagine fornisce una pagina di errore (.cshtml) e una classe PageModel(ErrorModel) nella cartella Pages. Per un'app MVC, il modello di progetto include un metodo di azione Errore e una visualizzazione Errore nel Home controller.
Non contrassegnare il metodo di azione del gestore degli errori con attributi del metodo HTTP, ad esempio HttpGet. I verbi espliciti impediscono ad alcune richieste di raggiungere il metodo. Consentire l'accesso anonimo al metodo se gli utenti non autenticati dovrebbero visualizzare la visualizzazione degli errori.
Accedere all'eccezione
Usare IExceptionHandlerPathFeature per accedere all'eccezione e al percorso della richiesta originale in un controller o in una pagina del gestore degli errori:
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public string ExceptionMessage { get; set; }
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
ExceptionMessage = "File error thrown";
}
if (exceptionHandlerPathFeature?.Path == "/index")
{
ExceptionMessage += " from home page";
}
}
}
Warning
Non fornire informazioni sugli errori sensibili ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Per attivare la pagina di gestione delle eccezioni precedente, impostare l'ambiente su production e provocare un'eccezione.
Lambda del gestore delle eccezioni
Un'alternativa a una pagina di gestione delle eccezioni personalizzata consiste nel fornire un'espressione lambda a UseExceptionHandler. L'uso di una funzione lambda consente di accedere all'errore prima di restituire la risposta.
Di seguito è riportato un esempio dell'uso di un'espressione lambda per la gestione delle eccezioni:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
context.Response.ContentType = "text/html";
await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
await context.Response.WriteAsync("ERROR!<br><br>\r\n");
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
{
await context.Response.WriteAsync("File error thrown!<br><br>\r\n");
}
await context.Response.WriteAsync("<a href=\"/\">Home</a><br>\r\n");
await context.Response.WriteAsync("</body></html>\r\n");
await context.Response.WriteAsync(new string(' ', 512)); // IE padding
});
});
app.UseHsts();
}
Nel codice precedente viene await context.Response.WriteAsync(new string(' ', 512)); aggiunto in modo che il browser Internet Explorer visualizzi il messaggio di errore anziché un messaggio di errore di Internet Explorer. Per altre informazioni, vedere questo problema in GitHub.
Warning
Non fornire informazioni sugli errori sensibili da IExceptionHandlerFeature o IExceptionHandlerPathFeature ai client. Fornire informazioni sugli errori rappresenta un rischio di sicurezza.
Per visualizzare il risultato dell'espressione lambda di gestione delle eccezioni nell'app di esempio , usare le direttive preprocessore ProdEnvironment e ErrorHandlerLambda e selezionare Attivare un'eccezione sulla pagina iniziale.
UseStatusCodePages
Per impostazione predefinita, un'app ASP.NET Core non offre una tabella codici di stato per i codici di stato HTTP, ad esempio 404 - Non trovato. L'app restituisce un codice di stato e un corpo di risposta vuoto. Per fornire pagine dei codici di stato, usa il middleware Status Code Pages.
Il middleware viene reso disponibile dal pacchetto Microsoft.AspNetCore.Diagnostics .
Per abilitare i gestori di solo testo predefiniti per i codici di stato di errore comuni, chiamare UseStatusCodePages nel metodo Startup.Configure:
app.UseStatusCodePages();
Chiamare UseStatusCodePages prima del middleware di gestione delle richieste ( ad esempio, middleware di file statici e middleware MVC).
Quando UseStatusCodePages non viene usato, passare a un URL senza un endpoint restituisce un messaggio di errore dipendente dal browser che indica che l'endpoint non è stato trovato. Ad esempio, passando a Home/Privacy2. Quando UseStatusCodePages viene chiamato, il browser restituisce:
Status Code: 404; Not Found
UseStatusCodePages con una stringa di formato
Per personalizzare il tipo di contenuto della risposta e il testo, usare l'overload di UseStatusCodePages che accetta un tipo di contenuto e una stringa di formato:
app.UseStatusCodePages(
"text/plain", "Status code page, status code: {0}");
UseStatusCodePages con una lambda
Per specificare la gestione degli errori personalizzata e il codice per la scrittura delle risposte, usare l'overload di UseStatusCodePages che accetta un'espressione lambda:
app.UseStatusCodePages(async context =>
{
context.HttpContext.Response.ContentType = "text/plain";
await context.HttpContext.Response.WriteAsync(
"Status code page, status code: " +
context.HttpContext.Response.StatusCode);
});
UseStatusCodePagesWithRedirects
Metodo di estensione UseStatusCodePagesWithRedirects:
- Invia un codice di stato 302 - Trovato al client.
- Reindirizza il client al percorso specificato nel modello di URL.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");
Il modello di URL può includere un segnaposto {0} per il codice di stato, come illustrato nell'esempio. Se il modello di URL inizia con ~ (tilde), ~ viene sostituito con PathBase dell'app. Se si fa riferimento a un endpoint all'interno dell'app, creare una vista MVC o una pagina Razor per l'endpoint. Per un Razor esempio di Pages, consulta Pages/StatusCode.cshtml nell'app di esempio.
Questo metodo viene usato comunemente quando l'app:
- Deve reindirizzare il client a un endpoint diverso, in genere nei casi in cui un'altra app elabora l'errore. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint reindirizzato.
- Non deve conservare e restituire il codice di stato originale con la risposta di reindirizzamento iniziale.
UseStatusCodePagesWithReExecute
Metodo di estensione UseStatusCodePagesWithReExecute:
- Restituisce il codice di stato originale al client.
- Genera il corpo della risposta eseguendo nuovamente la pipeline delle richieste con un percorso alternativo.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");
Se si fa riferimento a un endpoint all'interno dell'app, creare una vista MVC o una pagina Razor per l'endpoint. Assicurarsi che UseStatusCodePagesWithReExecute sia posizionato prima di UseRouting in modo che la richiesta possa essere reindirizzata alla pagina di stato. Per un Razor esempio di Pages, consulta Pages/StatusCode.cshtml nell'app di esempio.
Questo metodo viene usato comunemente quando l'app deve:
- Elaborare la richiesta senza il reindirizzamento a un endpoint diverso. Per le app Web, la barra degli indirizzi del browser del client riflette l'endpoint richiesto in origine.
- Conservare e restituire il codice di stato originale con la risposta.
I modelli di URL e di stringa di query potrebbero includere un segnaposto ({0}) per il codice di stato. Il modello di URL deve iniziare con una barra (/). Quando si utilizza un segnaposto nel percorso, verificare che l'endpoint (pagina o controller) possa gestire il segmento del percorso. Ad esempio, una Razor pagina per gli errori dovrebbe accettare il valore facoltativo del segmento di percorso con la direttiva @page:
@page "{code?}"
L'endpoint che elabora l'errore può ottenere l'URL originale che ha generato l'errore, come illustrato nell'esempio seguente:
var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature != null)
{
OriginalURL =
statusCodeReExecuteFeature.OriginalPathBase
+ statusCodeReExecuteFeature.OriginalPath
+ statusCodeReExecuteFeature.OriginalQueryString;
}
Non contrassegnare il metodo di azione del gestore degli errori con attributi del metodo HTTP, ad esempio HttpGet. I verbi espliciti impediscono ad alcune richieste di raggiungere il metodo. Consentire l'accesso anonimo al metodo se gli utenti non autenticati dovrebbero visualizzare la visualizzazione degli errori.
Disabilita le pagine dei codici di stato
Per disabilitare le tabelle codici di stato per un controller MVC o un metodo di azione, usare l'attributo [SkipStatusCodePages] .
Per disabilitare le pagine del codice di stato per richieste specifiche in un metodo gestore di Razor Pages o in un controller MVC, usare IStatusCodePagesFeature:
var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature != null)
{
statusCodePagesFeature.Enabled = false;
}
Codice di gestione delle eccezioni
Il codice in pagine di gestione delle eccezioni può generare eccezioni. È spesso consigliabile che le pagine di errore di produzione contengano esclusivamente contenuto statico.
Intestazioni di risposta
Una volta inviate le intestazioni di una risposta:
- L'app non può modificare il codice di stato della risposta.
- Non è possibile eseguire pagine o handler di eccezione. La risposta deve essere completata o la connessione deve essere interrotta.
Gestione delle eccezioni del server
Oltre alla logica di gestione delle eccezioni nell'app, l'implementazione del server HTTP può gestire alcune eccezioni. Se rileva un'eccezione prima dell'invio delle intestazioni di risposta, il server invia una risposta 500 - Errore interno del server senza corpo. Se il server rileva un'eccezione dopo l'invio delle intestazioni di risposta, il server chiude la connessione. Le richieste che non sono gestite dall'app vengono gestite dal server. Qualsiasi eccezione che si verifica quando il server sta gestendo la richiesta viene gestita dalla gestione delle eccezioni del server. Eventuali pagine di errore personalizzate dell'app, middleware di gestione delle eccezioni e filtri non hanno effetto su questo comportamento.
Gestione delle eccezioni durante l'avvio
Solo il livello di hosting può gestire le eccezioni che si verificano durante l'avvio dell'app. L'host può essere configurato per rilevare gli errori di avvio e rilevare errori dettagliati.
Il livello di hosting può visualizzare una pagina di errore per un errore di avvio acquisito solo se l'errore si verifica dopo l'associazione indirizzo host/porta. Se si verifica un errore di associazione:
- Il livello di hosting registra un'eccezione critica.
- Il processo dotnet si arresta in modo anomalo.
- Non viene visualizzata alcuna pagina di errore quando il server HTTP è Kestrel.
Se durante l'esecuzione in IIS (o servizio app di Azure) o in IIS Express il processo non può essere avviato, viene restituito l'errore 502.5 Errore del processo dal modulo ASP.NET Core. Per altre informazioni, vedere Risolvere i problemi relativi a ASP.NET Core nel servizio app Azure e IIS.
Pagina di errore del database
Il middleware della pagina degli errori del database acquisisce le eccezioni correlate al database che possono essere risolte usando le migrazioni di Entity Framework. Quando si verificano queste eccezioni, viene generata una risposta HTML con i dettagli delle azioni possibili per risolvere il problema. Questa pagina deve essere abilitata solo nell'ambiente Development . Abilitare la pagina aggiungendo codice Startup.Configure:
if (env.IsDevelopment())
{
app.UseDatabaseErrorPage();
}
UseDatabaseErrorPage richiede il pacchetto NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore .
Filtri di eccezione
Nelle app MVC i filtri delle eccezioni possono essere configurati a livello globale o per ogni controller o azione singolarmente. Nelle Razor app Pages possono essere configurate a livello globale o per modello di pagina. Questi filtri gestiscono tutte le eccezioni non gestite che si verificano durante l'esecuzione di un'azione del controller o di un altro filtro Per altre informazioni, vedere Filtri in ASP.NET Core.
Tip
I filtri delle eccezioni sono utili per intercettare le eccezioni che si verificano all'interno delle azioni MVC, ma non sono flessibili come il middleware di gestione delle eccezioni. È consigliabile usare il middleware. Usare i filtri solo quando è necessario eseguire la gestione degli errori in modo diverso in base all'azione MVC scelta.
Errori di stato del modello
Per informazioni su come gestire gli errori dello stato del modello, vedere Associazione di modelli e Convalida del modello.