本文介紹了處理 ASP.NET Core 應(yīng)用中常見錯誤的一些方法。
要將應(yīng)用配置為顯示有關(guān)請求異常的詳細(xì)信息的頁面,請使用“開發(fā)人員異常頁”。 該頁面通過 Microsoft.AspNetCore.App 元包中的 Microsoft.AspNetCore.Diagnostics 包提供。 當(dāng)應(yīng)用在開發(fā)環(huán)境中運(yùn)行時,在 Startup.Configure 方法中添加一行:
C#
app.UseDeveloperExceptionPage();
將對 UseDeveloperExceptionPage 的調(diào)用放在要對其捕獲異常的任何中間件前面。
警告
僅當(dāng)應(yīng)用程序在開發(fā)環(huán)境中運(yùn)行時才啟用開發(fā)人員異常頁。 否則當(dāng)應(yīng)用程序在生產(chǎn)環(huán)境中運(yùn)行時,詳細(xì)的異常信息會向公眾泄露 有關(guān)配置環(huán)境的詳細(xì)信息,請參閱 在 ASP.NET Core 中使用多個環(huán)境。
要查看開發(fā)人員異常頁,請將環(huán)境設(shè)置為 Development,運(yùn)行示例應(yīng)用,并向應(yīng)用的基 URL 添加 ?throw=true。 該頁包括關(guān)于異常和請求的以下信息:
如果應(yīng)用未在開發(fā)環(huán)境中運(yùn)行,則調(diào)用 UseExceptionHandler 擴(kuò)展方法,添加異常處理中間件。 中間件:
在示例應(yīng)用的以下示例中,UseExceptionHandler 在非開發(fā)環(huán)境中添加了異常處理中間件。 在捕獲并記錄異常后,擴(kuò)展方法指定 /Error 終結(jié)點(diǎn)處重新執(zhí)行了請求的錯誤頁或控制器:
C#
app.UseExceptionHandler("/Error");
Razor Pages 應(yīng)用模板在“Pages”文件夾中提供了一個錯誤頁 (.cshtml) 和 PageModel 類 (ErrorModel)。
在 MVC 應(yīng)用中,以下錯誤處理程序方法包含在 MVC 應(yīng)用模板中并在主控制器中顯示:
C#
[AllowAnonymous]
public IActionResult Error()
{
return View(new ErrorViewModel
{ RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
不要使用 HTTP 方法屬性(如 HttpGet)修飾錯誤處理程序操作方法。 顯式謂詞可阻止某些請求訪問方法。 允許匿名訪問方法,以便未經(jīng)身份驗(yàn)證的用戶能夠接收錯誤視圖。
使用 IExceptionHandlerPathFeature 訪問控制器或頁中的異?;蛟颊埱舐窂剑?/p>
C#
// using Microsoft.AspNetCore.Diagnostics;
var exceptionHandlerPathFeature =
HttpContext.Features.Get<IExceptionHandlerPathFeature>();
var path = exceptionHandlerPathFeature?.Path;
var error = exceptionHandlerPathFeature?.Error;
警告
不要向客戶端提供來自 IExceptionHandlerFeature 或 IExceptionHandlerPathFeature 的敏感錯誤信息。 提供服務(wù)的錯誤是一種安全風(fēng)險(xiǎn)。
使用自定義異常處理頁為錯誤提供終結(jié)點(diǎn)的另一種方法是為 UseExceptionHandler 提供 lambda。 使用帶 UseExceptionHandler 的 lambda 允許在返回響應(yīng)之前訪問錯誤。
示例應(yīng)用演示了 Startup.Configure 中的自定義異常處理代碼。 使用“索引”頁上的“引發(fā)異?!辨溄佑|發(fā)異常。 運(yùn)行如下 lambda:
C#
// using Microsoft.AspNetCore.Diagnostics;
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500;
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>();
// Use exceptionHandlerPathFeature to process the exception (for example,
// logging), but do NOT expose sensitive error information directly to
// the client.
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
});
});
警告
不要向客戶端提供來自 IExceptionHandlerFeature 或 IExceptionHandlerPathFeature 的敏感錯誤信息。 提供服務(wù)的錯誤是一種安全風(fēng)險(xiǎn)。
默認(rèn)情況下,ASP.NET Core 應(yīng)用不會為 HTTP 狀態(tài)代碼(如“404 - 未找到”)提供狀態(tài)代碼頁。 應(yīng)用返回狀態(tài)代碼和空響應(yīng)正文。 要提供狀態(tài)代碼頁,請使用狀態(tài)代碼頁中間件。
該中間件通過 Microsoft.AspNetCore.App 元包中的 Microsoft.AspNetCore.Diagnostics 包提供。
向 Startup.Configure 方法添加代碼行:
C#
app.UseStatusCodePages();
在請求處理中間件(例如,靜態(tài)文件中間件和 MVC 中間件)之前調(diào)用 UseStatusCodePages 方法。
默認(rèn)情況下,狀態(tài)代碼頁中間件為常見狀態(tài)代碼(如“404 - 未找到”)添加純文本處理程序:
Status Code: 404; Not Found
中間件支持幾種可用于自定義其行為的擴(kuò)展方法。
重載 UseStatusCodePages 采用 lambda 表達(dá)式,可用于處理自定義錯誤處理邏輯和手動編寫響應(yīng):
C#
// using Microsoft.AspNetCore.Http;
app.UseStatusCodePages(async context =>
{
context.HttpContext.Response.ContentType = "text/plain";
await context.HttpContext.Response.WriteAsync(
"Status code page, status code: " +
context.HttpContext.Response.StatusCode);
});
重載 UseStatusCodePages 采用內(nèi)容類型和格式字符串,可用于自定義內(nèi)容類型和響應(yīng)文本:
C#
app.UseStatusCodePages("text/plain", "Status code page, status code: {0}");
UseStatusCodePagesWithRedirects:
C#
app.UseStatusCodePagesWithRedirects("/Error/{0}");
通常,應(yīng)用在以下情況下使用 UseStatusCodePagesWithRedirects:
UseStatusCodePagesWithReExecute:
C#
app.UseStatusCodePagesWithReExecute("/Error/{0}");
應(yīng)用在以下情況下通常使用 UseStatusCodePagesWithReExecute:
模板可能包括狀態(tài)代碼的占位符 ({0})。 模板必須以正斜杠 (/) 開頭。 使用占位符時,請確認(rèn)終結(jié)點(diǎn)(頁或控制器)可以處理路徑段。 例如,錯誤的 Razor Page 應(yīng)通過 @page 指令接受可選路徑段值:
CSHTML
@page "{code?}"
可禁用 Razor 頁處理程序方法或 MVC 控制器中的特定請求的狀態(tài)代碼頁。 要禁用狀態(tài)代碼頁,請嘗試從請求的 HttpContext.Features 集合中檢索 IStatusCodePagesFeature,并在功能可用時禁用該功能:
C#
var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();
if (statusCodePagesFeature != null)
{
statusCodePagesFeature.Enabled = false;
}
若要在應(yīng)用中使用指向終結(jié)點(diǎn)的 UseStatusCodePages 重載,請為終結(jié)點(diǎn)創(chuàng)建 MVC 視圖或 Razor Page。 例如,Razor Pages 應(yīng)用模板將生成以下頁和頁模型類:
Error.cshtml:
CSHTML
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays
detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed
applications.</strong> It can result in displaying sensitive information
from exceptions to end users. For local debugging, enable the
<strong>Development</strong> environment by setting the
<strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to
<strong>Development</strong> and restarting the app.
</p>
Error.cshtml.cs:
C#
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
異常處理頁中的代碼可能會引發(fā)異常。 建議在生產(chǎn)錯誤頁面中包含純靜態(tài)內(nèi)容。
此外,請注意,發(fā)送響應(yīng)標(biāo)頭后:
除應(yīng)用中的異常處理邏輯外,服務(wù)器實(shí)現(xiàn)也可以處理一些異常。 如果服務(wù)器在發(fā)送響應(yīng)標(biāo)頭之前捕獲到異常,服務(wù)器將發(fā)送不包含響應(yīng)正文的“500 - 內(nèi)部服務(wù)器錯誤”響應(yīng)。 如果服務(wù)器在發(fā)送響應(yīng)標(biāo)頭后捕獲到異常,服務(wù)器會關(guān)閉連接。 應(yīng)用程序無法處理的請求將由服務(wù)器進(jìn)行處理。 當(dāng)服務(wù)器處理請求時,發(fā)生的任何異常都將由服務(wù)器的異常處理進(jìn)行處理。 應(yīng)用的自定義錯誤頁面、異常處理中間件和篩選器都不會影響此行為。
應(yīng)用程序啟動期間發(fā)生的異常僅可在承載層進(jìn)行處理。 借助 Web 主機(jī),可以使用 captureStartupErrors 和 detailedErrors 鍵配置主機(jī)在響應(yīng)啟動期間出現(xiàn)錯誤時的行為方式。
僅當(dāng)捕獲的啟動錯誤發(fā)生在主機(jī)地址/端口綁定之后,承載層才會為該錯誤顯示錯誤頁。 如果出于任何原因?qū)е氯魏谓壎ㄊ。?/p>
在 IIS 或 IIS Express 上運(yùn)行應(yīng)用時,如果無法啟動進(jìn)程,ASP.NET Core 模塊將返回“502.5 - 進(jìn)程失敗”。 有關(guān)更多信息,請參見對 IIS 上的 ASP.NET Core 進(jìn)行故障排除。 要了解如何排查 Azure 應(yīng)用服務(wù)的啟動問題,請參閱 對 Azure 應(yīng)用服務(wù)上的 ASP.NET Core 進(jìn)行故障排除。
MVC 應(yīng)用還有一些其他的錯誤處理選項(xiàng),例如配置異常篩選器和執(zhí)行模型驗(yàn)證。
在 MVC 應(yīng)用中,異常篩選器可以進(jìn)行全局配置,也可以為每個控制器或每個操作單獨(dú)配置。 這些篩選器處理在執(zhí)行控制器操作或其他篩選器時出現(xiàn)的任何未處理的異常。 不會以其他方式調(diào)用這些篩選器。 有關(guān)更多信息,請參見ASP.NET Core 中的篩選器。
提示
異常篩選器適合捕獲 MVC 操作內(nèi)發(fā)生的異常,但它們不如異常處理中間件靈活。 建議使用中間件。 僅在需要根據(jù)所選 MVC 操作以不同方式執(zhí)行錯誤處理時,才使用篩選器。
模型驗(yàn)證在每個控制器操作被調(diào)用之前發(fā)生,操作方法負(fù)責(zé)檢查 ModelState.IsValid 并相應(yīng)地作出反應(yīng)。
某些應(yīng)用使用標(biāo)準(zhǔn)約定處理模型驗(yàn)證錯誤,在這種情況下,使用篩選器可以更好地實(shí)施這種策略。你需要使用無效模型狀態(tài)測試操作的行為。 有關(guān)更多信息,請參見ASP.NET Core 中的測試控制器邏輯。
更多建議: