2012-05-22 5 views
11

मेरे पास ऐसी स्थिति है जहां मुझे अपने कस्टम प्राधिकरण विशेषता में एक कस्टम फ़िल्टर, अर्थात्, मेरे कस्टम प्राधिकरण प्रदाता में कुछ निर्भरताओं को इंजेक्ट करने की आवश्यकता है। मैंने बहुत से लोगों और पदों पर ठोकर खाई जो कह रहे थे कि हमें 'व्यवहार' से 'विशेषता मेटाडाटा' को अलग करना चाहिए। यह समझ में आता है और यह भी तथ्य है कि फ़िल्टर गुणों को 'निर्भरता रीसोलवर' के माध्यम से तत्काल नहीं किया जाता है, इसलिए निर्भरताओं को इंजेक्ट करना मुश्किल होता है।IFilterProvider और चिंताओं को अलग करना

इसलिए मैंने अपने कोड का थोड़ा सा रिफैक्टरिंग किया और मैं जानना चाहता था कि मेरे पास यह सही है (मैं डीआई फ्रेमवर्क के रूप में कैसल विंडसर का उपयोग कर रहा हूं)।

सबसे पहले मैं अपने गुण छीन केवल कच्चे डेटा मैं

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class MyAuthorizeAttribute : Attribute 
{ 
    public string Code { get; set; } 
} 

मैं एक कस्टम प्राधिकरण फिल्टर है कि यह निर्धारित करने के लिए वर्तमान उपयोगकर्ता उचित प्राधिकरण

public class MyAuthorizationFilter : IAuthorizationFilter 
{ 
    private IAuthorizationProvider _authorizationProvider; 
    private string _code; 

    public MyAuthorizationFilter(IAuthorizationProvider authorizationProvider, string code) 
    { 
     Contract.Requires(authorizationProvider != null); 
     Contract.Requires(!string.IsNullOrWhiteSpace(code)); 

     _authorizationProvider = authorizationProvider; 
     _code = code; 
    } 

    public void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if (filterContext == null) 
     { 
      throw new ArgumentNullException("filterContext"); 
     } 

     if (filterContext.HttpContext.Request.IsAuthenticated) 
     { 
      BaseController controller = filterContext.Controller as BaseController; 
      if (controller != null) 
      { 
       if (!IsAuthorized(controller.CurrentUser, controller.GetCurrentSecurityContext())) 
       { 
        // forbidden 
        filterContext.RequestContext.HttpContext.Response.StatusCode = 403; 
        if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest()) 
        { 
         filterContext.Result = new RedirectToRouteResult("default", new RouteValueDictionary(new 
         { 
          action = "http403", 
          controller = "error" 
         }), false); 
        } 
        else 
        { 
         filterContext.Result = controller.InvokeHttp404(filterContext.HttpContext); 
        } 
       } 
      } 
      else 
      { 

      } 
     } 
     else 
     { 
      filterContext.Result = new RedirectResult(FormsAuthentication.LoginUrl); 
     } 
    } 

    private bool IsAuthorized(MyUser user, BaseSecurityContext securityContext) 
    { 
     bool has = false; 
     if (_authorizationProvider != null && !string.IsNullOrWhiteSpace(_code)) 
     { 
      if (user != null) 
      { 
       if (securityContext != null) 
       { 
        has = _authorizationProvider.HasPermission(user, _code, securityContext); 
       } 
      } 
     } 
     else 
     { 
      has = true; 
     } 
     return has; 
    } 
} 
है के तर्क होते हैं बनाया जरूरत को रोकने के लिए

अंतिम भाग एक कस्टम फ़िल्टर प्रदाता बनाना था जो इस विशिष्ट विशेषता को लाएगा और मेरे कस्टम फ़िल्टर को इसकी निर्भरताओं को पारित करेगा और किसी भी डेटा को विशेषता से निकाला जाएगा।

public class MyAuthorizationFilterProvider : IFilterProvider 
{ 
    private IWindsorContainer _container; 

    public MyAuthorizationFilterProvider(IWindsorContainer container) 
    { 
     Contract.Requires(container != null); 
     _container = container; 
    } 

