简体   繁体   English

抛出HttpResponseException还是返回Request.CreateErrorResponse?

[英]Throw HttpResponseException or return Request.CreateErrorResponse?

After reviewing an article Exception Handling in ASP.NET Web API I am a bit confused as to when to throw an exception vs return an error response. 在查看ASP.NET Web API中的文章异常处理之后,我对于何时抛出异常vs返回错误响应感到困惑。 I am also left wondering whether it is possible to modify the response when your method returns a domain specific model instead of HttpResponseMessage ... 我还想知道当你的方法返回特定于域的模型而不是HttpResponseMessage时是否可以修改响应...

So, to recap here are my questions followed by some code with case #s: 所以,在这里回顾一下我的问题,然后是一些带有#s的代码:

Questions 问题

Questions regarding Case #1 关于案例#1的问题

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

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 HttpResponseException vs Request.CreateErrorResponse ? 抛出HttpResponseExceptionRequest.CreateErrorResponse什么区别? The output to client seems identical... 输出到客户端似乎相同......
  3. Should I always use HttpError to "wrap" response messages in errors (whether the exception is thrown or error response returned)? 我是否应该总是使用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);
}

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 . 源代码也可以在这里找到。

If you are not returning HttpResponseMessage and 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 并简单地使用适当的状态代码和消息调用它

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 inside the action. 请参阅上文 - 但如果操作返回域模型,则无法修改操作的响应。

Cases #2-4 案例#2-4

  1. The main reasons to throw HttpResponseException are: 抛出HttpResponseException的主要原因是:
    • 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. HttpResponseException封装了一个HttpResponseMessage,它是作为HTTP响应返回的内容。

    eg, case #2 could be rewritten as 例如,情况#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; } 

    ... 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. HttpError为响应主体提供了一致的格式,可以序列化为JSON / XML / etc,但不是必需的。 eg, you may not want to include an entity-body in the response, or you might want some other format. 例如,您可能不希望在响应中包含实体主体,或者您可能需要其他格式。

Do not throw an HttpResponseException or return an HttpResponesMessage for errors - except if the intent is to end the request with that exact result . 不要抛出HttpResponseException或返回HttpResponesMessage以查找错误 - 除非意图是使用该确切结果 结束请求

HttpResponseException's are not handled the same as other exceptions . HttpResponseException的处理方式与其他异常不同 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. 在终止当前代码的执行流程时,它们是在HttpResponseMessage中滑动的狡猾方式。

Unless the code is infrastructure code relying on this special un-handling, avoid using the HttpResponseException type! 除非代码是依赖于此特殊取消处理的基础结构代码,否则请避免使用HttpResponseException类型!

HttpResponseMessage's are not exceptions. HttpResponseMessage也不例外。 They do not terminate the current code's execution flow. 它们不会终止当前代码的执行流程。 They can not be filtered as exceptions. 它们不能作为例外进行过滤。 They can not be logged as exceptions. 它们不能作为例外记录。 They represent a valid result - even a 500 response is "a valid non-exception response"! 它们代表了有效的结果 - 即使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 ( not deriving from HttpResponseException) with desired 'http error/response' properties such as a status code - as per normal exception handling . 当存在异常/错误情况时,继续并抛出正常的.NET异常 - 或者使用所需的“http错误/响应”属性(例如状态代码)的自定义应用程序异常类型( 不是从HttpResponseException派生) - 按照正常异常处理

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 uniform and can be handled as part of the exposed pipeline! 通过避免HttpResponseException ,“异常情况”处理变得统一,并且可以作为暴露管道的一部分进行处理! 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. 例如,可以将“NotFound”转换为404,将“ArgumentException”转换为400,将“NullReference”转换为500容易且统一的应用程序级异常 - 同时允许可扩展性提供“基础知识”,例如错误记录。

Another case for when to use HttpResponseException instead 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.CreateResponse will not roll the transaction back, whereas throwing an exception will. 使用Response.CreateResponse不会回滚事务,而抛出异常会。

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. 我想指出,根据我的经验,如果抛出HttpResponseException而不是在webapi 2方法中返回HttpResponseMessage,那么如果立即向IIS Express发出呼叫,它将超时或返回200但是在html错误中响应。 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. 最简单的测试方法是对抛出HttpResponseException的方法进行$ .ajax调用,并在ajax的errorCallBack中立即调用另一个方法甚至是一个简单的http页面。 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. 如果在错误回调中添加断点或settimeout()以延迟第二次或第二次调用,使服务器有时间恢复,则可以正常工作。 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. 这从来没有,但它几乎像throw HttpResponseException导致服务器端侦听器线程退出并重新启动导致没有服务器接受连接的瞬间。

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. 更新: 奇怪的Ajax连接Timeout的根本原因是如果ajax调用足够快,则使用相同的tcp连接。 I was raising a 401 error ether by returning a HttpResonseMessage or throwing a HTTPResponseException which was returned to the browser ajax call. 我通过返回一个HttpResonseMessage或抛出一个返回给浏览器ajax调用的HTTPResponseException来引发401错误。 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. 但随着该调用MS返回一个Object Not Found错误,因为在Startup.Auth.vb app.UserCookieAuthentication已启用,因此它试图返回拦截响应并添加重定向,但它错误地使用Object而不是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. 这个错误是html但是在事实之后附加到响应,所以只有当ajax调用足够快并且使用相同的tcp连接时它才会返回到浏览器然后它被附加到下一个调用的前面。 For some reason Chrome just timedout, fiddler pucked becasue of the mix of json and htm but firefox rturned the real error. 出于某种原因,Chrome只是时间限制,因为json和htm混合使用了fiddler,但firefox掀起了真正的错误。 So wierd but packet sniffer or firefox was the only way to track this one down. 如此奇怪,但数据包嗅探器或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. 更新:我正在撤回关于IIS Express超时的陈述,这恰好是我的客户端ajax回调的错误,因为Ajax 1.8返回$ .ajax()并返回$ .ajax。()。then()两者都返回promise而不是相同的chained promise then()返回一个新的promise,导致执行顺序错误。 So when the then() promise completed it was a script timeout. 因此当then()promise完成时,它是一个脚本超时。 Weird gotcha but not an IIS express issue a problem between the Keyboard and chair. 奇怪的问题,但不是IIS快递问题键盘和椅子之间的问题。

As far as I can tell, whether you throw an exception, or you return Request.CreateErrorResponse, the result is the same. 据我所知,无论是抛出异常还是返回Request.CreateErrorResponse,结果都是一样的。 If you look at the source code for System.Web.Http.dll, you will see as much. 如果你看一下System.Web.Http.dll的源代码,你会看到同样多的东西。 Take a look at this general summary, and a very similar solution that I have made: Web Api, HttpError, and the behavior of exceptions 看一下这个总结,我做了一个非常类似的解决方案: Web Api,HttpError和异常行为

I like Oppositional answer 我喜欢Oppositional的回答

Anyway, I needed a way to catch the inherited Exception and that solution doesn't satisfy all my needs. 无论如何,我需要一种方法来捕获继承的Exception,并且该解决方案不能满足我的所有需求。

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 my2cents

In error situations, I wanted to return a specific error details class, in whatever format the client requested instead of the happy path object. 在错误情况下,我想以客户端请求的任何格式而不是happy路径对象返回特定的错误详细信息类。

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

Result is a class that contains error details, while ProviderCollection is my happy path result. Result是一个包含错误详细信息的类,而ProviderCollection是我的快乐路径结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM