Asp.Net Core异常处理整理

目前版本是Asp.Net Core v1.1,这个版本的感觉对Http请求中的错误处理方便不是很完善。

没有HttpException异常类,不能在任何的地方自由的抛出对应的异常状态。

一、默认的异常处理配置

1.默认配置在StartUp文件的Configure中注册错误处理

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

特别说明:

1.env.IsDevelopment()  用于判断当前运行环境是否是测试环境

2.app.UseDeveloperExceptionPage(); 如果是测试环境则启用开发者异常页面

开发者异常页面

当在Web处理管道放生了一个位置异常时,开发者异常页面会显示有用的调试信息。页面包含几个选项卡显示Stack、Query、Cookie、Header异常信息。

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>

二、错误处理扩展,使用委托

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);

三、错误处理扩展,使用自定义扩展类

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 分页的问题

原文地址:https://www.cnblogs.com/tianma3798/p/7126372.html