處理 ASP.NET Core 中的錯誤

2019-04-17 08:59 更新

本文介紹了處理 ASP.NET Core 應(yīng)用中常見錯誤的一些方法。

查看或下載示例代碼如何下載

開發(fā)人員異常頁

要將應(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)于異常和請求的以下信息:

  • 堆棧跟蹤
  • 查詢字符串參數(shù)(如果有)
  • Cookie(如果有)
  • 標(biāo)頭

配置自定義異常處理頁

如果應(yīng)用未在開發(fā)環(huán)境中運(yùn)行,則調(diào)用 UseExceptionHandler 擴(kuò)展方法,添加異常處理中間件。 中間件:

  • 捕獲異常。
  • 記錄異常。
  • 在備用管道中為指定的頁或控制器重新執(zhí)行請求。 如果響應(yīng)已啟動,則不會重新執(zhí)行請求。

在示例應(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)。

配置狀態(tài)代碼頁

默認(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}");

重定向和重新執(zhí)行擴(kuò)展方法

UseStatusCodePagesWithRedirects

  • 向客戶端發(fā)送“302 - 已找到”狀態(tài)代碼。
  • 將客戶端重定向到 URL 模板中的位置。

C#

app.UseStatusCodePagesWithRedirects("/Error/{0}");

通常,應(yīng)用在以下情況下使用 UseStatusCodePagesWithRedirects

  • 應(yīng)將客戶端重定向到不同的終結(jié)點(diǎn)(通常在不同的應(yīng)用處理錯誤的情況下)。 對于 Web 應(yīng)用,客戶端的瀏覽器地址欄反映重定向終結(jié)點(diǎn)。
  • 不應(yīng)保留原始狀態(tài)代碼并通過初始重定向響應(yīng)返回該代碼。

UseStatusCodePagesWithReExecute

  • 向客戶端返回原始狀態(tài)代碼。
  • 通過使用備用路徑重新執(zhí)行請求管道,從而生成響應(yīng)正文。

C#

app.UseStatusCodePagesWithReExecute("/Error/{0}");

應(yīng)用在以下情況下通常使用 UseStatusCodePagesWithReExecute

  • 處理請求,但不重定向到不同終結(jié)點(diǎn)。 對于 Web 應(yīng)用,客戶端的瀏覽器地址欄反映最初請求的終結(jié)點(diǎn)。
  • 保留原始狀態(tài)代碼并通過響應(yīng)返回該代碼。

模板可能包括狀態(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)用無法更改響應(yīng)的狀態(tài)代碼。
  • 任何異常頁或處理程序都無法運(yùn)行。 必須完成響應(yīng)或中止連接。

服務(wù)器異常處理

除應(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>

  • 托管層將記錄關(guān)鍵異常。
  • dotnet 進(jìn)程崩潰。
  • 當(dāng)應(yīng)用在 Kestrel 服務(wù)器上運(yùn)行時,不會顯示錯誤頁。

在 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)行故障排除。

ASP.NET Core MVC 錯誤處理

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í)行錯誤處理時,才使用篩選器。

處理模型狀態(tài)錯誤

模型驗(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 中的測試控制器邏輯


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號