Loading

asp.net core之异常处理

在开发过程中,处理错误是一个重要的方面。ASP.NET Core提供了多种方式来处理错误,以确保应用程序的稳定性和可靠性。

TryCatch

TryCatch是最常见也是最基础的一种异常处理方式,只需要用TryCatch把执行代码包起来,即可捕获异常。
格式如下:

try
{
    // 执行操作
    doAny();
}
catch (Exception ex)
{
    // 处理异常
    doExceptionHandling();
}

这属于最基本的异常处理方式,这里就不加上实操代码了。本文主要讲解asp.net core中的其他异常处理方式。

开发人员异常页

ASP.NET Core Web应用在以下情况下默认启用开发人员异常页,用于显示未经处理的请求异常的详细信息。
ASP.NET Core 应用在以下情况下默认启用开发人员异常页:

  • 在开发环境中运行。
  • 使用当前模板创建的应用,即使用 WebApplication.CreateBuilder。 使用 WebHost.CreateDefaultBuilder 创建的应用必须通过在 Configure 中调用 app.UseDeveloperExceptionPage 来启用开发人员异常页。

开发人员异常页运行在中间件管道的前面部分,以便它能够捕获随后中间件中抛出的未经处理的异常。
这里我们新建一个MVC项目,使用WebApplication.CreateBuilder,所以不需要显式调用app.UseDeveloperExceptionPage 来启用开发人员异常页,在HomeController中添加一个Thorw方法直接抛出异常:

using LearnException.Models;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;

namespace LearnException.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }
        public IActionResult Throw()
        {
            throw new Exception("Customer Excetion");
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

启动项目,然后访问/Home/Throw路径。
image.png
可以看到这个页面可以看到我们的详细错误信息,包括异常栈,Query参数, Cookies参数,HTTP请求Headers信息以及路由信息。
这个页面在开发阶段,非常利于我们排查错误。

异常处理程序页

由于我们的一些异常信息不便在非开发环境展示,所以在非开发环境,我们需要一个异常处理程序页。
若要为生产环境配置自定义错误处理页,请调用 UseExceptionHandler。 此异常处理中间件:

  • 捕获并记录未经处理的异常。
  • 使用指示的路径在备用管道中重新执行请求。 如果响应已启动,则不会重新执行请求。 模板生成的代码使用 /Home/Error 路径重新执行请求。

在我们创建的MVC模板的Program中,有这样的代码:

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
}

表示在非开发环境中启用此异常处理中间件。这里的"/Home/Error"表示跳转到该路由。该路由为异常处理页面。
在模板Views/Shared下面我们可以看到一个Error.cshtml,和Models下面有一个ErrorViewModel,这就是默认的异常处理程序页。
在上面HomeController的代码中我们可以看到一个Error的Action,此Action指向Error页面。我们试试直接起启用app.UseExceptionHandler("/Home/Error"),放开在非开发环境才使用的条件:

//if (!app.Environment.IsDevelopment())
//{
    app.UseExceptionHandler("/Home/Error");
//}

分别请求/Home/Error和/Home/Throw路径
image.png
image.png
可以看到/Home/Throw也是跳转到Error页面,但是没有详细的异常信息。

自定义异常处理程序页

除了上述的方式,我们在需要自定义异常处理程序页时,可以使用app.UseExceptionHandler的另一个重载方法:

app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

在上面代码中exceptionHandlerApp是一个IApplicationBuilder,本质是添加一个终结点中间件去处理响应内容,上面内容包括了修改Http响应的StatusCode,ContentType,以及响应内容。
我们在HomeController中继续添加一个FileNotFound的Action。

public IActionResult FileNotFound()
{
	throw new FileNotFoundException();
}

启动项目,分别请求/Home/Throw和/Home/FileNotFound。
image.png
image.png
可以看到,响应内容和我们配置的一致。
由上面表现,其实我们可以想到,如何自定义一个异常处理中间件。

 public class MyExceptionMiddleware
{
    private readonly RequestDelegate _next;

    public MyExceptionMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");


            if (ex is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }
        }
    }
}

在Program添加中间件

app.UseMiddleware<MyExceptionMiddleware>();

image.png
可以看到,效果完全一致。
除此之外,我们还有使用ExceptionFilter的方式去处理异常,只要实现实现 IExceptionFilter 或 IAsyncExceptionFilter即可。
添加一个MyExceptionFilter

public class MyExceptionFilter : IAsyncExceptionFilter
{
    public async Task OnExceptionAsync(ExceptionContext context)
    {
        context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;

        // using static System.Net.Mime.MediaTypeNames;
        context.HttpContext.Response.ContentType = Text.Plain;

        await context.HttpContext.Response.WriteAsync("An exception was thrown. by MyExceptionFilter");
    	context.ExceptionHandled = true;
    }
}

在HomeController添加一个Filter的Action。

[TypeFilter(typeof(MyExceptionFilter))]
public IActionResult Filter()
{
    throw new Exception("MyExceptionFilter Excetion");
}

启动项目,访问/Home/Filter路径。
image.png
可以看到效果跟预想的一致。

ASP.NET Core提供了多种方式来处理错误。开发人员可以根据具体的需求选择适合的错误处理方式,并进行相应的处理和响应。通过合理的错误处理,可以提高应用程序的稳定性和可靠性,提供更好的用户体验。

欢迎进群催更。

image.png

posted @ 2023-08-03 15:43  饭勺oO  阅读(828)  评论(4编辑  收藏  举报