處理 ASP.NET Core 中的錯誤

2019-04-17 08:59 更新

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

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

開發(fā)人員異常頁

要將應用配置為顯示有關請求異常的詳細信息的頁面,請使用“開發(fā)人員異常頁”。 該頁面通過 Microsoft.AspNetCore.App 元包中的 Microsoft.AspNetCore.Diagnostics 包提供。 當應用在開發(fā)環(huán)境中運行時,在 Startup.Configure 方法中添加一行:

C#

app.UseDeveloperExceptionPage();

將對 UseDeveloperExceptionPage 的調(diào)用放在要對其捕獲異常的任何中間件前面。

 警告

僅當應用程序在開發(fā)環(huán)境中運行時才啟用開發(fā)人員異常頁。 否則當應用程序在生產(chǎn)環(huán)境中運行時,詳細的異常信息會向公眾泄露 有關配置環(huán)境的詳細信息,請參閱 在 ASP.NET Core 中使用多個環(huán)境。

要查看開發(fā)人員異常頁,請將環(huán)境設置為 Development,運行示例應用,并向應用的基 URL 添加 ?throw=true。 該頁包括關于異常和請求的以下信息:

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

配置自定義異常處理頁

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

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

在示例應用的以下示例中,UseExceptionHandler 在非開發(fā)環(huán)境中添加了異常處理中間件。 在捕獲并記錄異常后,擴展方法指定 /Error 終結點處重新執(zhí)行了請求的錯誤頁或控制器:

C#

app.UseExceptionHandler("/Error");

Razor Pages 應用模板在“Pages”文件夾中提供了一個錯誤頁 (.cshtml) 和 PageModel 類 (ErrorModel)。

在 MVC 應用中,以下錯誤處理程序方法包含在 MVC 應用模板中并在主控制器中顯示:

C#

[AllowAnonymous]
public IActionResult Error()
{
    return View(new ErrorViewModel 
        { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}

不要使用 HTTP 方法屬性(如 HttpGet)修飾錯誤處理程序操作方法。 顯式謂詞可阻止某些請求訪問方法。 允許匿名訪問方法,以便未經(jīng)身份驗證的用戶能夠接收錯誤視圖。

訪問異常

使用 IExceptionHandlerPathFeature 訪問控制器或頁中的異常或原始請求路徑:

C#

// using Microsoft.AspNetCore.Diagnostics;

var exceptionHandlerPathFeature = 
    HttpContext.Features.Get<IExceptionHandlerPathFeature>();
var path = exceptionHandlerPathFeature?.Path;
var error = exceptionHandlerPathFeature?.Error;

 警告

不要向客戶端提供來自 IExceptionHandlerFeature 或 IExceptionHandlerPathFeature 的敏感錯誤信息。 提供服務的錯誤是一種安全風險。

配置自定義異常處理代碼

使用自定義異常處理頁為錯誤提供終結點的另一種方法是為 UseExceptionHandler 提供 lambda。 使用帶 UseExceptionHandler 的 lambda 允許在返回響應之前訪問錯誤。

示例應用演示了 Startup.Configure 中的自定義異常處理代碼。 使用“索引”頁上的“引發(fā)異?!辨溄佑|發(fā)異常。 運行如下 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 的敏感錯誤信息。 提供服務的錯誤是一種安全風險。

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

默認情況下,ASP.NET Core 應用不會為 HTTP 狀態(tài)代碼(如“404 - 未找到”)提供狀態(tài)代碼頁。 應用返回狀態(tài)代碼和空響應正文。 要提供狀態(tài)代碼頁,請使用狀態(tài)代碼頁中間件。

該中間件通過 Microsoft.AspNetCore.App 元包中的 Microsoft.AspNetCore.Diagnostics 包提供。

向 Startup.Configure 方法添加代碼行:

C#

app.UseStatusCodePages();

在請求處理中間件(例如,靜態(tài)文件中間件和 MVC 中間件)之前調(diào)用 UseStatusCodePages 方法。

默認情況下,狀態(tài)代碼頁中間件為常見狀態(tài)代碼(如“404 - 未找到”)添加純文本處理程序:

Status Code: 404; Not Found

中間件支持幾種可用于自定義其行為的擴展方法。

重載 UseStatusCodePages 采用 lambda 表達式,可用于處理自定義錯誤處理邏輯和手動編寫響應:

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)容類型和響應文本:

C#

app.UseStatusCodePages("text/plain", "Status code page, status code: {0}");

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

UseStatusCodePagesWithRedirects

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

C#

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

通常,應用在以下情況下使用 UseStatusCodePagesWithRedirects

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

UseStatusCodePagesWithReExecute

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

C#

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

應用在以下情況下通常使用 UseStatusCodePagesWithReExecute

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

模板可能包括狀態(tài)代碼的占位符 ({0})。 模板必須以正斜杠 (/) 開頭。 使用占位符時,請確認終結點(頁或控制器)可以處理路徑段。 例如,錯誤的 Razor Page 應通過 @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;
}

