重构 WebAPI 控制器中的错误处理

本文关键字:错误 处理 WebAPI 控制器 重构 | 更新日期: 2023-09-27 18:33:46

我在WebAPI中有很多控制器方法,类似于以下内容:

public IHttpActionResult Delete(int id)
{
    var command = new DeleteItemCommand() { Id = id };
    try
    {
        _deleteCommandHandler.Handle(command);
    }
    catch (CommandHandlerResourceNotFoundException)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    catch(CommandHandlerException)
    {
        throw new HttpResponseException(HttpStatusCode.InternalServerError);
    }
    // More catches etc...
    return Ok();
}

命令处理程序(在本例中为 _deleteCommandHandler)在执行的早期注入,命令可以在方法中或使用 WebAPI 的自动方法构建。

我想做的是将try/catch错误处理封装在一个私有方法中,并最终得到一个类似于以下内容的控制器:

public IHttpActionResult Delete(int id)
{
    var command = new DeleteItemCommand() { Id = id };
    return ExecuteCommand(x => _deleteCommandHandler.Handle(command));
}

不过,我不确定私有ExecuteCommand方法的签名应该是什么。

重构 WebAPI 控制器中的错误处理

我认为你可以用这样的方法Invoke你的动作:

public IHttpActionResult Delete(int id)
{
    return ExecuteCommand(() => {
        var command = new DeleteItemCommand() { Id = id };
        _deleteCommandHandler.Handle(command);
    });
}
private IHttpActionResult ExecuteCommand(Action action)
{
    try
    {
        action.Invoke();
        //or: action();
    }
    catch (CommandHandlerResourceNotFoundException)
    {
        return HttpResponseException(HttpStatusCode.NotFound);
    }
    catch (CommandHandlerException)
    {
        return HttpResponseException(HttpStatusCode.InternalServerError);
    }
    return Ok();
}

HttpResponseException的一个很好的参考。

我将创建一个自定义错误处理程序过滤器,并以集中式形式处理所有可能的错误。这样,您只需从操作方法中抛出任何异常,然后它们将被过滤器捕获,您可以在其中处理它们并相应地更改响应。

public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute 
{
    public override void OnException(HttpActionExecutedContext context)
    {
        if (context.Exception is NotImplementedException)
        {
            context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
        }
    }
}

示例取自本文,您可以在其中更详细地找到该概念。

这是一个类似于 shA.t 答案的解决方案,但异常映射在字典中,try/catch 逻辑在扩展方法中:

    public class TestController:ApiController
    {
        public IHttpActionResult Delete(int id)
        {
            return ExecuteCommand(() => {
                var command = new DeleteItemCommand() { Id = id };
                _deleteCommandHandler.Handle(command);
            });
    }
    private IHttpActionResult ExecuteCommand(Action action)
    {
        return action.SafeInvoke();
    }
}
public static class ActionExtensions
{
    private static readonly Dictionary<Type, HttpStatusCode> _exceptionToStatusCodeLookup = new Dictionary<Type, HttpStatusCode>
    {
        {typeof(CommandHandlerResourceNotFoundException), HttpStatusCode.NotFound },
        {typeof(CommandHandlerException), HttpStatusCode.InternalServerError },
    };
    public static IHttpActionResult SafeInvoke(this Action action)
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            var statusCode = _exceptionToStatusCodeLookup.ContainsKey(ex.GetType()) ? _exceptionToStatusCodeLookup[ex.GetType()] : HttpStatusCode.InternalServerError;
            return new HttpResponseException(statusCode);
        }
        return new OkResult();
    }
}