HttpResponseException को फेंकें या Request.CreateErrorResponse लौटाएँ?


172

ASP.NET वेब एपीआई में एक लेख अपवाद हैंडलिंग की समीक्षा करने के बाद, मैं थोड़ा उलझन में हूं कि कब एक अपवाद बनाम एक त्रुटि प्रतिक्रिया वापस करना है। मुझे यह सोचकर भी आश्चर्य हो रहा है कि क्या प्रतिक्रिया को संशोधित करना संभव है, जब आपका तरीका बदले में एक डोमेन विशिष्ट मॉडल देता है 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
@ माइक वैसन लिंक्ड लेख के लेखक के रूप में, आप किस दृष्टिकोण को अपनाएंगे?
zam6ak

जवाबों:


102

मैंने जो दृष्टिकोण लिया है वह केवल एपीआई नियंत्रक कार्यों से अपवादों को फेंकना है और एक अपवाद फ़िल्टर पंजीकृत है जो अपवाद को संसाधित करता है और कार्रवाई निष्पादन संदर्भ पर एक उपयुक्त प्रतिक्रिया सेट करता है।

फ़िल्टर एक धाराप्रवाह इंटरफ़ेस को उजागर करता है जो फ़िल्टर को वैश्विक कॉन्फ़िगरेशन के साथ पंजीकृत करने से पहले विशिष्ट प्रकार के अपवादों के लिए रजिस्टर करने का साधन प्रदान करता है।

इस फिल्टर का उपयोग नियंत्रक क्रियाओं में इसे फैलाने के बजाय केंद्रीकृत अपवाद को संभालने में सक्षम बनाता है। हालांकि ऐसे मामले हैं जहां मैं नियंत्रक कार्रवाई के अपवादों को पकड़ लूंगा और एक विशिष्ट प्रतिक्रिया लौटाऊंगा, अगर यह उस विशेष अपवाद के हैंडलिंग को केंद्रीकृत करने के लिए समझ में नहीं आता है।

फ़िल्टर का उदाहरण पंजीकरण:

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, क्यों DefaultRandler में CreateErrorResponse के बजाय CreateResponse?
zam6ak

मैं कारण वाक्यांश से त्रुटि विवरण (शरीर में क्रमबद्ध) को अलग करने का प्रयास कर रहा था; लेकिन आप निश्चित रूप से CreateErrorResponse का उपयोग कर सकते हैं यदि वह मॉडल बाइंडिंग के मामले में अधिक समझ में आता है।
विपक्षी

1
चूंकि आप फ़िल्टर को सिर्फ एक लाइन कोड के साथ पंजीकृत कर सकते हैं, मुझे लगता है कि यह लगभग किसी भी परियोजना के प्रकार के लिए उपयुक्त है। हमारे पास एक क्लास लाइब्रेरी में फ़िल्टर है जो हमारे आंतरिक नुगेट फ़ीड पर प्रकाशित है, इसलिए डेवलपर्स के लिए इसका उपयोग करना आसान है।
विपक्षी

आप गार्ड (होमग्रोन या थर्ड पार्टी) के लिए क्या उपयोग कर रहे हैं?
zam6ak

Hoemgrown, मैंने ऊपर के उदाहरण में इसका उपयोग हटा दिया। गार्ड वर्ग स्थिर तरीकों का एक सेट प्रदान करता है, जो यह सुनिश्चित करता है कि सत्यापन किया गया है या नहीं। यदि आप कार्यान्वयन चाहते हैं तो 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

मेरे पास इस दृष्टिकोण के बारे में एक मामूली सवाल है। मैं कोणीयजेएस पृष्ठ में {{}} सिंटैक्स का उपयोग करके संदेश का उपभोग कर रहा हूं। अगर मैं कैरिज रिटर्न छोड़ता हूं, तो वे संदेश में n \ r \ के रूप में आते हैं। उन्हें संरक्षित करने का सही तरीका क्या है?
नाओमी

मैंने इस दृष्टिकोण की कोशिश की। मैंने किया throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid Request Format!")), लेकिन फिडलर में, यह स्थिति 500 ​​(400 नहीं) को दर्शाता है। कोई विचार क्यों?
सैम

