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