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

Posted

技术标签:

【中文标题】在 Web API 中返回自定义错误对象【英文标题】:Return custom error objects in Web API 【发布时间】:2013-04-21 00:23:35 【问题描述】:

我正在使用 MVC 4 Web API 框架开发一个 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));

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

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

【问题讨论】:

【参考方案1】:

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

【讨论】:

谢谢。这类似于我正在做的 - 创建一个自定义 ExceptionFilterAttribute 该网站不再可用【参考方案2】:

这些答案比他们需要的要复杂得多。

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

【讨论】:

优秀的答案,+1 还包括适当的测试 确保你已经使用 System.Net.Http; 这是迄今为止最好的答案。更健壮且易于实施。在我的变体中,在匿名对象中,我添加了一些属性以及异常消息和其他有用的调试提示。谢谢! 很好,但是当使用 PostSharp aop 方法时,你不能只设置上下文响应,没有办法访问上下文,所以你必须过度设计东西:(跨度> 参见 (jakeydocs.readthedocs.io/en/latest/migration/webapi.html) 在 app_start 中使用 WebApiConfig。什么创建了 app_start 目录【参考方案3】:

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

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

 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,并且总是在不更新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));
    

当我发现这个计划中的漏洞时,我会多尝试一下并更新。

【讨论】:

为什么要隐藏 Message 属性?调用基本 ctor 并以这种方式传递消息不是更安全吗?

以上是关于在 Web API 中返回自定义错误对象的主要内容,如果未能解决你的问题,请参考以下文章

错误 json 的自定义错误响应。点网核心 Web API

Web API--自定义异常结果的处理

在 python C API 中返回新的自定义类对象的列表

如何在 Web API Post 方法中返回自定义消息

Web api dotnet core 中的 Api 版本控制错误的自定义错误响应

是否可以将自定义错误页面与 MVC 站点一起使用,但不能在 Web API 中使用?