अंतर त्रुटि का मूल कार्य है। यहाँ अनुप्रयोग में किसी भी अपवाद के लिए ThrowResponseException है। लेकिन अपवाद को फेंकने वाला वास्तविक कार्य होना चाहिए ...
सर्ज

15

मामला एक

  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 / etc पर क्रमबद्ध किया जा सकता है, लेकिन इसकी आवश्यकता नहीं है। उदाहरण के लिए, आप प्रतिक्रिया में एक निकाय निकाय को शामिल नहीं करना चाह सकते हैं, या आप कुछ अन्य प्रारूप चाहते हैं।


मैंने जो दृष्टिकोण लिया है, वह केवल एपीआई नियंत्रक कार्यों से अपवादों को फेंकना है और मैंने एक अपवाद फ़िल्टर पंजीकृत किया है जो अपवाद को संसाधित करता है और कार्रवाई निष्पादन संदर्भ पर एक उपयुक्त प्रतिक्रिया सेट करता है। फ़िल्टर 'प्लग करने योग्य' है, जैसे कि मैं वैश्विक कॉन्फ़िगरेशन के साथ फ़िल्टर को पंजीकृत करने से पहले विशिष्ट प्रकार के अपवादों के लिए हैंडलर पंजीकृत कर सकता हूं। यह मुझे नियंत्रकों में फैलाने के बजाय केंद्रीकृत अपवाद हैंडलिंग करने की अनुमति देता है।
विपक्षी

@ अपोजिट कोई भी मौका जो आप अपने अपवाद फ़िल्टर को साझा करने के लिए तैयार हैं? शायद एक Gist के रूप में या एक कोड शेयर साइट पर जैसे CodePaste?
पैगी कुक

@ माइक वासन क्या आप कहेंगे कि "रिटर्न एरर रिस्पांस" अधिक सामान्य दृष्टिकोण बनाम "थ्रो अपवाद" है? मैं कार्यात्मक रूप से समझता हूं कि अंतिम परिणाम हो सकता है (है?) वही, लेकिन मैं सोच रहा हूं कि क्यों न केवल कोशिश / पकड़ और वापसी त्रुटि प्रतिक्रिया में पूरे नियंत्रक तर्क को शामिल किया जाए?
zam6ak

15

HttpResponseException को न फेंकें या त्रुटियों के लिए एक HttpResponesMessage लौटाएँ - सिवाय इसके कि इरादे उस सटीक परिणाम के साथ अनुरोध को समाप्त करना है

HttpResponseException अन्य अपवादों के समान नहीं है । वे एक्सेप्शन फिल्टर में नहीं फंसे । वे अपवाद हैंडलर में नहीं पकड़े गए हैं । वे वर्तमान कोड के निष्पादन प्रवाह को समाप्त करते हुए HttpResponseMessage में फिसलने का एक धूर्त तरीका है।

जब तक कोड इस विशेष अन-हैंडलिंग पर निर्भर बुनियादी ढांचा कोड नहीं है, तब तक HttpResponseException type का उपयोग करने से बचें !

HttpResponseMessage अपवाद नहीं हैं। वे वर्तमान कोड के निष्पादन प्रवाह को समाप्त नहीं करते हैं। उन्हें अपवाद के रूप में फ़िल्टर नहीं किया जा सकता है। उन्हें अपवाद के रूप में लॉग इन नहीं किया जा सकता है। वे एक वैध परिणाम का प्रतिनिधित्व करते हैं - यहां तक ​​कि एक 500 प्रतिक्रिया "एक वैध गैर-अपवाद प्रतिक्रिया" है!


जीवन को सरल बनाएं:

जब कोई असाधारण / त्रुटि का मामला हो, तो आगे बढ़ें और एक सामान्य .NET अपवाद को फेंक दें - या एक इच्छित अनुप्रयोग अपवाद प्रकार ( HttpResponseException से प्राप्त नहीं ) वांछित 'http त्रुटि / प्रतिक्रिया' गुणों के साथ जैसे एक स्थिति कोड - सामान्य अपवाद के अनुसार हैंडलिंग

इन असाधारण मामलों के साथ कुछ उपयुक्त करने के लिए अपवाद फ़िल्टर / अपवाद हैंडलर / अपवाद लॉगर्स का उपयोग करें: स्थिति कोड बदलें / जोड़ें? ट्रैकिंग पहचानकर्ता जोड़ें? स्टैक के निशान शामिल हैं? लॉग इन करें?

HttpResponseException से बचकर 'असाधारण मामले' से निपटने को एक समान बनाया जाता है और इसे उजागर पाइपलाइन के हिस्से के रूप में संभाला जा सकता है! उदाहरण के लिए, एक 'NotFound' को 404 में और 'ArgumentException' को 400 में और 'NullReference' को 500 में आसानी से और समान रूप से एप्लिकेशन-लेवल अपवादों के साथ बदल सकता है - जबकि त्रुटि लॉगिंग जैसी "मूल बातें" प्रदान करने के लिए एक्स्टेंसिबिलिटी की अनुमति देता है।


2
मुझे समझ में क्यों ArgumentExceptionनियंत्रक में एक तार्किक रूप से एक 400 होगा, लेकिन क्या ArgumentExceptionस्टैक में गहरा है? जरूरी नहीं कि इन्हें 400 में बदलना सही होगा, फिर भी अगर आपके पास एक ऐसा फ़िल्टर है जो कंबल सभी ArgumentExceptions से 400 में बदल जाता है, तो इससे बचने का एकमात्र तरीका नियंत्रक में अपवाद को पकड़ना है और कुछ और फेंकना है, जो लगता है एक फिल्टर या इसी तरह के वर्दी अपवाद से निपटने के उद्देश्य को हराने के लिए।
कैमरीन

@cmeeren कोड में मैं इसे निपटा रहा था, अधिकांश ने इसका अपवाद पकड़ा और प्रत्येक वेब-विधि में इसे HttpResponse [अपवाद / संदेश] में बदल दिया। दोनों मामले समान हैं , अगर चिंता आंतरिक अपवादों के साथ कुछ अलग करने की है जो पकड़े गए आंतरिक अपवाद के साथ "कुछ" कर रही है: मैं सलाह देता हूं कि एक उचित रैपिंग अपवाद को फेंक दिया जाए जो अभी भी आगे संभाला गया है ढेर।
user2864740 16

@cmeeren अद्यतनों के बाद, त्रुटियों का उपयोग करने के लिए हमारे अधिकांश वेब प्रविष्टि बिंदु एक विशेष व्युत्पन्न (गैर-HttpResponseException, जो हैं और जो उचित प्रतिक्रिया कोड के लिए मैप किए गए हैं) फेंकते हैं। यूनिफ़ॉर्म हैंडलर कुछ स्टैक इंस्पेक्शन (icky कर सकता है, लेकिन यह कुछ देखभाल के साथ काम करता है) यह निर्धारित करने के लिए कि अपवाद किस स्तर से आया है - यानी। 99% मामलों को कवर करें, जिनमें अधिक परिष्कृत हैंडलिंग नहीं है - या केवल आंतरिक त्रुटियों के लिए 500 के साथ प्रतिक्रिया करें। HttpResponseException के साथ क्रूज़ यह है कि यह उपयोगी पाइपलाइन प्रसंस्करण को बायपास करता है।
user2864740 16

9

कब , या अन्य त्रुटि स्थिति कोड के HttpResponseExceptionबजाय कब उपयोग करना है Response.CreateResponse(HttpStatusCode.NotFound), इसके लिए एक और मामला यह है कि यदि आपके पास एक्शन फिल्टर में लेनदेन है और आप चाहते हैं कि क्लाइंट के लिए त्रुटि प्रतिक्रिया लौटने पर लेनदेन वापस हो जाए।

प्रयोग Response.CreateResponseसे लेन-देन वापस नहीं होगा, जबकि अपवाद अपवाद को फेंकना होगा।


3

