我对我们向客户返回错误的方式有顾虑。
当我们得到一个错误时,我们是否通过抛出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);
}
这只是一个示例代码,无论是验证错误还是服务器错误都不重要,我只是想知道最佳实践,每种方法的优点和缺点。
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链接。)
链接:
ASP中的异常处理。NET Web API (asp.net)
而且
RESTful API设计:错误怎么办?(apigee.com)
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链接。)
链接:
ASP中的异常处理。NET Web API (asp.net)
而且
RESTful API设计:错误怎么办?(apigee.com)
其中一些答案似乎是过去的遗迹。我发现下面的解决方案既简单又有效。这是在. net 6中用于从ControllerBase派生的Web API。
而不是抛出异常,你可以直接返回各种HTTP响应代码作为对象,以及一个准确的错误消息:
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class MyWebApiController : ControllerBase
{
[HttpPost]
public IActionResult Process(Customer customer)
{
if (string.IsNullOrEmpty(customer.Name))
return BadRequest("Customer Name cannot be empty");
if (!Customers.Find(customer))
return NotFound("Customer does not have any account");
// After validating inputs, core logic goes here...
return Ok(customer.ID); // or simply "return Ok()" if not returning data
}
}
请在这里查看可用的错误代码列表。
至于什么时候返回错误(OP的问题),这取决于需求。在错误发生时返回错误意味着您可以避免额外处理的开销,但随后客户机必须重复调用以获取所有错误。还要考虑服务器视点,因为当发生错误时,它可能导致不希望的程序行为继续服务器端处理。
对于我来说,我通常发送回一个HttpResponseException,并根据抛出的异常设置相应的状态代码,如果异常是致命的,将决定我是否立即发送回HttpResponseException。
在一天结束的时候,它是一个返回响应而不是视图的API,所以我认为向使用者发送带有异常和状态代码的消息是很好的。我目前还不需要积累错误并将它们发送回来,因为大多数异常通常是由于不正确的参数或调用等。
在我的应用程序中的一个例子是,有时客户端会要求数据,但没有任何数据可用,所以我抛出一个自定义NoDataAvailableException,让它气泡到Web API应用程序,然后在我的自定义过滤器捕捉它发送回一个相关的消息以及正确的状态代码。
我不是100%确定这方面的最佳实践是什么,但这对我来说目前是有效的,所以这就是我正在做的。
更新:
自从我回答了这个问题,就有一些关于这个话题的博客文章:
https://weblogs.asp.net/fredriknormen/asp-net-web-api-exception-handling
(这个版本在夜间版本中有一些新特性)
https://learn.microsoft.com/archive/blogs/youssefm/error-handling-in-asp-net-webapi
更新2
更新我们的错误处理过程,我们有两种情况:
For general errors like not found, or invalid parameters being passed to an action we return a HttpResponseException to stop processing immediately. Additionally for model errors in our actions we will hand the model state dictionary to the Request.CreateErrorResponse extension and wrap it in a HttpResponseException. Adding the model state dictionary results in a list of the model errors sent in the response body.
For errors that occur in higher layers, server errors, we let the exception bubble to the Web API app, here we have a global exception filter which looks at the exception, logs it with ELMAH and tries to make sense of it setting the correct HTTP status code and a relevant friendly error message as the body again in a HttpResponseException. For exceptions that we aren't expecting the client will receive the default 500 internal server error, but a generic message due to security reasons.
更新3
最近,在选择Web API 2后,为了发送回一般错误,我们现在使用IHttpActionResult接口,特别是在System.Web.Http.Results命名空间中内置的类,如NotFound, BadRequest,当他们适合时,如果他们不适合,我们扩展他们,例如一个NotFound结果与响应消息:
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);
}
}