處理 ASP.NET Core 中的錯(cuò)誤

2019-04-17 08:59 更新

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

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

開(kāi)發(fā)人員異常頁(yè)

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

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

配置自定義異常處理頁(yè)

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

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

在示例應(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ò)誤視圖。

訪問(wèn)異常

使用 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)。

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

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

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

UseStatusCodePagesWithRedirects

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

C#

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

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

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

UseStatusCodePagesWithReExecute

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

C#

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

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

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

模板可能包括狀態(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ú)法更改響應(yīng)的狀態(tài)代碼。
  • 任何異常頁(yè)或處理程序都無(wú)法運(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ù)器錯(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ì)影響此行為。

啟動(dòng)異常處理

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

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

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

ASP.NET Core MVC 錯(cuò)誤處理

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í),才使用篩選器。

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

模型驗(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è)試控制器邏輯。


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)