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

ear*_*ing 31 error-handling exception-handling asp.net-mvc-4 asp.net-web-api

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

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

这会将对象简单地返回给客户端 {"message":"Invalid id"}

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

{
 "status":-1,
 "substatus":3,
 "message":"Could not find user"
 }
Run Code Online (Sandbox Code Playgroud)

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

我也看了ModelStateDictionary一下,并提出了这个"黑客",但它仍然不是一个干净的输出:

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));
Run Code Online (Sandbox Code Playgroud)

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

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

Fre*_*edM 41

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

[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()
    };
}
Run Code Online (Sandbox Code Playgroud)


ear*_*ing 9

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

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

 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;
    }
 }
Run Code Online (Sandbox Code Playgroud)

创建一个静态方法来HttpError从一个实例生成一个MyException.我在这里使用反射,所以我可以添加属性,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;
    }
Run Code Online (Sandbox Code Playgroud)

我目前有一个常规异常处理程序的自定义属性.所有类型的例外都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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 你为什么要隐藏Message属性?调用基本ctor并以这种方式传递消息会不会更安全? (3认同)