若要在應用中使用指向終結點的 UseStatusCodePages 重載,請為終結點創(chuàng)建 MVC 視圖或 Razor Page。 例如,Razor Pages 應用模板將生成以下頁和頁模型類:

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ā)送響應標頭后:

  • 應用無法更改響應的狀態(tài)代碼。
  • 任何異常頁或處理程序都無法運行。 必須完成響應或中止連接。

服務器異常處理

除應用中的異常處理邏輯外,服務器實現(xiàn)也可以處理一些異常。 如果服務器在發(fā)送響應標頭之前捕獲到異常,服務器將發(fā)送不包含響應正文的“500 - 內(nèi)部服務器錯誤”響應。 如果服務器在發(fā)送響應標頭后捕獲到異常,服務器會關閉連接。 應用程序無法處理的請求將由服務器進行處理。 當服務器處理請求時,發(fā)生的任何異常都將由服務器的異常處理進行處理。 應用的自定義錯誤頁面、異常處理中間件和篩選器都不會影響此行為。

啟動異常處理

應用程序啟動期間發(fā)生的異常僅可在承載層進行處理。 借助 Web 主機,可以使用 captureStartupErrors 和 detailedErrors 鍵配置主機在響應啟動期間出現(xiàn)錯誤時的行為方式。

僅當捕獲的啟動錯誤發(fā)生在主機地址/端口綁定之后,承載層才會為該錯誤顯示錯誤頁。 如果出于任何原因導致任何綁定失?。?/p>

  • 托管層將記錄關鍵異常。
  • dotnet 進程崩潰。
  • 當應用在 Kestrel 服務器上運行時,不會顯示錯誤頁。

在 IIS 或 IIS Express 上運行應用時,如果無法啟動進程,ASP.NET Core 模塊將返回“502.5 - 進程失敗”。 有關更多信息,請參見對 IIS 上的 ASP.NET Core 進行故障排除。 要了解如何排查 Azure 應用服務的啟動問題,請參閱 對 Azure 應用服務上的 ASP.NET Core 進行故障排除

ASP.NET Core MVC 錯誤處理

MVC 應用還有一些其他的錯誤處理選項,例如配置異常篩選器和執(zhí)行模型驗證。

異常篩選器

在 MVC 應用中,異常篩選器可以進行全局配置,也可以為每個控制器或每個操作單獨配置。 這些篩選器處理在執(zhí)行控制器操作或其他篩選器時出現(xiàn)的任何未處理的異常。 不會以其他方式調(diào)用這些篩選器。 有關更多信息,請參見ASP.NET Core 中的篩選器

 提示

異常篩選器適合捕獲 MVC 操作內(nèi)發(fā)生的異常,但它們不如異常處理中間件靈活。 建議使用中間件。 僅在需要根據(jù)所選 MVC 操作以不同方式執(zhí)行錯誤處理時,才使用篩選器。

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

模型驗證在每個控制器操作被調(diào)用之前發(fā)生,操作方法負責檢查 ModelState.IsValid 并相應地作出反應。

某些應用使用標準約定處理模型驗證錯誤,在這種情況下,使用篩選器可以更好地實施這種策略。你需要使用無效模型狀態(tài)測試操作的行為。 有關更多信息,請參見ASP.NET Core 中的測試控制器邏輯


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號