    public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 
    { 
     Type controllerType = controllerContext.Controller.GetType(); 
     var authorizationProvider = _container.Resolve<IAuthorizationProvider>(); 
     foreach (MyAuthorizeAttribute attribute in controllerType.GetCustomAttributes(typeof(MyAuthorizeAttribute), false)) 
     { 
      yield return new Filter(new MyAuthorizationFilter(authorizationProvider, attribute.Code), FilterScope.Controller, 0); 
     } 
     foreach (MyAuthorizeAttribute attribute in actionDescriptor.GetCustomAttributes(typeof(MyAuthorizeAttribute), false)) 
     { 
      yield return new Filter(new MyAuthorizationFilter(authorizationProvider, attribute.Code), FilterScope.Action, 0); 
     } 
    } 
} 

अंतिम चरण Global.asax

FilterProviders.Providers.Add(new MyAuthorizationFilterProvider(_container)); 

तो मैं पहले सोच रहा हूँ में फिल्टर प्रदाता रजिस्टर, अगर मैं विचार का अधिकार मिल गया और दूसरा, क्या सुधार किया जा सकता है।

+0

हाय फ्रेंकोइस, मैं आपके जैसी ही समस्या के लिए एक बहुत ही समान समाधान के साथ आया था। मैं वर्तमान में खुद को वही प्रश्न पूछ रहा हूं जैसा आप थे। क्या आपने इस समाधान का उपयोग कर समाप्त किया? समय के साथ इसके साथ कोई समस्या है? क्या आपके पास कोई सिफारिश है? धन्यवाद। –

उत्तर

2

हाँ, मुझे लगता है कि आपको सही विचार मिल गया है। मुझे यह पसंद है कि आप विशेषता और फ़िल्टर कार्यान्वयन के बीच चिंताओं को अलग कर रहे हैं, और मुझे लगता है कि आप संपत्ति DI के बजाय निर्माता डी का उपयोग कर रहे हैं।

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

यदि आप फ़िल्टर के साथ विशेषता को जोड़ना चाहते हैं और संपत्ति DI का उपयोग करना चाहते हैं, तो अधिक decoupled फ़िल्टर प्रदाता होने का एक आसान तरीका है। यहाँ है कि दृष्टिकोण के दो उदाहरण हैं: http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3 http://lozanotek.com/blog/archive/2010/10/12/dependency_injection_for_filters_in_mvc3.aspx

वर्तमान दृष्टिकोण के हल करने के लिए दो चुनौतियों के होते हैं: 1. कुछ इंजेक्शन लेकिन सभी नहीं, डि के माध्यम से फिल्टर निर्माता मापदंडों का। 2. किसी विशेषता से (निर्भरता-इंजेक्शन) फ़िल्टर उदाहरण में मैपिंग।

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

चुनौती # 1 के लिए, आप _container की तरह कुछ उपयोग कर सकते हैं। ओवरलोड लोड करें जो आपको तर्कों में पारित करने देता है। वह समाधान कंटेनर-विशिष्ट और शायद थोड़ा मुश्किल है।

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

public class MyAuthorizationFilterFactory : IFilterInstanceFactory 
{ 
    private readonly IAuthorizationProvider provider; 

    public MyAuthorizationFilterFactory(IAuthorizationProvider provider) 
    { 
     this.provider = provider; 
    } 

    public object Create(Attribute attribute) 
    { 
     MyAuthorizeAttribute authorizeAttribute = attribute as MyAuthorizeAttribute; 

     if (authorizeAttribute == null) 
     { 
      return null; 
     } 

     return new MyAuthorizationFilter(provider, authorizeAttribute.Code); 
    } 
} 

तुम बस प्रत्येक पंजीकरण से # 2 चुनौती का समाधान कर सकते हैं:

public interface IFilterInstanceFactory 
{ 
    object Create(Attribute attribute); 
} 

फिर आप प्रत्येक विशेषता/फिल्टर जोड़ी के लिए एक कारखाने को लागू करेंगे:

यहाँ कि कारखाना कैसा दिखाई दे सकता है CastleWindsor के साथ IFilterInstanceFactory का कार्यान्वयन।

फिल्टर प्रदाता अब विशिष्ट विशेषताओं और फिल्टर की किसी भी जानकारी से decoupled जा सकता है:

public class MyFilterProvider : IFilterProvider 
{ 
    private IWindsorContainer _container; 

    public MyFilterProvider(IWindsorContainer container) 
    { 
     Contract.Requires(container != null); 
     _container = container; 
    } 

    public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 
    { 
     Type controllerType = controllerContext.Controller.GetType(); 
     var authorizationProvider = _container.Resolve<IAuthorizationProvider>(); 
     foreach (FilterAttribute attribute in controllerType.GetCustomAttributes(typeof(FilterAttribute), false)) 
     { 
      object instance = Resolve(attribute); 
      yield return new Filter(instance, FilterScope.Controller, 0); 
     } 
     foreach (FilterAttribute attribute in actionDescriptor.GetCustomAttributes(typeof(FilterAttribute), false)) 
     { 
      object instance = Resolve(attribute); 
      yield return new Filter(instance, FilterScope.Action, 0); 
     } 
    } 

    private object Resolve(Attribute attribute) 
    { 
     IFilterInstanceFactory[] factories = _container.ResolveAll<IFilterInstanceFactory>(); 

     foreach (IFilterInstanceFactory factory in factories) 
     { 
      object dependencyInjectedInstance = factory.Create(attribute); 

      if (dependencyInjectedInstance != null) 
      { 
       return dependencyInjectedInstance; 
      } 
     } 

     return attribute; 
    } 
} 

डेविड

+0

शायद कुछ याद आ रहा है, लेकिन वास्तव में यह कोड विशेषता का एक उदाहरण नहीं देता है (संबंधित फ़िल्टर नहीं)। 'कोड' ऑब्जेक्ट उदाहरण = हल (विशेषता); उपज वापसी नया फ़िल्टर (उदाहरण, फ़िल्टरस्कोप.एक्शन, 0); 'कोड' –

+0

दोह, मुझे अनदेखा करें, दोबारा पढ़ें और महसूस किया कि फैक्ट्री फ़िल्टर निर्माण को संभालती है। –

0

यह शायद थोड़ा ज्यादा है, लेकिन कारखाने से बचने का एक ही रास्ता है दाऊद ने सुझाव दिया के रूप में (और इसे थोड़ा और सामान्य बनाना) अभी तक एक और विशेषता पेश करना है।

[AssociatedFilter(typeof(MyAuthorizationFilter))] 

जो आप मूल विशेषता में निम्नानुसार जोड़ सकते हैं।

[AssociatedFilter(typeof(MyAuthorizationFilter))] 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class MyAuthorizeAttribute : Attribute 
{ 
    public string Code { get; set; } 
} 

एसोसिएटेडफिल्टर विशेषता इस तरह दिखती है।

public class AssociatedFilterAttribute : Attribute 
{ 
    public AssociatedFilterAttribute(Type filterType) 
    { 
     FilterType = filterType; 
    } 
    public Type FilterType { get; set; } 
} 

फिर आप इस विशेषता से फ़िल्टरटाइप खींचकर सही फ़िल्टर पुनर्प्राप्त कर सकते हैं।

private object Resolve(Attribute attribute) 
{ 
    var filterAttributes = attribute.GetType().GetCustomAttributes(typeof(AssociatedFilterAttribute), false); 
    var first = (AssociatedFilterAttribute)filterAttributes.FirstOrDefault(); 
    return new Filter(_container.Resolve(first.FilterType), FilterScope.First, null); 
} 

वर्तमान में यह सैद्धांतिक रूप से मैं आप जोड़ सकते एक से अधिक (एक विशेषता की शुरूआत की कई फिल्टर) जिस स्थिति में आप थोड़ा छोड़ चाहते हैं, जहां इस पकड़ लेता है पहले लगता है कि केवल पहले AssociatedFilter विशेषता लेने तक ही सीमित है परिणाम।

स्पष्ट रूप से हमें त्रुटि प्रबंधन जोड़ने की भी आवश्यकता है, उदा। यदि कोई एसोसिएटेडफिल्टर एट्रिब्यूट नहीं है ...

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