gpt4 book ai didi

c# - 抛出 HttpResponseException 或返回 Request.CreateErrorResponse?

转载 作者:IT王子 更新时间:2023-10-29 03:30:36 26 4
gpt4 key购买 nike

评论一篇文章后Exception Handling in ASP.NET Web API对于何时抛出异常与返回错误响应,我有点困惑。我还想知道当您的方法返回域特定模型而不是 HttpResponseMessage...

时是否可以修改响应

所以,在这里回顾一下我的问题,然后是一些带有 case #s 的代码:

问题

关于案例 #1 的问题

  1. 我是否应该始终使用 HttpResponseMessage 而不是具体的域模型,以便可以自定义消息?
  2. 如果返回具体领域模型,是否可以自定义消息?

关于案例 #2、3、4 的问题

  1. 我应该抛出异常还是返回错误响应?如果答案是“视情况而定”,您能否举例说明何时使用其中一种与另一种。
  2. 抛出 HttpResponseExceptionRequest.CreateErrorResponse 有什么区别?客户端的输出似乎是相同的...
  3. 我是否应该始终使用 HttpError 将响应消息“包装”在错误中(无论是抛出异常还是返回错误响应)?

案例样本

// CASE #1
public Customer Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var notFoundResponse = new HttpResponseMessage(HttpStatusCode.NotFound);
throw new HttpResponseException(notFoundResponse);
}
//var response = Request.CreateResponse(HttpStatusCode.OK, customer);
//response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return customer;
}

// CASE #2
public HttpResponseMessage Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var notFoundResponse = new HttpResponseMessage(HttpStatusCode.NotFound);
throw new HttpResponseException(notFoundResponse);
}
var response = Request.CreateResponse(HttpStatusCode.OK, customer);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return response;
}

// CASE #3
public HttpResponseMessage Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var message = String.Format("customer with id: {0} was not found", id);
var errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
throw new HttpResponseException(errorResponse);
}
var response = Request.CreateResponse(HttpStatusCode.OK, customer);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return response;
}

// CASE #4
public HttpResponseMessage Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var message = String.Format("customer with id: {0} was not found", id);
var httpError = new HttpError(message);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
}
var response = Request.CreateResponse(HttpStatusCode.OK, customer);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return response;
}

更新

为了帮助进一步演示案例 #2、3、4,以下代码片段突出显示了在找不到客户时“可能发生”的几个选项...

if (customer == null)
{
// which of these 4 options is the best strategy for Web API?

// option 1 (throw)
var notFoundMessage = new HttpResponseMessage(HttpStatusCode.NotFound);
throw new HttpResponseException(notFoundMessage);

// option 2 (throw w/ HttpError)
var message = String.Format("Customer with id: {0} was not found", id);
var httpError = new HttpError(message);
var errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
throw new HttpResponseException(errorResponse);

// option 3 (return)
var message = String.Format("Customer with id: {0} was not found", id);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
// option 4 (return w/ HttpError)
var message = String.Format("Customer with id: {0} was not found", id);
var httpError = new HttpError(message);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
}

最佳答案

我采用的方法是仅从 api Controller 操作中抛出异常,并注册一个异常过滤器来处理异常并在操作执行上下文中设置适当的响应。

过滤器公开了一个流畅的接口(interface),该接口(interface)提供了一种在使用全局配置注册过滤器之前为特定类型的异常注册处理程序的方法。

使用此过滤器可以实现集中式异常处理,而不是将其分散到 Controller 操作中。然而,在某些情况下,如果集中处理该特定异常没有意义,我会在 Controller 操作中捕获异常并返回特定响应。

过滤器注册示例:

GlobalConfiguration.Configuration.Filters.Add(
new UnhandledExceptionFilterAttribute()
.Register<KeyNotFoundException>(HttpStatusCode.NotFound)

.Register<SecurityException>(HttpStatusCode.Forbidden)

.Register<SqlException>(
(exception, request) =>
{
var sqlException = exception as SqlException;

if (sqlException.Number > 50000)
{
var response = request.CreateResponse(HttpStatusCode.BadRequest);
response.ReasonPhrase = sqlException.Message.Replace(Environment.NewLine, String.Empty);

return response;
}
else
{
return request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
)
);

UnhandledExceptionFilterAttribute 类:

using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http.Filters;

namespace Sample
{
/// <summary>
/// Represents the an attribute that provides a filter for unhandled exceptions.
/// </summary>
public class UnhandledExceptionFilterAttribute : ExceptionFilterAttribute
{
#region UnhandledExceptionFilterAttribute()
/// <summary>
/// Initializes a new instance of the <see cref="UnhandledExceptionFilterAttribute"/> class.
/// </summary>
public UnhandledExceptionFilterAttribute() : base()
{

}
#endregion

#region DefaultHandler
/// <summary>
/// Gets a delegate method that returns an <see cref="HttpResponseMessage"/>
/// that describes the supplied exception.
/// </summary>
/// <value>
/// A <see cref="Func{Exception, HttpRequestMessage, HttpResponseMessage}"/> delegate method that returns
/// an <see cref="HttpResponseMessage"/> that describes the supplied exception.
/// </value>
private static Func<Exception, HttpRequestMessage, HttpResponseMessage> DefaultHandler = (exception, request) =>
{
if(exception == null)
{
return null;
}

var response = request.CreateResponse<string>(
HttpStatusCode.InternalServerError, GetContentOf(exception)
);
response.ReasonPhrase = exception.Message.Replace(Environment.NewLine, String.Empty);

return response;
};
#endregion

#region GetContentOf
/// <summary>
/// Gets a delegate method that extracts information from the specified exception.
/// </summary>
/// <value>
/// A <see cref="Func{Exception, String}"/> delegate method that extracts information
/// from the specified exception.
/// </value>
private static Func<Exception, string> GetContentOf = (exception) =>
{
if (exception == null)
{
return String.Empty;
}

var result = new StringBuilder();

result.AppendLine(exception.Message);
result.AppendLine();

Exception innerException = exception.InnerException;
while (innerException != null)
{
result.AppendLine(innerException.Message);
result.AppendLine();
innerException = innerException.InnerException;
}

#if DEBUG
result.AppendLine(exception.StackTrace);
#endif

return result.ToString();
};
#endregion

#region Handlers
/// <summary>
/// Gets the exception handlers registered with this filter.
/// </summary>
/// <value>
/// A <see cref="ConcurrentDictionary{Type, Tuple}"/> collection that contains
/// the exception handlers registered with this filter.
/// </value>
protected ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>> Handlers
{
get
{
return _filterHandlers;
}
}
private readonly ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>> _filterHandlers = new ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>>();
#endregion

#region OnException(HttpActionExecutedContext actionExecutedContext)
/// <summary>
/// Raises the exception event.
/// </summary>
/// <param name="actionExecutedContext">The context for the action.</param>
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if(actionExecutedContext == null || actionExecutedContext.Exception == null)
{
return;
}

var type = actionExecutedContext.Exception.GetType();

Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> registration = null;

if (this.Handlers.TryGetValue(type, out registration))
{
var statusCode = registration.Item1;
var handler = registration.Item2;

var response = handler(
actionExecutedContext.Exception.GetBaseException(),
actionExecutedContext.Request
);

// Use registered status code if available
if (statusCode.HasValue)
{
response.StatusCode = statusCode.Value;
}

actionExecutedContext.Response = response;
}
else
{
// If no exception handler registered for the exception type, fallback to default handler
actionExecutedContext.Response = DefaultHandler(
actionExecutedContext.Exception.GetBaseException(), actionExecutedContext.Request
);
}
}
#endregion

#region Register<TException>(HttpStatusCode statusCode)
/// <summary>
/// Registers an exception handler that returns the specified status code for exceptions of type <typeparamref name="TException"/>.
/// </summary>
/// <typeparam name="TException">The type of exception to register a handler for.</typeparam>
/// <param name="statusCode">The HTTP status code to return for exceptions of type <typeparamref name="TException"/>.</param>
/// <returns>
/// This <see cref="UnhandledExceptionFilterAttribute"/> after the exception handler has been added.
/// </returns>
public UnhandledExceptionFilterAttribute Register<TException>(HttpStatusCode statusCode)
where TException : Exception
{

var type = typeof(TException);
var item = new Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>(
statusCode, DefaultHandler
);

if (!this.Handlers.TryAdd(type, item))
{
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> oldItem = null;

if (this.Handlers.TryRemove(type, out oldItem))
{
this.Handlers.TryAdd(type, item);
}
}

return this;
}
#endregion

#region Register<TException>(Func<Exception, HttpRequestMessage, HttpResponseMessage> handler)
/// <summary>
/// Registers the specified exception <paramref name="handler"/> for exceptions of type <typeparamref name="TException"/>.
/// </summary>
/// <typeparam name="TException">The type of exception to register the <paramref name="handler"/> for.</typeparam>
/// <param name="handler">The exception handler responsible for exceptions of type <typeparamref name="TException"/>.</param>
/// <returns>
/// This <see cref="UnhandledExceptionFilterAttribute"/> after the exception <paramref name="handler"/>
/// has been added.
/// </returns>
/// <exception cref="ArgumentNullException">The <paramref name="handler"/> is <see langword="null"/>.</exception>
public UnhandledExceptionFilterAttribute Register<TException>(Func<Exception, HttpRequestMessage, HttpResponseMessage> handler)
where TException : Exception
{
if(handler == null)
{
throw new ArgumentNullException("handler");
}

var type = typeof(TException);
var item = new Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>(
null, handler
);

if (!this.Handlers.TryAdd(type, item))
{
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> oldItem = null;

if (this.Handlers.TryRemove(type, out oldItem))
{
this.Handlers.TryAdd(type, item);
}
}

return this;
}
#endregion

#region Unregister<TException>()
/// <summary>
/// Unregisters the exception handler for exceptions of type <typeparamref name="TException"/>.
/// </summary>
/// <typeparam name="TException">The type of exception to unregister handlers for.</typeparam>
/// <returns>
/// This <see cref="UnhandledExceptionFilterAttribute"/> after the exception handler
/// for exceptions of type <typeparamref name="TException"/> has been removed.
/// </returns>
public UnhandledExceptionFilterAttribute Unregister<TException>()
where TException : Exception
{
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> item = null;

this.Handlers.TryRemove(typeof(TException), out item);

return this;
}
#endregion
}
}

还可以找到源代码here .

关于c# - 抛出 HttpResponseException 或返回 Request.CreateErrorResponse?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12519561/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com