在 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 中返回自定义错误对象的主要内容,如果未能解决你的问题,请参考以下文章