2012-07-09 8 views
14

कहें कि मेरे पास एक कन्स्ट्रक्टर है जहां यह प्रारंभिक रूप से मेरे नियंत्रण से परे कारणों से अपवाद फेंक सकता है।वेबएपीआई में नियंत्रक कन्स्ट्रक्टर के भीतर अपवादों को कैसे संभालना चाहिए?

FantasticApiController(IAwesomeGenerator awesome, 
    IBusinessRepository repository, IIceCreamFactory factory) 
{ 
     Awesome = awesome; 
     Repository = repository; 
     IceCream = factory.MakeIceCream(); 

     DoSomeInitialization(); // this can throw an exception 
} 

साधारणतया, जब वेबएपीआई में एक नियंत्रक कार्रवाई एक अपवाद फेंकता है मैं इसे एक csutom ExceptionFilterAttribute के माध्यम से संभाल कर सकते हैं:

public class CustomErrorHandler 
{ 
    public override void OnException(HttpActionExecutedContext context) 
    { 
     // Critical error, this is real bad. 
     if (context.Exception is BubonicPlagueException) 
     { 
      Log.Error(context.Exception, "CLOSE EVERYTHING!"); 
      Madagascar.ShutdownAllPorts(); 
     } 

     // No big deal, just show something user friendly 
     throw new HttpResponseException(new HttpResponseMessage 
     { 
      Content = new StringContent("Hey something bad happened. " + 
             "Not closing the ports though"), 
      StatusCode = HttpStatusCode.InternalServerError; 
     }); 
    } 

तो अगर मैं एक एक BoardPlane एपीआई विधि है जो एक BubonicPlagueException फेंकता है, तो है मेरे CustomerErrorHandler बंदरगाहों को मेडागास्कर में बंद कर देगा और इसे अपेक्षित त्रुटि के रूप में लॉग करेगा। अन्य मामलों में जब यह वास्तव में गंभीर नहीं होता है, तो मैं बस कुछ उपयोगकर्ता के अनुकूल संदेश प्रदर्शित करता हूं और 500 InternalServerError लौटाता हूं।

लेकिन उन मामलों में जहां DoSomeInitialization अपवाद फेंकता है, यह बिल्कुल कुछ भी नहीं करता है। मैं वेबएपीआई नियंत्रक कन्स्ट्रक्टर में अपवाद कैसे संभाल सकता हूं?

+0

वेबएपी की एक दिलचस्प विशेषता यह है कि आप क्लाइंट को अपवाद वापस लौटने के तरीके को आसानी से अनुकूलित कर सकते हैं। एक ही क्रिया विधि के भीतर स्थिति और एचटीएमएल त्रुटि। जाहिर है कि अगर आप कन्स्ट्रक्टर के अंदर अपवाद फेंकते हैं तो आप इसे सब खो देते हैं। मुझे लगता है कि आपको ऐसा होने से बचना चाहिए। अधिकांश तर्क वेबपै विधि में नहीं होना चाहिए जो कि कन्स्ट्रक्टर में नहीं है। मैंने कहा कि मुझे लगता है कि आपको मानक एएसपीनेट त्रुटि प्रबंधन का उपयोग करना चाहिए, जो वेब के भीतर त्रुटि पृष्ठों को कॉन्फ़िगर कर रहा है। कॉनफिग, या ग्लोबल असैक्स में आतंकवादी घटना को रोकना –

उत्तर

13

वेबएपी नियंत्रक बनाए जाते हैं, और इस प्रकार कन्स्ट्रक्टर HttpControllerActivators के माध्यम से बुलाए जाते हैं। डिफ़ॉल्ट सक्रियकर्ता System.Web.Http.Dispatcher.DefaultHttpControllerActivator है।

विकल्प 1 & 2 GitHub यहाँ पर के लिए बहुत मोटा उदाहरण https://github.com/markyjones/StackOverflow/tree/master/ControllerExceptionHandling/src

विकल्प 1 जो काफी अच्छी तरह से काम करता है (आप अच्छी तरह से एक पहले से ही प्रयोग किया जा सकता है) एक डि कंटेनर का उपयोग शामिल है। मैंने अपने उदाहरण के लिए निनजेक्ट का उपयोग किया है और "इंटरसेप्टर" Read More का उपयोग किया है ताकि डिफॉल्ट एचटीपी कंट्रोलर एक्टिवेटर पर विधि बनाएं को कॉल करने और कॉल करने का प्रयास किया जा सके। मैं कम से कम AutoFac और Ninject कि निम्नलिखित के लिए कुछ करने के लिए simlar कर सकते हैं पता है:

इंटरसेप्टर

बनाएं मैं नहीं जानता कि क्या आपके मेडागास्कर और प्रवेश करें आइटम के जीवनकाल गुंजाइश रहे हैं, लेकिन वे कर सकते थे अच्छी तरह से अपने इंटरसेप्टर

public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor 
{ 
    private ILog _log; 
    private IMadagascar _madagascar; 

    public ControllerCreationInterceptor(ILog log, IMadagascar madagascar) 
    { 
     _log = log; 
     _madagascar = madagascar; 
    } 

में इंजेक्ट किया लेकिन अपने प्रश्न में उदाहरण के लिए रखते हुए जहां प्रवेश करें और मेडागास्कर स्टेटिक वैश्विक

public class ControllerCreationInterceptor : Ninject.Extensions.Interception.IInterceptor 
{ 

    public void Intercept(Ninject.Extensions.Interception.IInvocation invocation) 
    { 
     try 
     { 
      invocation.Proceed(); 
     } 
     catch(InvalidOperationException e) 
     { 
      if (e.InnerException is BubonicPlagueException) 
      { 
       Log.Error(e.InnerException, "CLOSE EVERYTHING!"); 
       Madagascar.ShutdownAllPorts(); 
       //DO SOMETHING WITH THE ORIGIONAL ERROR! 
      } 
      //DO SOMETHING WITH THE ORIGIONAL ERROR! 
     } 
    } 
} 
किसी तरह का कर रहे हैं हो

अंत में इंटरसेप्टर वैश्विक asax या App_Start (NinjectWebCommon)

kernel.Bind<System.Web.Http.Dispatcher.IHttpControllerActivator>() 
      .To<System.Web.Http.Dispatcher.DefaultHttpControllerActivator>().Intercept().With<ControllerCreationInterceptor>(); 

विकल्प 2 में नियंत्रक के निर्माण में अपने स्वयं के नियंत्रक उत्प्रेरक IHttpControllerActivator इंटरफेस को लागू करने त्रुटि को लागू करने और संभालने के लिए है में रजिस्टर विधि बनाएं

public class YourCustomControllerActivator : IHttpControllerActivator 
{ 
    private readonly IHttpControllerActivator _default = new DefaultHttpControllerActivator(); 

    public YourCustomControllerActivator() 
    { 

    } 

    public System.Web.Http.Controllers.IHttpController Create(System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, Type controllerType) 
    { 
     try 
     { 
      return _default.Create(request, controllerDescriptor, controllerType); 
     } 
     catch (InvalidOperationException e) 
     { 
      if (e.InnerException is BubonicPlagueException) 
      { 
       Log.Error(e.InnerException, "CLOSE EVERYTHING!"); 
       Madagascar.ShutdownAllPorts(); 
       //DO SOMETHING WITH THE ORIGIONAL ERROR! 
      } 
      //DO SOMETHING WITH THE ORIGIONAL ERROR! 
      return null; 
     } 

    } 
} 

एक बार जब आप अपने स्वयं के कस्टम उत्प्रेरक है डिफ़ॉल्ट उत्प्रेरक वैश्विक asax में switched out हो सकता है:

GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new YourCustomControllerActivator()); 

विकल्प बेशक 3 अगर आप DefaultHttpControllerActivator रैप करने के लिए डेकोरेटर पैटर्न इस्तेमाल कर सकते हैं कन्स्ट्रक्टर में आपके प्रारंभिकरण को वास्तविक नियंत्रकों के तरीकों, गुणों आदि तक पहुंच की आवश्यकता नहीं है ... यानी यह मानते हुए कि इसे कन्स्ट्रक्टर से हटाया जा सकता है ... तो प्रारंभिकरण को फ़िल्टर में स्थानांतरित करना कहीं अधिक आसान होगा

public class MadagascarFilter : AbstractActionFilter 
{ 
    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) 
    { 
    try{ 
      DoSomeInitialization(); // this can throw an exception 
     } 
     catch(BubonicPlagueException e){ 
    Log.Error(e, "CLOSE EVERYTHING!"); 
     Madagascar.ShutdownAllPorts(); 
      //DO SOMETHING WITH THE ERROR       
     } 

     base.OnActionExecuting(actionContext); 
    } 

public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext) 
    { 
     base.OnActionExecuted(actionExecutedContext); 
    } 

    public override bool AllowMultiple 
    { 
     get { return false; } 
    } 


} 
संबंधित मुद्दे