首页 文章

在Web API中返回自定义错误对象

提问于
浏览
31

我有一个Web API,我正在使用MVC 4 Web API框架 . 如果有异常,我现在正在抛出一个新的HttpResponseException . 即:

if (!Int32.TryParse(id, out userId))
    throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid id"));

这会将一个对象返回给客户端,这只是 {"message":"Invalid id"}

我希望通过返回更详细的对象来进一步控制对异常的响应 . 就像是

{
 "status":-1,
 "substatus":3,
 "message":"Could not find user"
 }

我该怎么做呢?是序列化我的错误对象并在响应消息中设置它的最佳方法吗?

我已经调查了 ModelStateDictionary 并且已经提出了"hack"的这一点,但它仍然不是一个干净的输出:

var msd = new ModelStateDictionary();
msd.AddModelError("status", "-1");
msd.AddModelError("substatus", "3");
msd.AddModelError("message", "invalid stuff");
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, msd));

edit
看起来像一个自定义 HttpError 是我需要的 . 这似乎可以解决问题,现在可以从我的业务层扩展它...

var error = new HttpError("invalid stuff") {{"status", -1}, {"substatus", 3}};
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, error));

3 回答

  • 40

    这些答案比他们需要的更复杂 .

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Filters.Add(new HandleApiExceptionAttribute());
            // ...
        }
    }
    
    public class HandleApiExceptionAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            var request = context.ActionContext.Request;
    
            var response = new
            {
                 //Properties go here...
            };
    
            context.Response = request.CreateResponse(HttpStatusCode.BadRequest, response);
        }
    }
    

    这就是你所需要的 . 单元测试也很好很容易:

    [Test]
    public async void OnException_ShouldBuildProperErrorResponse()
    {
        var expected = new 
        {
             //Properties go here...
        };
    
        //Setup
        var target = new HandleApiExceptionAttribute()
    
        var contextMock = BuildContextMock();
    
        //Act
        target.OnException(contextMock);
    
        dynamic actual = await contextMock.Response.Content.ReadAsAsync<ExpandoObject>();
    
        Assert.AreEqual(expected.Aproperty, actual.Aproperty);
    }
    
    private HttpActionExecutedContext BuildContextMock()
    {
        var requestMock = new HttpRequestMessage();
        requestMock.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
    
        return new HttpActionExecutedContext()
        {
            ActionContext = new HttpActionContext
            {
                ControllerContext = new HttpControllerContext
                {
                    Request = requestMock
                }
    
            },
            Exception = new Exception()
        };
    }
    
  • 2

    我认为这样可以解决问题:

    为业务层创建自定义异常类:

    public class MyException: Exception
     {
        public ResponseStatus Status { get; private set; }
        public ResponseSubStatus SubStatus { get; private set; }
        public new string Message { get; private set; }
    
        public MyException()
        {}
    
        public MyException(ResponseStatus status, ResponseSubStatus subStatus, string message)
        {
            Status = status;
            SubStatus = subStatus;
            Message = message;
        }
     }
    

    创建一个静态方法以从 MyException 的实例生成 HttpError . 我在这里使用反射,所以我可以添加属性 MyException 并且总是让它们返回w / o更新 Create

    public static HttpError Create<T>(MyException exception) where T:Exception
        {
            var properties = exception.GetType().GetProperties(BindingFlags.Instance 
                                                             | BindingFlags.Public 
                                                             | BindingFlags.DeclaredOnly);
            var error = new HttpError();
            foreach (var propertyInfo in properties)
            {
                error.Add(propertyInfo.Name, propertyInfo.GetValue(exception, null));
            }
            return error;
        }
    

    我目前有一个常规异常处理程序的自定义属性 . 所有类型为 MyException 的异常都将在此处理:

    public class ExceptionHandlingAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            var statusCode = HttpStatusCode.InternalServerError;
    
            if (context.Exception is MyException)
            {
                statusCode = HttpStatusCode.BadRequest;
                throw new HttpResponseException(context.Request.CreateErrorResponse(statusCode, HttpErrorHelper.Create(context.Exception)));
            }
    
            if (context.Exception is AuthenticationException)
                statusCode = HttpStatusCode.Forbidden;
    
            throw new HttpResponseException(context.Request.CreateErrorResponse(statusCode, context.Exception.Message));
        }
    }
    

    当我在这个计划中找到漏洞时,我会更多地玩这个并更新 .

  • 9

    看看下面的文章 . 它将帮助您控制Web api异常和错误消息:Web Api, HttpError, and the Behavior of Exceptions

相关问题