溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

ASP.NET?Core全局異常處理方法是什么

發(fā)布時(shí)間:2022-03-28 09:13:15 來源:億速云 閱讀:676 作者:iii 欄目:開發(fā)技術(shù)

這篇“ASP.NET Core全局異常處理方法是什么”文章的知識點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“ASP.NET Core全局異常處理方法是什么”文章吧。

一、前言

在程序設(shè)計(jì)中,我們會遇到各種各樣的異常問題,一個(gè)好的異常處理解決方案能夠幫助開發(fā)者快速的定位問題,也能夠給用戶更好的用戶體驗(yàn)。那么我們在AspNetCore中該如何捕獲和處理異常呢?我們以一個(gè)WebApi項(xiàng)目為例,講解如何捕獲和處理異常。

二、異常處理

1、異常處理

開發(fā)過ASP.NET程序的人都知道:IExceptionFilter。這個(gè)過濾器同樣在AspNetCore中也可以用來捕獲異常。不過,對于使用IExceptionFilter,更建議使用它的異步版本:IAsyncExceptionFilter。那么該如何使用過濾器呢?下面以IAsyncExceptionFilter為例,對于同步版本其實(shí)也是一樣的。

我們在項(xiàng)目中添加一個(gè)Model文件夾,存放返回結(jié)果實(shí)體類,這里定義一個(gè)泛型類:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ExceptionDemo.Model
{
    public class ResultModel<T>
    {
        /// <summary>
        /// 返回結(jié)果編碼 0:失敗 1:成功
        /// </summary>
        public int ResultCode { get; set; }

        /// <summary>
        /// 返回結(jié)果內(nèi)容 成功:Success  失?。寒惓?nèi)容
        /// </summary>
        public string ResultMsg { get; set; }

        /// <summary>
        /// 返回結(jié)果 成功:返回T類型數(shù)據(jù) 失敗:默認(rèn)null
        /// </summary>
        public T ResultData { get; set; }
    }
}

我們在項(xiàng)目中添加一個(gè)Filter文件夾,所有的過濾器都放在該文件夾下面。然后添加一個(gè)類:CustomerExceptionFilter,并使該類繼承自IAsyncExceptionFilter。代碼如下:

using ExceptionDemo.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using System.Threading.Tasks;

namespace ExceptionDemo.Filter
{
    /// <summary>
    /// 自定義異常過濾器
    /// </summary>
    public class CustomerExceptionFilter : IAsyncExceptionFilter
    {
        /// <summary>
        /// 重寫OnExceptionAsync方法,定義自己的處理邏輯
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public Task OnExceptionAsync(ExceptionContext context)
        {
            // 如果異常沒有被處理則進(jìn)行處理
            if(context.ExceptionHandled==false)
            {
                // 定義返回類型
                var result = new ResultModel<string>
                {
                    ResultCode = 0,
                    ResultMsg = context.Exception.Message
                };
                context.Result = new ContentResult
                {
                    // 返回狀態(tài)碼設(shè)置為200,表示成功
                      StatusCode = StatusCodes.Status200OK,
                      // 設(shè)置返回格式
                      ContentType="application/json;charset=utf-8",
                      Content=JsonConvert.SerializeObject(result)
                };
            }
            // 設(shè)置為true,表示異常已經(jīng)被處理了
            context.ExceptionHandled = true;
            return Task.CompletedTask;
        }
    }
}

上面的代碼很簡單,我們新建了一個(gè)自定義的異常過濾器,然后在OnExceptionAsync方法中定義自己的處理邏輯,報(bào)錯之后依然讓http返回狀態(tài)碼為200,并且將錯誤信息返回到客戶端。

然后添加一個(gè)控制器,命名為ExceptionFilter,在控制器中模擬發(fā)生異常的情況:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ExceptionDemo.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace ExceptionDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ExceptionFilterController : ControllerBase
    {
        [HttpGet]
        public async Task<ResultModel<int>> Get()
        {
            int i = 0;
            int k = 10;
            // 這里會發(fā)生異常
            int j = await Task.Run<int>(() => 
            {
                return k / i;
            });


            return new ResultModel<int>()
            {
                ResultCode=1,
                ResultMsg="Success",
                ResultData=j
            };
        }
    }
}

