在ASP.NET Web API中返回错误的最佳做法
我担心我们将错误返回给客户端的方式。
当出现错误时,是否通过抛出HttpResponseException立即返回错误:
public void Post(Customer customer)
{
if (string.IsNullOrEmpty(customer.Name))
{
throw new HttpResponseException("Customer Name cannot be empty", HttpStatusCode.BadRequest)
}
if (customer.Accounts.Count == 0)
{
throw new HttpResponseException("Customer does not have any account", HttpStatusCode.BadRequest)
}
}
或者我们累积所有错误,然后发回客户端:
public void Post(Customer customer)
{
List<string> errors = new List<string>();
if (string.IsNullOrEmpty(customer.Name))
{
errors.Add("Customer Name cannot be empty");
}
if (customer.Accounts.Count == 0)
{
errors.Add("Customer does not have any account");
}
var responseMessage = new HttpResponseMessage<List<string>>(errors, HttpStatusCode.BadRequest);
throw new HttpResponseException(responseMessage);
}
这只是一个示例代码,无论是验证错误还是服务器错误都无关紧要,我只想知道每种方法的最佳做法以及优缺点。
对我来说,我通常会发回一个HttpResponseException
并相应地根据抛出的异常设置状态码,如果异常是致命的,将决定我是否立即发回HttpResponseException
。
在一天结束时它的API发送回应而不是视图,所以我认为可以向消费者发送带有异常和状态代码的消息。 我目前不需要累积错误并将它们发回,因为大多数例外通常是由于不正确的参数或调用等原因造成的。
我的应用程序的一个例子是,有时客户端会询问数据,但没有任何数据可用,所以我抛出一个自定义的noDataAvailableException并让它冒泡到web api应用程序,然后在我的自定义过滤器中捕获它发回相关消息以及正确的状态码。
我并不十分确定最佳做法是什么,但目前这对我来说是很有用的,所以即使我在做。
更新 :
由于我回答了这个问题,所以在这个主题上写了一些博客文章:
http://weblogs.asp.net/fredriknormen/archive/2012/06/11/asp-net-web-api-exception-handling.aspx
(这个在夜间版本中有一些新功能)http://blogs.msdn.com/b/youssefm/archive/2012/06/28/error-handling-in-asp-net-webapi.aspx
更新2
更新我们的错误处理流程,我们有两种情况:
对于像找不到的一般错误,或传递给动作的无效参数,我们会返回HttpResponseException以立即停止处理。 此外,对于我们的操作中的模型错误,我们将把模型状态字典交给Request.CreateErrorResponse
扩展,并将其包装在HttpResponseException中。 添加模型状态字典会生成响应正文中发送的模型错误列表。
对于发生在更高层的错误,服务器错误,我们让异常冒泡到Web API应用程序,在这里我们有一个全局异常过滤器,它查看异常,用elmah记录它并尝试理解它,以设置正确的http状态码和一个相关友好的错误消息作为HttpResponseException中的主体。 对于我们并不期望客户端会收到默认500内部服务器错误的例外情况,但出于安全原因而使用通用消息。
更新3
最近,在拿起Web API 2之后,为了发回一般错误,我们现在使用IHttpActionResult接口,特别是System.Web.Http.Results命名空间中的内置类,比如NotFound,BadRequest,如果它们不合适扩展它们,例如一个没有找到响应消息的结果:
public class NotFoundWithMessageResult : IHttpActionResult
{
private string message;
public NotFoundWithMessageResult(string message)
{
this.message = message;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.NotFound);
response.Content = new StringContent(message);
return Task.FromResult(response);
}
}
ASP.NET Web API 2真的简化了它。 例如,下面的代码:
public HttpResponseMessage GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
var message = string.Format("Product with id = {0} not found", id);
HttpError err = new HttpError(message);
return Request.CreateResponse(HttpStatusCode.NotFound, err);
}
else
{
return Request.CreateResponse(HttpStatusCode.OK, item);
}
}
找不到该项目时,将以下内容返回给浏览器:
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Date: Thu, 09 Aug 2012 23:27:18 GMT
Content-Length: 51
{
"Message": "Product with id = 12 not found"
}
建议:除非发生灾难性错误(例如WCF Fault Exception),否则不要抛出HTTP Error 500。 选择一个表示数据状态的适当的HTTP状态码。 (请参阅下面的apigee链接。)
链接:
看起来你在验证方面比在错误/异常方面遇到更多麻烦,所以我会稍微说一下。
验证
控制器操作通常应采用输入模型,直接在模型上声明验证。
public class Customer
{
[Require]
public string Name { get; set; }
}
然后你可以使用一个ActionFilter
自动发送Valiation消息回客户端。
public class ValidationActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var modelState = actionContext.ModelState;
if (!modelState.IsValid) {
actionContext.Response = actionContext.Request
.CreateErrorResponse(HttpStatusCode.BadRequest, modelState);
}
}
}
有关此检查的更多信息,请访问http://ben.onfabrik.com/posts/automatic-modelstate-validation-in-aspnet-mvc
错误处理
最好将消息返回给代表发生异常的客户端(使用相关的状态码)。
如果你想指定一个消息Request.CreateErrorResponse(HttpStatusCode, message)
你必须使用Request.CreateErrorResponse(HttpStatusCode, message)
。 但是,这将代码绑定到Request
对象,您不应该这样做。
我通常会创建自己的“安全”异常类型,我希望客户端知道如何处理和包装所有其他类型的通用500错误。
使用动作过滤器处理异常将如下所示:
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var exception = context.Exception as ApiException;
if (exception != null) {
context.Response = context.Request.CreateErrorResponse(exception.StatusCode, exception.Message);
}
}
}
然后你可以在全球注册。
GlobalConfiguration.Configuration.Filters.Add(new ApiExceptionFilterAttribute());
这是我的自定义异常类型。
using System;
using System.Net;
namespace WebApi
{
public class ApiException : Exception
{
private readonly HttpStatusCode statusCode;
public ApiException (HttpStatusCode statusCode, string message, Exception ex)
: base(message, ex)
{
this.statusCode = statusCode;
}
public ApiException (HttpStatusCode statusCode, string message)
: base(message)
{
this.statusCode = statusCode;
}
public ApiException (HttpStatusCode statusCode)
{
this.statusCode = statusCode;
}
public HttpStatusCode StatusCode
{
get { return this.statusCode; }
}
}
}
我的API可能抛出的示例异常。
public class NotAuthenticatedException : ApiException
{
public NotAuthenticatedException()
: base(HttpStatusCode.Forbidden)
{
}
}
链接地址: http://www.djcxy.com/p/45655.html