Asp.Net Core異常處理整理

目前版本是Asp.Net Core v1.1,這個版本的感受對Http請求中的錯誤處理方便不是很完善。html

沒有HttpException異常類,不能在任何的地方自由的拋出對應的異常狀態。json

1、默認的異常處理配置app

1.默認配置在StartUp文件的Configure中註冊錯誤處理async

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

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

特別說明:ide

1.env.IsDevelopment()  用於判斷當前運行環境是不是測試環境post

2.app.UseDeveloperExceptionPage(); 若是是測試環境則啓用開發者異常頁面測試

開發者異常頁面ui

當在Web處理管道放生了一個位置異常時,開發者異常頁面會顯示有用的調試信息。頁面包含幾個選項卡顯示Stack、Query、Cookie、Header異常信息。this

3.app.UseExceptionHandler("/Home/Error"); 在正式運行環境中輸出異常的內容信息。編碼

4.默認的異常處理對於404沒有處理,若是是404的地址則返回空內容。

5.業務邏輯404的拋出

使用狀態視圖

public IActionResult Test2()
{
    //手動拋出404 頁面
    return StatusCode(404, "你要訪問的頁面不存在");
}

或者修改相應上下文的狀態碼

@{
    //自定義拋出404異常
    this.Context.Response.StatusCode = 404;
}
<h1>404狀態的頁面</h1>

2、錯誤處理擴展,使用委託

1.

 

//此處的404頁面,只能用於處理路由中不匹配的404頁面
//若是是程序邏輯中的404不予以處理
//而且對於程序中Exception 也不作處理,直接相應500,內容爲空
RequestDelegate handler = async context =>
{
    var resp = context.Response;
    if (resp.StatusCode == 404)
    {
        await resp.WriteAsync($"當前是404頁面,暫時沒有獲取到異常內容");
    }
};
app.UseStatusCodePages(builder =>
{
    builder.Run(handler);
});

 

2.

//使用StatusCodePagesMiddleware 指定狀態對應的顯示頁面
Func<StatusCodeContext, Task> handler = async context =>
{
    var resp = context.HttpContext.Response;
    if (resp.StatusCode == 404)
    {
        //須要 using Microsoft.AspNetCore.Http;
        await resp.WriteAsync($"當前是404頁面,暫時沒有獲取到異常內容", Encoding.UTF8);
    }
    else if (resp.StatusCode == 500)
    {
        await resp.WriteAsync($"當前是500頁面,暫時沒有獲取到異常內容", Encoding.UTF8);
    }
};
app.UseStatusCodePages(handler);

 

 

3、錯誤處理擴展,使用自定義擴展類

Asp.Net Core三種呈現錯誤也的方式均可以擴展

1.DeveloperExceptionPageMiddleware中間件的擴展能夠自定義開發者異常頁面,當前略過

2.ExceptionHandlerMiddleware中間件,能夠自定義擴展異常處理

示例,處理異常並制定異常的響應頁面

public class ErrorHandlingMiddleware
{
    private RequestDelegate _next;
    private ExceptionHandlerOptions _options;

    public ErrorHandlingMiddleware(RequestDelegate next, IOptions<ExceptionHandlerOptions> options)
    {
        _next = next;
        _options = options.Value;
    }
    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch
        {
            //雖然此處指定500,可是出現異常,再次執行異常頁面則響應的是200
            context.Response.StatusCode = 500;
            context.Response.Clear();
            if (_options.ExceptionHandlingPath.HasValue)
            {
                context.Request.Path = _options.ExceptionHandlingPath;
            }
            RequestDelegate handler = _options.ExceptionHandler ?? _next;
            await handler(context);
        }
    }
}

使用擴展方法爲IApplicationBuilder擴展內容

public static class ErrorHandlingExtensions
{
    public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder, ExceptionHandlerOptions options)
    {
        return builder.UseMiddleware<ErrorHandlingMiddleware>(Options.Create(options));
    }
}

在 Configure中註冊當前錯誤處理

//使用自定義異常處理
app.UseErrorHandling(new ExceptionHandlerOptions()
{
    //注意此處的路徑不能使用~符號
    ExceptionHandlingPath = "/Home/Error"
});

3.StatusCodePagesMiddleware中間件能夠根據正常的相應內容(沒有拋出異常的狀況下) 擴展狀態相應頁面

public interface IStatusCodePagesFeature
{
    bool Enabled { get; set; }
}

