C# 抛出 HttpResponseException 还是返回 Request.CreateErrorResponse?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12519561/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 23:32:07  来源:igfitidea点击:

Throw HttpResponseException or return Request.CreateErrorResponse?

c#exception-handlingasp.net-web-apihttp-status-codes

提问by zam6ak

After reviewing an article Exception Handling in ASP.NET Web APII am a bit confused as to when to throw an exception vs return an error response. I am also left wondering whether it is possible to modify the response when your method returns a domain specific model instead of HttpResponseMessage...

在查看了ASP.NET Web API 中的异常处理一文后我对何时抛出异常与返回错误响应感到有些困惑。我还想知道当您的方法返回特定于域的模型而不是HttpResponseMessage...时是否可以修改响应

So, to recap here are my questions followed by some code with case #s:

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

Questions

问题

Questions regarding Case #1

关于案例#1 的问题

  1. Should I always use HttpResponseMessageinstead of a concrete domain model, so that the message can be customized?
  2. Can the message be customized if you are returning concrete domain model?
  1. 我应该总是使用HttpResponseMessage而不是具体的域模型,以便可以自定义消息吗?
  2. 如果您要返回具体的域模型,可以自定义消息吗?

Questions regarding Case #2,3,4

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

  1. Should I be throwing an exception or returning error response? If the answer is "it depends", can you give situations/examples on when to use one vs the other.
  2. What is the difference between throwing HttpResponseExceptionvs Request.CreateErrorResponse? The output to client seems identical...
  3. Should I always use HttpErrorto "wrap" response messages in errors (whether the exception is thrown or error response returned)?
  1. 我应该抛出异常还是返回错误响应?如果答案是“视情况而定”,您能否提供有关何时使用一种与另一种的情况/示例。
  2. throwHttpResponseException和throw和有Request.CreateErrorResponse什么不一样?客户端的输出似乎相同......
  3. 我应该总是使用HttpError在错误中“包装”响应消息(无论是抛出异常还是返回错误响应)?

Case Samples

案例样本

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

Update

更新

To help further demonstrate cases #2,3,4 the following code snippet highlights several options that "can happen" when a customer is not found...

为了帮助进一步演示案例 #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);
}

采纳答案by Oppositional

The approach I have taken is to just throw exceptions from the api controller actions and have an exception filter registered that processes the exception and sets an appropriate response on the action execution context.

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

The filter exposes a fluent interface that provides a means of registering handlers for specific types of exceptions prior to registering the filter with global configuration.

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

The use of this filter enables centralized exception handling instead of spreading it across the controller actions. There are however cases where I will catch exceptions within the controller action and return a specific response if it does not make sense to centralize the handling of that particular exception.

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

Example registration of filter:

过滤器注册示例:

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 class:

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

Source code can also be found here.

源代码也可以在这里找到。

回答by Mike Wasson

Case #1

情况1

  1. Not necessarily, there are other places in the pipeline to modify the response (action filters, message handlers).
  2. See above -- but if the action returns a domain model, then you can't modify the response insidethe action.
  1. 不一定,管道中还有其他地方可以修改响应(动作过滤器、消息处理程序)。
  2. 见上文——但如果操作返回域模型,则您无法修改操作的响应。

Cases #2-4

案例#2-4

  1. The main reasons to throw HttpResponseException are:
    • if you are returning a domain model but need to handle error cases,
    • to simplify your controller logic by treating errors as exceptions
  2. These should be equivalent; HttpResponseException encapsulates an HttpResponseMessage, which is what gets returned back as the HTTP response.

    e.g., case #2 could be rewritten as

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

    ... but if your controller logic is more complicated, throwing an exception might simplify the code flow.

  3. HttpError gives you a consistent format for the response body and can be serialized to JSON/XML/etc, but it's not required. e.g., you may not want to include an entity-body in the response, or you might want some other format.

  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/等,但这不是必需的。例如,您可能不想在响应中包含实体主体,或者您可能需要其他格式。

回答by Rob Gray

Another case for when to use HttpResponseExceptioninstead of Response.CreateResponse(HttpStatusCode.NotFound), or other error status code, is if you have transactions in action filters and you want the transactions to be rolled back when returning an error response to the client.

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

Using Response.CreateResponsewill not roll the transaction back, whereas throwing an exception will.

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

回答by Andy Cohen

As far as I can tell, whether you throw an exception, or you return Request.CreateErrorResponse, the result is the same. If you look at the source code for System.Web.Http.dll, you will see as much. Take a look at this general summary, and a very similar solution that I have made: Web Api, HttpError, and the behavior of exceptions

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

回答by Fabio Angela

I like Oppositional answer

我喜欢反对的答案

Anyway, I needed a way to catch the inherited Exception and that solution doesn't satisfy all my needs.

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

So I ended up changing how he handles OnException and this is my version

所以我最终改变了他处理 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
      );
   }
}

The core is this loop where I check if the exception type is a subclass of a registered type.

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

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

my2cents

我的2cents

回答by Joe King

If you are not returning HttpResponseMessageand instead are returning entity/model classes directly, an approach which I have found useful is to add the following utility function to my controller

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

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

and simply call it with the appropriate status code and message

并简单地使用适当的状态代码和消息调用它

