如何在最少 API 應用程式中處理錯誤
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
透過 David Acker 的貢獻
本文說明如何在最少 API 應用程式中處理錯誤。 如需在控制器型 API 中錯誤處理的資訊,請參閱處理 ASP.NET Core 中的錯誤和處理 ASP.NET Core 控制器型 Web API 中的錯誤。
例外狀況
在 Minimal API 應用程式中,有兩種不同的內建集中機制來處理未處理的例外狀況:
- 開發人員例外狀況頁面中介軟體 (僅適用於開發環境)。
- 例外狀況處理常式中介軟體
本節參考以下範例應用程式來示範處理 Minimal API 中例外狀況的方法。 當要求 /exception
端點時,它會擲回例外狀況:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/exception", () =>
{
throw new InvalidOperationException("Sample Exception");
});
app.MapGet("/", () => "Test by calling /exception");
app.Run();
開發人員例外狀況頁面
「開發人員例外狀況頁面」會顯示未處理要求例外狀況的詳細資訊。 它會使用 DeveloperExceptionPageMiddleware 從 HTTP 管線中擷取同步和非同步例外狀況,並產生錯誤回應。 開發人員例外狀況頁面會在中介軟體管線的早期執行,以便它可以攔截在後續中介軟體中擲回的未處理的例外狀況。
當符合以下兩項條件時,ASP.NET Core 應用程式預設會啟用開發人員例外狀況頁面:
- 在開發環境中執行。
- 該應用程式是以目前的範本建立的,也就是使用 WebApplication.CreateBuilder。
使用舊版範本所建立的應用程式 (也就是使用 WebHost.CreateDefaultBuilder) 可以透過呼叫 app.UseDeveloperExceptionPage
來啟用開發人員例外狀況頁面。
警告
除非應用程式在開發環境中執行,否則請勿啟用 [開發人員例外狀況頁面]。 當應用程式在生產環境中執作時,請不要公開分享詳細的例外狀況資訊。 如需設定環境的詳細資訊,請參閱在 ASP.NET Core 中使用多個環境。
「開發人員例外狀況頁面」可能包含下列有關例外狀況和要求的資訊:
- 堆疊追蹤
- 查詢字串參數 (如果有的話)
- Cookie,(如果有的話)
- 標頭
- 端點中繼資料 (如果有的話)
不保證「開發人員例外狀況頁面」提供任何資訊。 請使用記錄來取得完整的錯誤資訊。
下圖顯示了一個範例開發人員例外狀況頁面,並附有動畫來顯示索引標籤和顯示的資訊:
為了回應具有 Accept: text/plain
標頭的要求,開發人員例外狀況頁面會傳回純文字,而不是 HTML。 例如:
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
若要查看開發人員例外狀況頁面:
- 在開發環境中執行範例應用程式。
- 移至
/exception
端點。
例外處理常式
在非開發環境中,使用例外狀況處理常式中介軟體 來產生錯誤承載。 若要設定 Exception Handler Middleware
,請呼叫 UseExceptionHandler。
例如,下列程式碼會變更應用程式,以使用符合 RFC 7807 的承載回應用戶端。 如需詳細資訊,請參閱本文稍後的問題詳細資料一節。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseExceptionHandler(exceptionHandlerApp
=> exceptionHandlerApp.Run(async context
=> await Results.Problem()
.ExecuteAsync(context)));
app.MapGet("/exception", () =>
{
throw new InvalidOperationException("Sample Exception");
});
app.MapGet("/", () => "Test by calling /exception");
app.Run();
用戶端和伺服器錯誤回應
仔細觀察下列 Minimal API 應用程式。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/users/{id:int}", (int id)
=> id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));
app.MapGet("/", () => "Test by calling /users/{id:int}");
app.Run();
public record User(int Id);
當 id
大於 0
時,/users
端點會產生 200 OK
和 User
的 json
表示形式,否則會產生 400 BAD REQUEST
狀態碼,沒有回應本文。 如需建立回應的詳細資訊,請參閱在 Minimal API 應用程式中建立回應。
Status Code Pages middleware
可以設定成為所有 HTTP 用戶端 (400
-499
) 或伺服器 (500
-599
) 回應產生一般本文內容 (當為空時)。 中介軟體是藉由呼叫 UseStatusCodePages 擴充方法來設定。
例如,下列範例會將應用程式變更為使用符合 RFC 7807 的承載 (用於所有用戶端和伺服器回應,包括路由傳送錯誤 (例如 404 NOT FOUND
)) 來回應用戶端。 如需詳細資訊,請參閱問題詳細資料一節。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseStatusCodePages(async statusCodeContext
=> await Results.Problem(statusCode: statusCodeContext.HttpContext.Response.StatusCode)
.ExecuteAsync(statusCodeContext.HttpContext));
app.MapGet("/users/{id:int}", (int id)
=> id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );
app.MapGet("/", () => "Test by calling /users/{id:int}");
app.Run();
public record User(int Id);
問題詳細資料
問題詳細資料 並不是描述 HTTP API 錯誤的唯一回應格式,不過,它們通常會用來報告 HTTP API 的錯誤。
問題詳細資料服務會實作 IProblemDetailsService 介面 (其支援在 ASP.NET Core 中建立問題詳細資料)。 IServiceCollection 上的 AddProblemDetails(IServiceCollection) 擴充方法會註冊預設的 IProblemDetailsService
實作。
在 ASP.NET Core 應用程式中,下列中介軟體會在呼叫 AddProblemDetails
時產生問題詳細資料 HTTP 回應,除非 Accept
要求 HTTP 標頭不包含註冊的 IProblemDetailsWriter 所支援的其中一個內容類型 (預設值:application/json
):
- ExceptionHandlerMiddleware:會在未定義自訂處理常式時產生問題詳細資料回應。
- StatusCodePagesMiddleware:預設會產生問題詳細資料回應。
- DeveloperExceptionPageMiddleware:在
Accept
要求 HTTP 標頭不包含text/html
時,在開發過程中產生問題詳細資料回應。
透過使用 AddProblemDetails
擴充方法,可以將 Minimal API 應用程式設定為還沒有本文內容的所有 HTTP 用戶端和伺服器錯誤回應產生問題詳細資料回應。
下列程式碼會將應用程式設定為產生問題詳細資料:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
app.MapGet("/users/{id:int}", (int id)
=> id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)));
app.MapGet("/", () => "Test by calling /users/{id:int}");
app.Run();
public record User(int Id);
如需使用 AddProblemDetails
的詳細資訊,請參閱問題詳細資料
IProblemDetailsService 後援
在下列程式碼中,如果 IProblemDetailsService 實作無法產生 ProblemDetails,則 httpContext.Response.WriteAsync("Fallback: An error occurred.")
會傳回錯誤:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async httpContext =>
{
var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
if (pds == null
|| !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
{
// Fallback behavior
await httpContext.Response.WriteAsync("Fallback: An error occurred.");
}
});
});
app.MapGet("/exception", () =>
{
throw new InvalidOperationException("Sample Exception");
});
app.MapGet("/", () => "Test by calling /exception");
app.Run();
上述 程式碼:
- 如果
problemDetailsService
無法撰寫ProblemDetails
,則會使用後援程式碼撰寫錯誤訊息。 例如,Accept 要求標頭指定DefaulProblemDetailsWriter
不支援的媒體類型的端點。 - 使用例外狀況處理常式中介軟體。
下列範例與上述範例類似,不同之處在於它會呼叫 Status Code Pages middleware
。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseStatusCodePages(statusCodeHandlerApp =>
{
statusCodeHandlerApp.Run(async httpContext =>
{
var pds = httpContext.RequestServices.GetService<IProblemDetailsService>();
if (pds == null
|| !await pds.TryWriteAsync(new() { HttpContext = httpContext }))
{
// Fallback behavior
await httpContext.Response.WriteAsync("Fallback: An error occurred.");
}
});
});
app.MapGet("/users/{id:int}", (int id) =>
{
return id <= 0 ? Results.BadRequest() : Results.Ok(new User(id));
});
app.MapGet("/", () => "Test by calling /users/{id:int}");
app.Run();
public record User(int Id);
本文說明如何在最少 API 應用程式中處理錯誤。
例外狀況
在 Minimal API 應用程式中,有兩種不同的內建集中機制來處理未處理的例外狀況:
- 開發人員例外狀況頁面中介軟體 (僅適用於開發環境)。
- 例外狀況處理常式中介軟體
本節參考以下 Minimal API 應用程式來示範處理例外狀況的方法。 當要求 /exception
端點時,它會擲回例外狀況:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/exception", ()
=> { throw new InvalidOperationException("Sample Exception"); });
app.Run();
開發人員例外狀況頁面
開發人員例外狀況頁面會顯示詳細的伺服器錯誤堆疊追蹤。 它會使用 DeveloperExceptionPageMiddleware 從 HTTP 管線中擷取同步和非同步例外狀況,並產生錯誤回應。
當符合以下兩項條件時,ASP.NET Core 應用程式預設會啟用開發人員例外狀況頁面:
- 在開發環境中執行。
- 應用程式使用 WebApplication.CreateBuilder。
如需設定中介軟體的詳細資訊,請參閱 Minimal API 應用程式中的中介軟體。
使用上述的 Minimal API 應用程式,當 Developer Exception Page
偵測到未處理的例外狀況時,它會產生一個類似於以下範例的預設純文字回應:
HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8
Date: Thu, 27 Oct 2022 18:00:59 GMT
Server: Kestrel
Transfer-Encoding: chunked
System.InvalidOperationException: Sample Exception
at Program.<>c.<<Main>$>b__0_1() in ....:line 17
at lambda_method2(Closure, Object, HttpContext)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
HEADERS
=======
Accept: */*
Connection: keep-alive
Host: localhost:5239
Accept-Encoding: gzip, deflate, br
警告
除非應用程式在開發環境中執行,否則請勿啟用 [開發人員例外狀況頁面]。 當應用程式在生產環境中執作時,請不要公開分享詳細的例外狀況資訊。 如需設定環境的詳細資訊,請參閱在 ASP.NET Core 中使用多個環境。
例外處理常式
在非開發環境中,使用例外狀況處理常式中介軟體 來產生錯誤承載。 若要設定 Exception Handler Middleware
,請呼叫 UseExceptionHandler。
例如,下列程式碼會變更應用程式,以使用符合 RFC 7807 的承載回應用戶端。 如需詳細資訊,請參閱問題詳細資料一節。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseExceptionHandler(exceptionHandlerApp
=> exceptionHandlerApp.Run(async context
=> await Results.Problem()
.ExecuteAsync(context)));
app.Map("/exception", ()
=> { throw new InvalidOperationException("Sample Exception"); });
app.Run();
用戶端和伺服器錯誤回應
仔細觀察下列 Minimal API 應用程式。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Map("/users/{id:int}", (int id)
=> id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );
app.Run();
public record User(int Id);
當 id
大於 0
時,/users
端點會產生 200 OK
和 User
的 json
表示形式,否則會產生 400 BAD REQUEST
狀態碼,沒有回應本文。 如需建立回應的詳細資訊,請參閱在 Minimal API 應用程式中建立回應。
Status Code Pages middleware
可以設定成為所有 HTTP 用戶端 (400
-499
) 或伺服器 (500
-599
) 回應產生一般本文內容 (當為空時)。 中介軟體是藉由呼叫 UseStatusCodePages 擴充方法來設定。
例如,下列範例會將應用程式變更為使用符合 RFC 7807 的承載 (用於所有用戶端和伺服器回應,包括路由傳送錯誤 (例如 404 NOT FOUND
)) 來回應用戶端。 如需詳細資訊,請參閱問題詳細資料一節。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseStatusCodePages(async statusCodeContext
=> await Results.Problem(statusCode: statusCodeContext.HttpContext.Response.StatusCode)
.ExecuteAsync(statusCodeContext.HttpContext));
app.Map("/users/{id:int}", (int id)
=> id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );
app.Run();
public record User(int Id);
問題詳細資料
問題詳細資料 並不是描述 HTTP API 錯誤的唯一回應格式,不過,它們通常會用來報告 HTTP API 的錯誤。
問題詳細資料服務會實作 IProblemDetailsService 介面 (其支援在 ASP.NET Core 中建立問題詳細資料)。 IServiceCollection 上的 AddProblemDetails(IServiceCollection) 擴充方法會註冊預設的 IProblemDetailsService
實作。
在 ASP.NET Core 應用程式中,下列中介軟體會在呼叫 AddProblemDetails
時產生問題詳細資料 HTTP 回應,除非 Accept
要求 HTTP 標頭不包含註冊的 IProblemDetailsWriter 所支援的其中一個內容類型 (預設值:application/json
):
- ExceptionHandlerMiddleware:會在未定義自訂處理常式時產生問題詳細資料回應。
- StatusCodePagesMiddleware:預設會產生問題詳細資料回應。
- DeveloperExceptionPageMiddleware:在
Accept
要求 HTTP 標頭不包含text/html
時,在開發過程中產生問題詳細資料回應。
透過使用 AddProblemDetails
擴充功能方法,可以將 Minimal API 應用程式設定為還沒有本文內容 的所有 HTTP 用戶端和伺服器錯誤回應產生問題詳細資訊回應。
下列程式碼會將應用程式設定為產生問題詳細資料:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.UseStatusCodePages();
app.Map("/users/{id:int}", (int id)
=> id <= 0 ? Results.BadRequest() : Results.Ok(new User(id)) );
app.Map("/exception", ()
=> { throw new InvalidOperationException("Sample Exception"); });
app.Run();
如需使用 AddProblemDetails
的詳細資訊,請參閱問題詳細資料