public class StatusCodePagesFeature : IStatusCodePagesFeature
{
    public bool Enabled { get; set; } = true;
}
public class StatusCodePagesMiddleware
{
    private RequestDelegate _next;
    private StatusCodePagesOptions _options;
    public StatusCodePagesMiddleware(RequestDelegate next,
        IOptions<StatusCodePagesOptions> options)
    {
        _next = next;
        _options = options.Value;
    }
    public async Task Invoke(HttpContext context)
    {
        //能夠用於阻止異常處理
        StatusCodePagesFeature feature = new StatusCodePagesFeature();
        context.Features.Set<IStatusCodePagesFeature>(feature);

        await _next(context);
        HttpResponse response = context.Response;
        //對於響應的狀態編碼,返回內容
        //if ((response.StatusCode >= 400 && response.StatusCode <= 599) && !response.ContentLength.HasValue && string.IsNullOrEmpty(response.ContentType))
        if (response.StatusCode >= 400 && response.StatusCode <= 599 && feature.Enabled)
        {
            //觸發 默認處理
            //await _options.HandleAsync(new StatusCodeContext(context, _options, _next));

            //對於非異常的頁面,執行Clear拋出異常
            //response.Clear();

            //response.Body.Seek(0, SeekOrigin.Begin);

            await response.WriteAsync($"當前HttpCode={response.StatusCode}");
        }
    }

}

使用擴展方法註冊中間件

public static class ErrorHandlingExtensions
{//註冊3中處理方式
    public static IApplicationBuilder UseStatusCodeHandling(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<StatusCodePagesMiddleware>();
    }
    public static IApplicationBuilder UseStatusCodeHandling(this IApplicationBuilder app, StatusCodePagesOptions options)
    {
        return app.UseMiddleware<StatusCodePagesMiddleware>(Options.Create(options));
    }
    public static IApplicationBuilder UseStatusCodeHandling(this IApplicationBuilder app, Func<StatusCodeContext, Task> handler)
    {
        return app.UseStatusCodePages(new StatusCodePagesOptions
        {
            HandleAsync = handler
        });
    }
}

在Configure中註冊使用

Func<StatusCodeContext, Task> handler = async context =>
{
    var resp = context.HttpContext.Response;
    //在如下的相應數據時,會清空原頁面的相應內容
    if (resp.StatusCode == 404)
    {
        //須要 using Microsoft.AspNetCore.Http;
        await resp.WriteAsync($"當前是404頁面,暫時沒有獲取到異常內容", Encoding.UTF8);
    }
    else if (resp.StatusCode == 500)
    {
        await resp.WriteAsync($"當前是500頁面,暫時沒有獲取到異常內容", Encoding.UTF8);
    }
};
app.UseStatusCodeHandling(handler);

4.能夠在擴展中綜合使用處理,以下示例

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate next;
    private ExceptionHandlerOptions _options;
    public ErrorHandlingMiddleware(RequestDelegate next, IOptions<ExceptionHandlerOptions> options)
    {
        this.next = next;
        this._options = options.Value;
    }
    public async Task Invoke(HttpContext context)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            var statusCode = context.Response.StatusCode;
            if (ex is ArgumentException)
            {
                statusCode = 200;
            }
            await HandleExceptionAsync(context, statusCode, ex.Message);
        }
        finally
        {
            var statusCode = context.Response.StatusCode;
            var msg = "";
            if (statusCode == 401)
            {
                msg = "未受權";
            }
            else if (statusCode == 404)
            {
                //msg = "未找到服務";
                //若是是404 返回404頁面
                if (_options.ExceptionHandlingPath.HasValue)
                {
                    context.Request.Path = _options.ExceptionHandlingPath;
                }
                RequestDelegate handler = _options.ExceptionHandler ?? next;
                await handler(context);
            }
            else if (statusCode == 502)
            {
                msg = "請求錯誤";
            }
            else if (statusCode != 200)
            {
                msg = "未知錯誤";
            }
            if (!string.IsNullOrWhiteSpace(msg))
            {
                await HandleExceptionAsync(context, statusCode, msg);
            }
        }
    }
    private static Task HandleExceptionAsync(HttpContext context, int statusCode, string msg)
    {
        var data = new { code = statusCode.ToString(), is_success = false, msg = msg };
        var result = JsonConvert.SerializeObject(new { data = data });
        try
        {
            //特別說明ContentType屬性在 HttpResponse初始化完成以後就不能修改了
            //若是試圖修改則拋出異常
            //異常內容:Headers are read-only, response has already started.
            //context.Response.ContentType = "application/json;charset=utf-8";


            //特別說明對於準備輸出的Response,執行Clear()清空拋出異常
            //The response cannot be cleared, it has already started sending.
            context.Response.Clear();

            //判斷輸出流是否已經開始
            //context.Response.HasStarted
        }
        catch (Exception ex)
        {
            //throw ex;
        }
        //清楚已經完成的相應內容

        return context.Response.WriteAsync(result);
    }
}
View Code

原文參考:http://www.cnblogs.com/artech/p/error-handling-in-asp-net-core-1.html

更多:

Restful API 的設計規範(轉)

.Net Core配置文件讀取整理

EF Core 1.0 和 SQLServer 2008 分頁的問題

相關文章
相關標籤/搜索