मैं यह बताना चाहता हूं कि यह मेरा अनुभव रहा है कि अगर किसी वेबपीआई 2 विधि में एक HttpResponseMessage को वापस करने के बजाय एक HttpResponseException को फेंक दिया जाए, तो अगर IIS कॉल में तुरंत कॉल किया जाता है, तो यह 200 बार टाइमआउट या वापस आ जाएगा, लेकिन html त्रुटि के साथ। प्रतिक्रिया। यह परीक्षण करने के लिए सबसे आसान तरीका $ .ajax कॉल एक विधि के लिए है जो एक HttpResponseException को फेंकता है और त्रुटि में CallBack ajax में एक अन्य विधि या यहां तक ​​कि एक साधारण http पृष्ठ पर तत्काल कॉल करें। आप देखेंगे कि इमेडिएट कॉल विफल हो जाएगी। यदि आप एक विराम बिंदु या एक निपटारा () त्रुटि कॉल में जोड़ते हैं तो दूसरी कॉल में देरी करने के लिए दूसरी या दो को सर्वर को ठीक से काम करने के लिए समय देता है।

अपडेट करें:अजीब अजाक्स कनेक्शन का मूल कारण टाइमआउट है, यदि अजाक्स कॉल को त्वरित रूप से किया जाता है, तो उसी tcp कनेक्शन का उपयोग किया जाता है। मैं एक HttpResonseMessage को लौटाकर या HTTPResponseException को फेंककर 401 त्रुटि ईथर को बढ़ा रहा था जिसे ब्राउज़र ajax कॉल में वापस कर दिया गया था। लेकिन उस कॉल के साथ MS एक ऑब्जेक्ट नहीं मिली त्रुटि लौटा रहा था क्योंकि Start.A.Auth.vb ऐप में ।UserCookieAuthentication सक्षम किया गया था, इसलिए यह प्रतिक्रिया को वापस करने और पुनर्निर्देशन को जोड़ने की कोशिश कर रहा था, लेकिन इसने ऑब्जेक्ट का इंस्टेंस ऑब्जेक्ट के साथ गलत किया। यह त्रुटि HTML थी, लेकिन इस तथ्य के बाद प्रतिक्रिया के लिए संलग्न की गई थी, तो केवल अगर अजाक्स कॉल को त्वरित रूप से बनाया गया था और उसी tcp कनेक्शन का उपयोग किया गया था जो ब्राउज़र में वापस आ गया और फिर यह अगले कॉल के सामने जोड़ दिया गया। किसी कारण से क्रोम सिर्फ समय पर, fiddler ने json और htm के मिश्रण के बिकस को पकौड़ा दिया लेकिन फ़ायरफ़ॉक्स ने असली त्रुटि को रोक दिया। इतना अजीब लेकिन पैकेट स्निफर या फायरफॉक्स इस एक को ट्रैक करने का एकमात्र तरीका था।

यह भी ध्यान दिया जाना चाहिए कि यदि आप स्वत: सहायता उत्पन्न करने के लिए वेब एपीआई सहायता का उपयोग कर रहे हैं और आप 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 को सिंगल पेज ऐप में रीफ्रेश करने की आवश्यकता है।

अद्यतन: मैं IIS एक्सप्रेस टाइमिंग के बारे में अपने बयान को वापस ले रहा हूं, यह मेरे क्लाइंट पक्ष में एक गलती के रूप में हुआ है ajax कॉल बैक के बाद से यह बाहर निकलता है। Ajax 1.8 $ .axax () और $ .ajax () वापस आ रहा है। दोनों ही वादे वापस करते हैं लेकिन एक ही जंजीर वाले वादे नहीं () एक नया वादा लौटाते हैं जिसके कारण निष्पादन का आदेश गलत हो जाता है। इसलिए जब तत्कालीन () वादा पूरा हुआ तो यह एक स्क्रिप्ट टाइमआउट था। अजीब गच्चा लेकिन आईआईएस एक्सप्रेस कीबोर्ड और कुर्सी के बीच कोई समस्या नहीं है।


0

जहां तक ​​मैं बता सकता हूं, चाहे आप एक अपवाद को फेंक दें, या आप अनुरोध वापस करें। यदि आप System.Web.Http.dll के स्रोत कोड को देखते हैं, तो आप उतना ही देखेंगे। इस सामान्य सारांश पर एक नज़र डालें, और एक बहुत ही समान समाधान जो मैंने बनाया है: वेब आपी, 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;
    }
}

my2cents

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.