最后我們需要把自定義的異常過濾器進(jìn)行注入,這里選擇使用全局注入的方式,在Startup類的ConfigureServices方法中進(jìn)行注入:

services.AddControllers(options => 
{
      options.Filters.Add(new CustomerExceptionFilter());
});

然后運(yùn)行程序,查看結(jié)果:

ASP.NET?Core全局異常處理方法是什么

如何我們沒有使用過濾器捕獲和處理異常,我們將得到Http狀態(tài)碼為500的內(nèi)部錯誤,這種錯誤不方便定位問題,而且給客戶端返回的信息也不夠友好。使用了過濾器處理異常,進(jìn)行特殊處理之后就會顯得很友好了。

在上面自定義過濾器的代碼中,有下面的一行代碼:

context.ExceptionHandled = true;

注意:這句代碼很關(guān)鍵,當(dāng)你處理完異常之后,一定要將此屬性更改為true,表示異常已經(jīng)處理過了,這樣其他地方就不會在處理這個(gè)異常了。 

2、使用中間件處理異常

我們知道,AspNetCore的管道模型具有層層傳遞的特點(diǎn),那么我們就可以在管道中實(shí)現(xiàn)全局異常捕獲。我們新創(chuàng)建一個(gè)自定義的異常中間件:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Text.Json;
using System.Threading.Tasks;

namespace ExceptionDemo.Middleware
{
    /// <summary>
    /// 自定義異常中間件
    /// </summary>
    public class CustomerExceptionMiddleware
    {
        /// <summary>
        /// 委托
        /// </summary>
        private readonly RequestDelegate _next;

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

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {

                context.Response.ContentType = "application/problem+json";

                var title = "An error occured: " + ex.Message;
                var details = ex.ToString();

                var problem = new ProblemDetails
                {
                    Status = 200,
                    Title = title,
                    Detail = details
                };

                var stream = context.Response.Body;
                await JsonSerializer.SerializeAsync(stream, problem);
            }
        }
    }
}

然后在新建一個(gè)擴(kuò)展方法:

using Microsoft.AspNetCore.Builder;

namespace ExceptionDemo.Middleware
{
    /// <summary>
    /// 靜態(tài)類
    /// </summary>
    public static class ExceptionMiddlewareExtension
    {
        /// <summary>
        /// 靜態(tài)方法
        /// </summary>
        /// <param name="app">要進(jìn)行擴(kuò)展的類型</param>
        public static void UseExceptionMiddleware(this IApplicationBuilder app)
        {
            app.UseMiddleware(typeof(CustomerExceptionMiddleware));
        }
    }
}

最后在Startup類的Configure方法中使用自定義的異常中間件:

app.UseExceptionMiddleware();

然后我們注釋掉上面注冊的異常過濾器,運(yùn)行程序進(jìn)行訪問:

ASP.NET?Core全局異常處理方法是什么

這樣也可以捕獲到異常。

3、使用框架自帶異常中間件

 我們首先看下面一段代碼:

if (env.IsDevelopment())
{
       app.UseDeveloperExceptionPage();
}

這段代碼在我們使用AspNetCore創(chuàng)建一個(gè)WebApi項(xiàng)目時(shí)就會看到,如果是創(chuàng)建的MVC項(xiàng)目,是下面一段代碼:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
}

這兩段代碼的作用就是捕獲和處理異常,是第一個(gè)被添加到管道中的中間件。

UseDeveloperExceptionPage的意思很好理解:對于開發(fā)模式,一旦報(bào)錯就跳轉(zhuǎn)到錯誤堆棧頁面。而第二個(gè)UseExceptionHandler也很有意思,從它的名字中我們大致可以猜出它肯定是個(gè)錯誤攔截程序。那么它和上面自定義的異常處理中間件有什么區(qū)別呢?

UseExceptionHandler其實(shí)就是默認(rèn)的錯誤處理。它其實(shí)也是一個(gè)中間件,它的原名叫做ExceptionHandlerMiddleware。在使用UseExceptionHandler方法時(shí),我們可以選填各種參數(shù)。比如上面的第二段代碼,填入了“/Error”參數(shù),表示當(dāng)產(chǎn)生異常的時(shí)候,將定位到對應(yīng)的路徑,這里定位的頁面就是“http://localhost:5001/Error”。這是MVC中自帶的一個(gè)錯誤頁面,當(dāng)然,你也可以指定自己定義的一個(gè)頁面。

UseExceptionHandler還有一個(gè)指定ExceptionHandlerOptions參數(shù)的擴(kuò)展方法,該參數(shù)是ExceptionHandlerMiddleware中間件的重要參數(shù):

參數(shù)名說明
ExceptionHandlingPath重定向的路徑,比如剛才的 ""/Error"" 實(shí)際上就是指定的該參數(shù)
ExceptionHandler錯誤攔截處理程序

ExceptionHandler允許我們在ExceptionHandlerMiddleware內(nèi)部指定咱們自己的異常處理邏輯。而該參數(shù)的類型為RequestDelegate類型的委托。因此,UseExceptionHandler提供了一個(gè)簡便的寫法,可以讓我們在ExceptionHandlerMiddleware中新建自定義的錯誤攔截管道來處理異常:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using ExceptionDemo.Filter;
using ExceptionDemo.Middleware;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace ExceptionDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            #region 注冊全局異常過濾器
            //services.AddControllers(options => 
            //{
            //    options.Filters.Add(new CustomerExceptionFilter());
            //});
            #endregion

            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler(builder => builder.Use(ExceptionHandlerDemo));
            }

            
            app.UseExceptionMiddleware();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

        private async Task ExceptionHandlerDemo(HttpContext httpContext,Func<Task> next)
        {
            //該信息由ExceptionHandlerMiddleware中間件提供,里面包含了ExceptionHandlerMiddleware中間件捕獲到的異常信息。
            var exceptionDetails = httpContext.Features.Get<IExceptionHandlerFeature>();
            var ex = exceptionDetails?.Error;

            if (ex != null)
            {
                httpContext.Response.ContentType = "application/problem+json";

                var title = "An error occured: " + ex.Message;
                var details = ex.ToString();

                var problem = new ProblemDetails
                {
                    Status = 500,
                    Title = title,
                    Detail = details
                };

                var stream = httpContext.Response.Body;
                await JsonSerializer.SerializeAsync(stream, problem);
            }
        }
    }
}

三、中間件和過濾器的比較

在上面的例子中,我們分別使用了中間件和過濾器的方式來處理異常,那么中間件和過濾器有什么區(qū)別呢?兩者的區(qū)別:攔截范圍的不同。

ASP.NET?Core全局異常處理方法是什么

IExceptionFilter作為一種過濾器,它需要在控制器發(fā)現(xiàn)錯誤之后將錯誤信息提交給它處理,因此它的異常處理范圍是控制器內(nèi)部。如果我們想捕獲進(jìn)入控制器之前的一些錯誤,IExceptionFilter是捕獲不到的。而對于ExceptionHandlerMiddleware異常中間件來說就很容易了,它作為第一個(gè)中間件被添加到管道中,在它之后發(fā)生的任何異常都可以捕獲的到。

那么為什么要有兩種異常處理的方式呢?只使用ExceptionHandlerMiddleware中間件處理異常不可以嗎?它可以捕獲任何時(shí)候發(fā)生的異常,為什么還要有過濾器呢?如果你想在控制器發(fā)生異常時(shí)快速捕獲和處理異常,那么使用過濾器處理異常是非常不錯的選擇。如果是控制器內(nèi)部發(fā)生了異常,首先是由過濾器捕獲到異常,最后才是中間件捕獲到異常。

我們在自定義過濾器的時(shí)候有這樣一段代碼:context.ExceptionHandled = true;如果在自定義過濾器中將異常標(biāo)記為已經(jīng)處理之后,則第一個(gè)異常處理中間件就認(rèn)為沒有錯誤了,不會進(jìn)入到處理邏輯中了。所以,如果不把 ExceptionHandled屬性設(shè)置為true,可能出現(xiàn)異常處理結(jié)果被覆蓋的情況。

以上就是關(guān)于“ASP.NET Core全局異常處理方法是什么”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI