【问题标题】:Throw HttpResponseException or return Request.CreateErrorResponse?抛出 HttpResponseException 还是返回 Request.CreateErrorResponse?
【发布时间】:2012-09-13 05:29:29
【问题描述】:

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

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

问题

关于案例 #1 的问题

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

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

  1. 我应该抛出异常还是返回错误响应?如果答案是“视情况而定”,您能否提供有关何时使用一种与另一种的情况/示例。
  2. throwing 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,以下代码 sn-p 突出显示了在找不到客户时“可能发生”的几个选项...

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

【问题讨论】:

  • @Mike Wasson 作为链接文章的作者,您会采用哪种方法?

标签: c# exception-handling asp.net-web-api http-status-codes


【解决方案1】:

我喜欢Oppositional answer

无论如何,我需要一种方法来捕获继承的异常,但该解决方案并不能满足我的所有需求。

所以我最终改变了他处理 OnException 的方式,这是我的版本

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)) {
      //tento di vedere se ho registrato qualche eccezione che eredita dal tipo di eccezione sollevata (in ordine di registrazione)
      foreach (var item in this.Handlers.Keys) {
         if (type.IsSubclassOf(item)) {
            registration = this.Handlers[item];
            break;
         }
      }
   }

   //se ho trovato un tipo compatibile, uso la sua gestione
   if (registration != null) {
      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
      );
   }
}

核心是这个循环,我检查异常类型是否是注册类型的子类。

foreach (var item in this.Handlers.Keys) {
    if (type.IsSubclassOf(item)) {
        registration = this.Handlers[item];
        break;
    }
}

my2cents

