HttpResponseException을 던지거나 Request.CreateErrorResponse를 반환 하시겠습니까?


172

ASP.NET 웹 API의 예외 처리 기사를 검토 한 후 예외 를 던질 때와 오류 응답을 반환하는 시점에 대해 약간 혼란스러워합니다. 또한 메소드가 대신 도메인 특정 모델을 반환 할 때 응답을 수정할 수 있는지 궁금합니다 HttpResponseMessage.

그래서, 여기에 요약하자면 내 질문과 사례 번호가있는 코드가 있습니다.

질문

사례 # 1 관련 질문

  1. HttpResponseMessage메시지를 사용자 정의 할 수 있도록 항상 구체적 도메인 모델 대신 사용해야합니까 ?
  2. 구체적 도메인 모델을 반환하는 경우 메시지를 사용자 정의 할 수 있습니까?

사례 # 2,3,4에 관한 질문

  1. 예외를 던지거나 오류 응답을 반환해야합니까? 대답이 "그것에 달려있다"면, 당신은 상황과 예를 하나를 사용할 때와 다른 것을 사용할 수있는 시점에 줄 수 있습니다.
  2. 던지는 사이의 차이 무엇입니까 HttpResponseException대는 Request.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);
}

6
@Mike Wasson 링크 된 기사의 저자로서 어떤 접근 방식을 취 하시겠습니까?
zam6ak

답변:


102

내가 취한 접근법은 API 컨트롤러 작업에서 예외를 throw하고 예외를 처리하고 작업 실행 컨텍스트에 적절한 응답을 설정하는 예외 필터를 등록하는 것입니다.

필터는 전역 구성으로 필터를 등록하기 전에 특정 유형의 예외에 대한 핸들러를 등록하는 수단을 제공하는 유연한 인터페이스를 제공합니다.

이 필터를 사용하면 컨트롤러 조치에 분산시키는 대신 중앙 집중식 예외 처리가 가능합니다. 그러나 컨트롤러 작업 내에서 예외를 포착하고 특정 예외 처리를 중앙 집중화하는 것이 적합하지 않은 경우 특정 응답을 반환하는 경우가 있습니다.

필터 등록 예 :

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

소스 코드는 여기 에서도 찾을 수 있습니다 .


와! :) 이것은 소규모 프로젝트의 경우 다소 많지만 여전히 훌륭합니다 ... BTW, 왜 DefaultHandler의 CreateErrorResponse 대신 CreateResponse입니까?
zam6ak

오류 문구 (본문에 직렬화 됨)를 이유 문구와 분리하려고했습니다. 그러나 모델 바인딩의 경우와 같이 더 의미가 있다면 CreateErrorResponse를 사용할 수 있습니다.
야당

1
한 줄의 코드로 필터를 등록 할 수 있으므로 거의 모든 프로젝트 유형에 적합하다고 생각합니다. 내부 NuGet 피드에 게시 된 클래스 라이브러리에 필터가 있으므로 개발자가 쉽게 사용할 수 있습니다.
야당

경비원 (가정 또는 제 3 자)을 위해 무엇을 사용하고 있습니까?
zam6ak

Hoemgrown, 나는 위의 예에서 그 사용을 제거했습니다. Guard 클래스는 어설 션이 충족되는지 보호하거나 확인하는 정적 메소드 세트를 제공합니다. 참조 codepaste.net/5oc1if (가드)와 codepaste.net/nsrsei 당신이 구현하려는 경우 (DelegateInfo을).
야당

23

HttpResponseMessage를 반환하지 않고 엔티티 / 모델 클래스를 직접 반환하는 경우 유용한 방법은 컨트롤러에 다음 유틸리티 함수를 추가하는 것입니다.

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

적절한 상태 코드와 메시지로 간단히 호출하십시오.


4
이것은 정답이며 본문에는 "메시지"형식이 키 값 쌍으로 제공됩니다. 이것은 일반적으로 다른 프레임 워크와 언어가하는 방식입니다
MobileMon

이 접근법에 대한 사소한 질문이 있습니다. angularJS 페이지에서 {{}} 구문을 사용하여 메시지를 소비하고 있습니다. 캐리지 리턴을 남겨두면 메시지에서 n \ r \로 나타납니다. 그것들을 보존하는 올바른 방법은 무엇입니까?
나오미

나는이 접근법을 시도했다. 나는 throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid Request Format!"))했지만 Fiddler에서는 상태 500 (400 아님)을 보여줍니다. 왜 그런지 알아?
Sam

차이점은 오류의 부모 함수입니다. 다음은 응용 프로그램의 예외에 대한 ThrowResponseException입니다. 그러나 예외를 던지는 실제 함수 여야합니다 ...
Serge

15

