如何在ASP.NET Core中以不同方式处理错误(或区分)API调用和MVC(视图)调用

时间:2016-10-20 08:18:56

标签: asp.net-web-api error-handling asp.net-core asp.net-core-mvc

在我基于普通MVC和WebApi的应用程序中,我有两种不同的错误处理路径。

如果在WebApi调用期间发生错误,我会拦截它(使用标准的web api选项)并返回带有相应HTTP状态代码的json消息,以便客户端应用程序可以处理它。

如果错误发生在MVC中,那么我会使用一些标准处理程序,可能会根据状态代码将用户重定向到某个默认错误屏幕。

现在在ASP.NET Core中,两者都加入了相同的框架,所以如果我只是拦截并返回JSON,那么我冒险向用户显示json,因为它可以是一个返回视图的动作。另一方面,如果我使用Top,那么我的API调用将从js内无法使用的错误页面获取HTML。

为这两种情况提供单独的错误处理的好方法是什么?或者也许有更好的方法来完全处理它?<​​/ p>

P.S。我宁愿重用开箱即用的MVC异常处理程序,只添加web api部分。

2 个答案:

答案 0 :(得分:11)

实现目标有很多方法:

1-使用两个不同的异常过滤器(我会采用这种方法,因为你的问题是关于mvc pipline)

实现:

// For api
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        // send error as json
    }
}


[ApiExceptionFilter]
public class ApiController : Controller{...}

// For mvc
public class MvcExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(ExceptionContext context)
    {
        // send view result
    }
}


[MvcExceptionFilter]
public class HomeController : Controller{...}

如果您想全局添加过滤器,请参阅Register filter for an area

2-使用UseWhenUseExceptionHandler

         app.UseWhen(x => x.Request.Path.Value.StartsWith("/api"), builder =>
         {
             builder.UseExceptionHandler(new ExceptionHandlerOptions()
             {
                 ExceptionHandler = async (ctx) =>
                 {
                     var feature = ctx.Features.Get<IExceptionHandlerFeature>();
                     var error = feature?.Error;
                     // send json
                 }
             });
         });
        app.UseWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
        {
            builder.UseExceptionHandler("/Error");
        });`

3-有条件地使用UseExceptionHandler

        app.UseExceptionHandler(new ExceptionHandlerOptions()
        {
            ExceptionHandler = async (ctx) =>
            {
                if (ctx.Request.Path.Value.StartsWith("/api"))
                {
                    var feature = ctx.Features.Get<IExceptionHandlerFeature>();
                    var error = feature?.Error;
                    // send json
                }
                else
                {
                    // redirect error page
                }
            }
        });

答案 1 :(得分:2)

我建议您编写自定义中间件来处理异常。 这里有一个例子:

public class ErrorMiddleware
{
    private readonly RequestDelegate next;

    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

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

    private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        if (exception == null) return;

        var code = HttpStatusCode.InternalServerError;

        if (exception is MyNotFoundException) code = HttpStatusCode.NotFound;
        //here you can check what kind of exception it is

        //wite is proper for Web API, but here you can do what you want
        await WriteExceptionAsync(context, exception, code).ConfigureAwait(false);
    }

    private static async Task WriteExceptionAsync(HttpContext context, Exception exception, HttpStatusCode code)
    {
        var response = context.Response;
        response.ContentType = "application/json";
        response.StatusCode = (int)code;
        await response.WriteAsync(JsonConvert.SerializeObject(new 
        {
            error = new
            {
                message = exception.Message,
                exception = exception.GetType().Name
            }
        })).ConfigureAwait(false);
    }
}

这就是注册中间件的方法:

app.UseMiddleware(typeof(ErrorMiddleware));