回答by NickBeaugié

In error situations, I wanted to return a specific error details class, in whatever format the client requested instead of the happy path object.

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

I want to have my controller methods return the domain specific happy path object and to throw an exception otherwise.

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

The problem I had was that the HttpResponseException constructors do not allow domain objects.

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

This is what I eventually came up with

这就是我最终想出的

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

Resultis a class that contains error details, while ProviderCollectionis my happy path result.

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

回答by Andrew DeVries

I want to point out that it has been my experience that if throwing an HttpResponseException instead of returning an HttpResponseMessage in a webapi 2 method, that if a call is made immediately to IIS Express it will timeout or return a 200 but with a html error in the response. Easiest way to test this is to make $.ajax call to a method that throws a HttpResponseException and in the errorCallBack in ajax make an immediate call to another method or even a simple http page. You will notice the imediate call will fail. If you add a break point or a settimeout() in the error call back to delay the second call a second or two giving the server time to recover it works correctly. This makes no since but its almost like the throw HttpResponseException causes the server side listener thread to exit and restart causing a split second of no server accepting connections or something.

我想指出,根据我的经验,如果在 webapi 2 方法中抛出 HttpResponseException 而不是返回 HttpResponseMessage,如果立即调用 IIS Express,它将超时或返回 200,但在响应。测试这个最简单的方法是对一个抛出 HttpResponseException 的方法进行 $.ajax 调用,并在 ajax 的 errorCallBack 中立即调用另一个方法,甚至是一个简单的 http 页面。您会注意到即时调用将失败。如果在错误回调中添加断点或 settimeout() 以将第二次调用延迟一两秒钟,则服务器有时间恢复它正常工作。

Update: The root cause of the wierd Ajax connection Timeout is if an ajax call is made quick enough the same tcp connection is used. I was raising a 401 error ether by returning a HttpResonseMessage or throwing a HTTPResponseException which was returned to the browser ajax call. But along with that call MS was returning a Object Not Found Error because in Startup.Auth.vb app.UserCookieAuthentication was enabled so it was trying to return intercept the response and add a redirect but it errored with Object not Instance of an Object. This Error was html but was appended to the response after the fact so only if the ajax call was made quick enough and the same tcp connection used did it get returned to the browser and then it got appended to the front of the next call. For some reason Chrome just timedout, fiddler pucked becasue of the mix of json and htm but firefox rturned the real error. So wierd but packet sniffer or firefox was the only way to track this one down.

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

Also it should be noted that if you are using Web API help to generate automatic help and you return HttpResponseMessage then you should add a

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

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

attribute to the method so the help generates correctly. Then

归因于方法,因此帮助正确生成。然后

return Request.CreateResponse<CustomReturnedType>(objCustomeReturnedType) 

or on error

或出错

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

Hope this helps someone else who may be getting random timeout or server not available immediately after throwing a HttpResponseException.

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

Also returning an HttpResponseException has the added benefit of not causing Visual Studio to break on an Un-handled exception usefull when the error being returned is the AuthToken needs to be refreshed in a single page app.

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

Update: I am retracting my statement about IIS Express timing out, this happened to be a mistake in my client side ajax call back it turns out since Ajax 1.8 returning $.ajax() and returning $.ajax.().then() both return promise but not the same chained promise then() returns a new promise which caused the order of execution to be wrong. So when the then() promise completed it was a script timeout. Weird gotcha but not an IIS express issue a problem between the Keyboard and chair.

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

回答by user2864740

Do not throw an HttpResponseException or return an HttpResponesMessage for errors - exceptif the intent is to end the requestwith that exact result.

不要抛出HttpResponseException或返回HttpResponesMessage错误-除非,如果意图是结束请求那个确切的结果

HttpResponseException's are not handled the same as other exceptions. They are not caught in Exception Filters. They are not caught in the Exception Handler. They are a sly way to slip in an HttpResponseMessage while terminating the current code's execution flow.

HttpResponseException 的处理方式与其他异常不同。它们不会被 Exception Filters 捕获。它们不会被 Exception Handler 捕获。它们是在终止当前代码的执行流程时插入 HttpResponseMessage 的一种狡猾方式。

Unless the code is infrastructure code relying on this special un-handling, avoidusing the HttpResponseException type!

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

HttpResponseMessage's are not exceptions. They do not terminate the current code's execution flow. They can notbe filtered as exceptions. They can notbe logged as exceptions. They represent a valid result - even a 500 response is "a valid non-exception response"!

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



Make life simpler:

让生活更简单:

When there is an exceptional/error case, go ahead and throw a normal .NET exception - or a customized application exception type (notderiving from HttpResponseException) with desired 'http error/response' properties such as a status code - as per normal exception handling.

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

Use Exception Filters / Exception Handlers / Exception Loggers to do something appropriate with these exceptional cases: change/add status codes? add tracking identifiers? include stack traces? log?

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

By avoiding HttpResponseException the 'exceptional case' handling is made uniformand can be handled as part of the exposed pipeline! For example one can turn a 'NotFound' into a 404 and an 'ArgumentException' into a 400 and a 'NullReference' into a 500 easily and uniformly with application-level exceptions - while allowing extensibility to provide "basics" such as error logging.

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