사례 # 1

  1. 반드시 파이프 라인에 응답 (작업 필터, 메시지 처리기)을 수정하는 다른 위치가있는 것은 아닙니다.
  2. 위를 참조하십시오.하지만 액션이 도메인 모델을 반환 하면 액션 내부 의 응답을 수정할 수 없습니다 .

사례 # 2-4

  1. HttpResponseException을 발생시키는 주요 이유는 다음과 같습니다.
    • 도메인 모델을 반환하지만 오류 사례를 처리해야하는 경우,
    • 오류를 예외로 처리하여 컨트롤러 논리를 단순화
  2. 이것들은 동등해야합니다. HttpResponseException은 HTTP 응답으로 반환되는 HttpResponseMessage를 캡슐화합니다.

    예를 들어, 사례 # 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 / etc로 직렬화 할 수 있지만 필수는 아닙니다. 예를 들어 응답에 엔터티 본문을 포함하지 않거나 다른 형식을 원할 수 있습니다.


내가 취한 접근법은 API 컨트롤러 작업에서 예외를 throw하는 것이며 예외를 처리하고 작업 실행 컨텍스트에 적절한 응답을 설정하는 예외 필터를 등록했습니다. 필터는 '플러그 가능'하므로 전역 구성으로 필터를 등록하기 전에 특정 유형의 예외에 대한 핸들러를 등록 할 수 있습니다. 이를 통해 컨트롤러 전체에 예외를 분산시키는 대신 중앙 집중식 예외 처리를 수행 할 수 있습니다.
야당

@Oppositional 예외 필터를 기꺼이 공유 하시겠습니까? 아마도 Gist 또는 CodePaste와 같은 코드 공유 사이트에서?
Paige Cook

@Mike Wasson "반환 오류 응답"이 "예외 던지기"보다 더 일반적인 접근법이라고 말할 수 있습니까? 나는 기능적으로 최종 결과가 같을 지 이해하지만, 전체 컨트롤러 로직을 try / catch에 포함시키고 오류 응답을 적절하게 반환하는 이유가 무엇인지 궁금합니다.
zam6ak

15

HttpResponseException을 던지거나 오류에 대한 HttpResponesMessage를 반환하지 않음 - 를 제외하고 의도하는 경우 요청 종료정확히 일치하는 결과를 .

HttpResponseException은 다른 예외와 동일하게 처리되지 않습니다 . 그들은되는 예외 필터에서 잡은 없습니다 . 이들은 예외 처리기에서 발견되지 않습니다 . 그것들은 현재 코드의 실행 흐름을 종료하면서 HttpResponseMessage를 미끄러 뜨리는 교활한 방법입니다.

코드가이 특수 처리에 의존하는 인프라 코드가 아닌 경우 HttpResponseException 유형을 사용 하지 마십시오 !

HttpResponseMessage는 예외가 아닙니다. 현재 코드의 실행 흐름을 종료하지 않습니다. 예외로 필터링 할 수 없습니다 . 예외로 기록 할 수 없습니다 . 그것들은 유효한 결과를 나타냅니다. 500 개의 응답조차도 "올바른 비 예외 응답"입니다!


인생을 더 단순하게 만드십시오 :