【讨论】:

    【解决方案2】:

    不要抛出 HttpResponseException 或返回 HttpResponesMessage 错误 - except 如果意图是结束请求 准确的结果 em>

    HttpResponseException 的处理方式与其他异常的处理方式不同。它们没有被异常过滤器捕获。它们没有被异常处理程序捕获。它们是在终止当前代码的执行流程时插入 HttpResponseMessage 的一种狡猾的方法。

    除非代码是依赖这种特殊取消处理的基础设施代码,否则避免使用 HttpResponseException 类型!

    HttpResponseMessage 也不例外。它们不会终止当前代码的执行流程。它们可以被过滤为例外。它们可以被记录为异常。它们代表一个有效的结果——即使是 500 响应也是“有效的非异常响应”!


    让生活更简单:

    当出现异常/错误情况时,继续抛出一个普通的 .NET 异常 - 或具有所需“http 错误/响应”属性的自定义应用程序异常类型(不是源自 HttpResponseException)例如状态码 - 按照正常的异常处理

    使用异常过滤器/异常处理程序/异常记录器对这些异常情况做一些适当的事情:更改/添加状态代码?添加跟踪标识符?包括堆栈跟踪?记录?

    通过避免 HttpResponseException“异常情况”处理变得统一,并且可以作为暴露管道的一部分进行处理!例如,可以通过应用程序级异常轻松统一地将“NotFound”转换为 404,将“ArgumentException”转换为 400,将“NullReference”转换为 500 - 同时允许可扩展性以提供“基础”,例如错误日志记录。

    【讨论】:

    • 我明白为什么控制器中的ArgumentExceptions 在逻辑上会是 400,但是在堆栈中更深的 ArgumentExceptions 呢?将这些转换为 400 不一定是正确的,但是如果您有一个过滤器将所有 ArgumentExceptions 转换为 400,那么避免这种情况的唯一方法是在控制器中捕获异常并重新抛出其他东西,这似乎违背了在过滤器或类似物中统一异常处理的目的。
    • @cmeeren 在我处理它的代码中,大部分都捕获了异常并将其转换为每个 web 方法中的 HttpResponse[Exception/Message]。这两种情况是相同的,如果关注的是对内部异常做一些不同的事情,那就是对捕获的内部异常做“某事”:我建议结果是抛出一个适当的包装异常仍然在堆栈中进一步处理。
    • @cmeeren 更新后,我们的大多数 Web 入口点都会向使用错误抛出一个特殊的派生(非 HttpResponseException,具有和/或映射到适当的响应代码)。统一的处理程序可以做一些堆栈检查(很恶心,但它工作时要小心)以确定异常来自哪个级别 - 即。覆盖 99% 的没有更精细处理的案例 - 或者简单地以 500 响应内部错误。 HttpResponseException 的症结在于它绕过了有用的管道处理。
    【解决方案3】:

    何时使用HttpResponseException 而不是Response.CreateResponse(HttpStatusCode.NotFound) 或其他错误状态代码的另一种情况是,如果您在操作过滤器中有事务,并且您希望在向客户端返回错误响应时回滚事务。

    使用Response.CreateResponse 不会回滚事务,而抛出异常会。

    【讨论】:

      【解决方案4】:

      我想指出,根据我的经验,如果在 webapi 2 方法中抛出 HttpResponseException 而不是返回 HttpResponseMessage,如果立即调用 IIS Express,它将超时或返回 200但响应中有 html 错误。 最简单的测试方法是对抛出 HttpResponseException 的方法进行 $.ajax 调用,并在 ajax 中的 errorCallBack 中立即调用另一个方法,甚至是一个简单的 http 页面。您会注意到立即呼叫将失败。如果您在错误回调中添加断点或 settimeout() 以将第二次调用延迟一两秒,从而使服务器有时间恢复它可以正常工作。这是没有的,因为它几乎就像 throw HttpResponseException 导致服务器端侦听器线程退出并重新启动,导致一瞬间没有服务器接受连接或其他东西。

      更新:奇怪的 Ajax 连接超时的根本原因是,如果一个 ajax 调用足够快,并且使用了相同的 tcp 连接。我通过返回 HttpResonseMessage 或抛出返回给浏览器 ajax 调用的 HTTPResponseException 引发了 401 错误。但是随着该调用,MS 返回了一个未找到对象错误,因为在 Startup.Auth.vb app.UserCookieAuthentication 已启用,因此它试图返回拦截响应并添加重定向,但它错误的是对象而不是对象的实例。此错误是 html,但在事后附加到响应中,因此只有当 ajax 调用足够快并且使用相同的 tcp 连接时,它才会返回到浏览器,然后附加到下一个调用的前面。由于某种原因,Chrome 刚刚超时,由于 json 和 htm 的混合,提琴手 pucked,但 firefox rturn 了真正的错误。太奇怪了,但数据包嗅探器或火狐是追踪这个的唯一方法。

      另外需要注意的是,如果您使用 Web API 帮助来生成自动帮助并返回 HttpResponseMessage,那么您应该添加一个

      [System.Web.Http.Description.ResponseType(typeof(CustomReturnedType))] 
      

      归因于该方法,以便正确生成帮助。那么

      return Request.CreateResponse<CustomReturnedType>(objCustomeReturnedType) 
      

      或出错

      return Request.CreateErrorResponse( System.Net.HttpStatusCode.InternalServerError, new Exception("An Error Ocurred"));
      

      希望这可以帮助其他可能在抛出 HttpResponseException 后随机超时或服务器不可用的人。

      返回 HttpResponseException 还具有额外的好处,即当返回的错误是需要在单页应用程序中刷新 AuthToken 时,不会导致 Visual Studio 因未处理的异常而中断。

      更新:我正在撤回关于 IIS Express 超时的声明,这恰好是我的客户端 ajax 回调中的一个错误,因为 Ajax 1.8 返回 $.ajax() 并返回 $.ajax。 ().then() 都返回 Promise 但不是同一个链式 Promise then() 返回一个导致执行顺序错误的新 Promise。所以当 then() 承诺完成时,它是一个脚本超时。奇怪的问题,但不是 IIS express 问题,是键盘和椅子之间的问题。

      【讨论】:

        【解决方案5】:

        在错误情况下,我想以客户端请求的任何格式而不是快乐路径对象返回特定的错误详细信息类。

        我想让我的控制器方法返回特定于域的快乐路径对象,否则抛出异常。

        我遇到的问题是 HttpResponseException 构造函数不允许域对象。

        这是我最终想到的

        public ProviderCollection GetProviders(string providerName)
        {
           try
           {
              return _providerPresenter.GetProviders(providerName);
           }
           catch (BadInputValidationException badInputValidationException)
           {
             throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest,
                                                  badInputValidationException.Result));
           }
        }
        

        Result 是一个包含错误详细信息的类,而ProviderCollection 是我的快乐路径结果。

        【讨论】:

          【解决方案6】:

          如果您不返回 HttpResponseMessage 而是直接返回实体/模型类,我发现一种有用的方法是将以下实用程序函数添加到我的控制器中

          private void ThrowResponseException(HttpStatusCode statusCode, string message)
          {
              var errorResponse = Request.CreateErrorResponse(statusCode, message);
              throw new HttpResponseException(errorResponse);
          }
          

          只需使用适当的状态代码和消息调用它

          【讨论】:

          • 这是正确的答案,它带有“消息”格式作为正文中的键值对。这通常是我看到其他框架和语言的方式
          • 我对这种方法有一个小问题。我正在使用 angularJS 页面中的 {{}} 语法使用该消息。如果我留下回车,它们在消息中以 n\r\ 的形式出现。保存它们的正确方法是什么?
          • 我试过这种方法。我做了throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid Request Format!")),但在 Fiddler 中,它显示状态 500(不是 400)。知道为什么吗?
          • 区别在于错误的父函数。这是应用程序中任何异常的 ThrowResponseException。但应该是真正的函数抛出异常...
          【解决方案7】:

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

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

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

          过滤器注册示例:

          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找到。

          【讨论】:

          • 哇! :) 这对于较小的项目可能有点多,但仍然非常好......顺便说一句,为什么在 DefaultHandler 中使用 CreateResponse 而不是 CreateErrorResponse?
          • 我试图将错误详细信息(在正文中序列化)与原因短语分开;但是如果像在模型绑定的情况下那样更有意义的话,你当然可以使用 CreateErrorResponse。
          • 由于只需一行代码就可以注册过滤器,我认为它几乎适用于任何项目类型。我们在一个类库中拥有过滤器,该过滤器发布在我们的内部 NuGet 提要上,因此开发人员很容易使用。
          • 你用什么做警卫(本土或第三方)?
          • Hoemgrown,我在上面的示例中删除了它的使用。 Guard 类提供了一组静态方法来防止或验证是否满足断言。如果您想要实现,请参阅 codepaste.net/5oc1if (Guard) 和 codepaste.net/nsrsei (DelegateInfo)。
          【解决方案8】:

          据我所知,无论是抛出异常,还是返回 Request.CreateErrorResponse,结果都是一样的。如果您查看 System.Web.Http.dll 的源代码,您会看到很多。看看这个一般总结,以及我提出的一个非常相似的解决方案:Web Api, HttpError, and the behavior of exceptions

          【讨论】:

            【解决方案9】:

            案例#1

            1. 不一定,管道中还有其他地方可以修改响应(操作过滤器、消息处理程序)。
            2. 见上文——但如果操作返回域模型,则您无法在操作内部修改响应。

            案例 #2-4

            1. 抛出 HttpResponseException 的主要原因是:
              • 如果您要返回域模型但需要处理错误情况,
              • 通过将错误视为异常来简化控制器逻辑
            2. 这些应该是等价的; HttpResponseException 封装了一个 HttpResponseMessage,它作为 HTTP 响应返回。

              例如,案例 #2 可以重写为

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

              ...但是如果你的控制器逻辑更复杂,抛出异常可能会简化代码流程。

            3. HttpError 为您提供了一致的响应正文格式,并且可以序列化为 JSON/XML/等,但这不是必需的。例如,您可能不想在响应中包含实体主体,或者您可能想要其他格式。

            【讨论】:

            • 我采取的方法是从 api 控制器操作中抛出异常,并且我已经注册了一个异常过滤器来处理异常并在操作执行上下文上设置适当的响应。过滤器是“可插入的”,因此我可以在使用全局配置注册过滤器之前为特定类型的异常注册处理程序。这允许我进行集中式异常处理,而不是将其分散到控制器中。
            • @Oppositional 您愿意分享您的异常过滤器吗?也许作为 Gist 或在 CodePaste 等代码共享网站上?
            • @Mike Wasson 你会说“返回错误响应”是比“抛出异常”更常见的方法吗?我理解在功能上最终结果可能(是?)相同,但我想知道为什么不在 try/catch 中包含整个控制器逻辑并酌情返回错误响应?
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-11-09
            • 1970-01-01
            • 2011-04-27
            • 2010-12-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多