예외 / 오류 사례가있는 경우 일반 .NET 예외 또는 사용자 정의 된 애플리케이션 예외 유형 ( 하지 이러한 상태 코드로 'HTTP 오류 / 응답의 속성 원하는과 HttpResponseException에서 파생를) -에 따라 일반 예외 취급 .

예외 필터 / 예외 처리기 / 예외 로거를 사용하여 다음과 같은 예외적 인 상황에 적합한 작업을 수행하십시오. 상태 코드 변경 / 추가? 추적 식별자를 추가 하시겠습니까? 스택 트레이스를 포함 하시겠습니까? 로그?

HttpResponseException을 피함으로써 '예외 사례'처리가 균일 해지고 노출 된 파이프 라인의 일부로 처리 될 수 있습니다! 예를 들어 응용 프로그램 수준 예외를 사용하여 'NotFound'를 404로, 'ArgumentException'을 400으로, 'NullReference'를 500으로 쉽고 균일하게 바꿀 수 있습니다. 반면에 확장 성은 오류 로깅과 같은 "기본"을 제공 할 수 있습니다.


2
ArgumentException컨트롤러의 s가 논리적으로 400이되는 이유를 이해 하지만 ArgumentException스택에서 더 깊은 것은 무엇 입니까? 이것을 400으로 바꾸는 것이 반드시 정확하지는 않지만, 모든 ArgumentExceptions를 400으로 변환하는 필터가 있는 경우 컨트롤러에서 예외를 잡아서 다른 것을 다시 던지는 것만 피할 수 있습니다. 필터 등에서 균일 한 예외 처리의 목적을 없애기 위해.
cmeeren

@cmeeren 내가 처리하는 코드에서 대부분 예외를 잡아 각 웹 방법에서 HttpResponse [Exception / Message]로 바꿨다. 발견 된 내부 예외로 "무언가"를 수행하여 수행되는 내부 예외로 다른 작업을 수행하려는 경우 두 가지 경우 모두 동일 합니다. 스택.
user2864740

@cmeeren 업데이트 후 대부분의 웹 진입 점은 사용 오류에 대한 특별한 파생 (비 HttpResponseException, 적절한 응답 코드가 있고 매핑 된)을 발생시킵니다. 균일 핸들러는 예외가 어느 레벨에서 발생했는지 판별하기 위해 스택 검사를 수행 할 수 있습니다 (즉, 약간의주의가 필요합니다). 보다 정교한 처리가없는 경우의 99 %를 커버하거나 내부 오류에 대해 500으로 간단히 응답합니다. HttpResponseException의 핵심은 유용한 파이프 라인 처리를 무시한다는 것입니다.
user2864740

9

HttpResponseException대신에 사용 하는 경우에 대한 또 다른 경우Response.CreateResponse(HttpStatusCode.NotFound)조치 필터에 트랜잭션이 있고 클라이언트에 오류 응답을 리턴 할 때 트랜잭션을 롤백하려는 경우, 또는 기타 오류 상태 코드 가 있습니다.

를 사용 Response.CreateResponse하면 트랜잭션이 롤백되지 않고 예외가 발생합니다.


3

webapi 2 메서드에서 HttpResponseMessage를 반환하는 대신 HttpResponseException을 throw하면 IIS Express에 즉시 호출하면 시간 초과 또는 200이 반환되지만 HTML 오류가 발생합니다. 응답. 이것을 테스트하는 가장 쉬운 방법은 HttpResponseException을 발생시키는 메소드를 $ .ajax 호출하고 아약스의 errorCallBack에서 다른 메소드 또는 간단한 http 페이지를 즉시 호출하는 것입니다. 즉시 통화가 실패 함을 알 수 있습니다. 오류 호출에 중단 점 또는 settimeout ()을 추가하여 두 번째 호출을 1 ~ 2 초 지연시켜 서버가 복구 할 시간을주는 것이 올바르게 작동합니다.

최신 정보:잘못된 Ajax 연결 시간 초과의 근본 원인은 동일한 tcp 연결이 사용되도록 ajax 호출이 빠르게 수행되는 경우입니다. HttpResonseMessage를 반환하거나 브라우저 아약스 호출로 반환 된 HTTPResponseException을 발생시켜 401 오류 에테르를 발생 시켰습니다. 그러나 그 호출과 함께 MS는 Startup.Auth.vb 앱에서 User Not Found 오류를 반환했습니다 .UserCookieAuthentication이 활성화되어 응답을 가로 채고 리디렉션을 추가하려고 시도했지만 Object의 Instance가 아닌 Object에 오류가 발생했습니다. 이 오류는 html이지만 사실 이후 응답에 추가되었으므로 ajax 호출이 충분히 빠르게 이루어졌고 동일한 tcp 연결이 브라우저로 리턴되어 다음 호출 앞에 추가 된 경우에만 가능합니다. 어떤 이유로 Chrome이 시간 초과되었습니다. 피들러는 json과 htm의 혼합으로 인해 펑크했지만 파이어 폭스는 실제 오류를 돌 렸습니다. 패킷 스니퍼 또는 파이어 폭스는 이것을 추적하는 유일한 방법입니다.

또한 웹 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 1.8이 $ .ajax ()를 반환하고 $ .ajax. (). then () 둘 다 약속을 반환하지만 동일한 체인 약속을 반환하지 않으면 then ()은 실행 순서가 잘못 된 새로운 약속을 반환합니다. 따라서 then () 약속이 완료되면 스크립트 시간 초과였습니다. 키보드와 의자 사이에 IIS Express가 아닌 이상한 문제가 있습니다.


0

내가 알 수있는 한, 예외를 던지거나 Request.CreateErrorResponse를 반환하는지 여부는 결과가 동일합니다. System.Web.Http.dll의 소스 코드를 보면 많이 볼 수 있습니다. 이 일반적인 요약과 Web Api, HttpError 및 예외의 동작과 비슷한 솔루션을 살펴보십시오.


0

오류 상황에서 클라이언트가 행복한 경로 객체 대신 요청한 형식으로 특정 오류 세부 정보 클래스를 반환하려고했습니다.

컨트롤러 메소드가 도메인 고유의 행복한 경로 객체를 반환하고 그렇지 않으면 예외를 발생시키고 싶습니다.

내가 가진 문제는 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 행복한 경로 결과입니다.


0

나는 반대 답변을 좋아한다

어쨌든 상속 된 예외를 잡을 수있는 방법이 필요했으며 그 솔루션이 모든 요구를 충족 시키지는 못합니다.

그래서 나는 그가 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;
    }
}

내 2 센트

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.