2011-01-13 9 views
23

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

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

रेल फ़िल्टर को बाहर करने की क्षमता के साथ यह आसान बनाता है। एएसपी.नेट एमवीसी आपको नहीं देता है। इसलिए मैंने इसे संभव बनाने का फैसला किया और मैंने सोचा जितना आसान था।

/// <summary> 
/// This will disable any filters of the given type from being applied. This is useful when, say, all but on action need the Authorize filter. 
/// </summary> 
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Class, AllowMultiple=true)] 
public class ExcludeFilterAttribute : ActionFilterAttribute 
{ 

    public ExcludeFilterAttribute(Type toExclude) 
    { 
     FilterToExclude = toExclude; 
    } 

    /// <summary> 
    /// The type of filter that will be ignored. 
    /// </summary> 
    public Type FilterToExclude 
    { 
     get; 
     private set; 
    } 
} 

/// <summary> 
/// A subclass of ControllerActionInvoker that implements the functionality of IgnoreFilterAttribute. To use this, just override Controller.CreateActionInvoker() and return an instance of this. 
/// </summary> 
public class ControllerActionInvokerWithExcludeFilter : ControllerActionInvoker 
{ 
    protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 
    { 
     //base implementation does all the hard work. we just prune off the filters to ignore 
     var filterInfo = base.GetFilters(controllerContext, actionDescriptor);   
     foreach(var toExclude in filterInfo.ActionFilters.OfType<ExcludeFilterAttribute>().Select(f=>f.FilterToExclude).ToArray()) 
     { 
      RemoveWhere(filterInfo.ActionFilters, filter => toExclude.IsAssignableFrom(filter.GetType())); 
      RemoveWhere(filterInfo.AuthorizationFilters, filter => toExclude.IsAssignableFrom(filter.GetType())); 
      RemoveWhere(filterInfo.ExceptionFilters, filter => toExclude.IsAssignableFrom(filter.GetType())); 
      RemoveWhere(filterInfo.ResultFilters, filter => toExclude.IsAssignableFrom(filter.GetType())); 
     } 
     return filterInfo; 
    } 


    /// <summary> 
    /// Removes all elements from the list that satisfy the condition. Returns the list that was passed in (minus removed elements) for chaining. Ripped from one of my helper libraries (where it was a pretty extension method). 
    /// </summary> 
    private static IList<T> RemoveWhere<T>(IList<T> list, Predicate<T> predicate) 
    { 

     if (list == null || list.Count == 0) 
      return list; 
     //note: didn't use foreach because an exception will be thrown when you remove items during enumeration 
     for (var i = 0; i < list.Count; i++) 
     { 
      var item = list[i]; 
      if (predicate(item)) 
      { 
       list.RemoveAt(i); 
       i--; 
      } 
     } 
     return list; 
    } 
} 

/// <summary> 
/// An example of using the ExcludeFilterAttribute. In this case, Action1 and Action3 require authorization but not Action2. Notice the CreateActionInvoker() override. That's necessary for the attribute to work and is probably best to put in some base class. 
/// </summary> 
[Authorize] 
public class ExampleController : Controller 
{ 
    protected override IActionInvoker CreateActionInvoker() 
    { 
     return new ControllerActionInvokerWithExcludeFilter(); 
    } 

    public ActionResult Action1() 
    { 
     return View(); 
    } 

    [ExcludeFilter(typeof(AuthorizeAttribute))] 
    public ActionResult Action2() 
    { 
     return View(); 
    } 

    public ActionResult Action3() 
    { 
     return View(); 
    } 

} 

उदाहरण सही नहीं है। जैसा कि आप देख सकते हैं, यह करने के लिए बहुत सरल था और महान काम करता है। मुझे उम्मीद है कि यह किसी के लिए उपयोगी है?

+0

'सूची । RemoveAll' मौजूद है: http://msdn.microsoft.com/en-us/library/wdka673a.aspx –

+0

हाँ मुझे सूची के बारे में पता है। RemoveAll। समस्या System.Web.Mvc.FilterInfo उन संग्रहों को IList <> और सूची के रूप में प्रकट नहीं करती है, भले ही अंतर्निहित कार्यान्वयन सूची <> है। मैं सूची पर जा सकता था और RemoveAll का उपयोग कर सकता था, लेकिन मुझे लगा कि एपीआई का सम्मान करना सबसे अच्छा था। मेरी छोटी सहायक विधि थोड़ा बदसूरत है, हाँ। मैं आमतौर पर एक सहायक विधि के रूप में एक सहायक पुस्तकालय में tucked है, जो कोड को अधिक क्लीनर बनाता है। लेकिन इसके लिए मैं कॉपी पेस्ट के माध्यम से संकलित करना चाहता था। तुम क्या सोचते हो? –

+0

मौजूदा फ़िल्टर को बाहर करने का एक और तरीका IFilterProvider को कार्यान्वित करना है। यहां पूरा नमूना देखें: http://blogs.microsoft.co.il/blogs/oric/archive/2011/10/28/exclude-a-filter.aspx –

उत्तर

23

मैं here उल्लिखित समाधान पसंद करता हूं। यद्यपि यह आपके जैसा समाधान सामान्य नहीं है, लेकिन मैंने इसे थोड़ा और सरल पाया।

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

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 
public sealed class DisableCompression : Attribute { } 

तो: तो मैं इस तरह एक खाली विशेषता बना

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
public class CompressionFilter : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     bool disabled = filterContext.ActionDescriptor.IsDefined(typeof(DisableCompression), true) || 
         filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(DisableCompression), true); 
     if (disabled) 
      return; 

     // action filter logic here... 
    } 
} 

पेज मैं से जुड़ा हुआ उल्लेख है हालांकि यह है कि इस के लिए है एमवीसी 3, ऐसा लगता है कि एमवीसी 1 में भी काफी अच्छा काम है।

संपादित करें: टिप्पणियों के जवाब में यहां कुछ उपयोग दिखा रहा है। उपर्युक्त परिवर्तन करने से पहले, यह बिल्कुल इस तरह दिखता था, [DisableCompression] विशेषता को छोड़कर जिस विधि को मैं बाहर करना चाहता था उसे ध्वजांकित करता हूं। इसमें कोई अन्य रिफैक्टरिंग शामिल नहीं है।

[CompressionFilter] 
public abstract class BaseController : Controller 
{ 
} 

public class SomeController : BaseController 
{ 
    public ActionResult WantThisActionCompressed() 
    { 
     // code 
    } 

    [DisableCompression] 
    public ActionResult DontWantThisActionCompressed() 
    { 
     // code 
    } 
} 
+0

प्रत्येक विशेषता प्रकार के लिए जिसे आप अक्षम करना चाहते हैं, आपको एक नया " अक्षम करें "विशेषता के साथ-साथ मूल विशेषता को संशोधित करें, और अपने कोड में उस विशेषता के सभी मामलों को प्रतिस्थापित करना सुनिश्चित करें। मेरे समाधान की तुलना में पूरी तरह से बोझिल काम की तरह लगता है, जिसके लिए कोई अतिरिक्त कोड की आवश्यकता नहीं है। एक डेवलपर के रूप में जो डीआरवाई में विश्वास करता है, मैं नहीं देखता कि कोई भी आपके समाधान को बेहतर तरीके से कैसे देख सकता है। मुझे लगता है कि एकमात्र लाभ यह है कि यह अधिक स्पष्ट है। लेकिन तो क्या? –

+0

मुझे स्पष्ट पसंद है क्योंकि मेरे और अन्य डेवलपर्स को समझना आसान है। और वास्तव में, एक्शन फ़िल्टर की संख्या एक वेब ऐप में उपयोग की जाती है, जिसे कुछ कार्यों को छोड़कर सभी पर भी लागू होना आवश्यक है, निश्चित रूप से बहुत कम है। और कोड की 4 अतिरिक्त रेखाएं मेरे लिए बोझिल नहीं लगती हैं। यह सुनिश्चित नहीं है कि आप कहां से अन्य सभी बोझिल हो रहे हैं, जहां तक ​​यह आपके प्रधानाचार्य पर बहुत अधिक काम करता है। – Gavin

+0

इसलिए [प्राधिकरण] विशेषता को अक्षम करने के लिए, आपको [प्राधिकृत] को कुछ [अक्षम करने योग्य प्राधिकृत] में उपclass करने की आवश्यकता है, फिर [अक्षम करें प्राधिकृत] नामक एक नया बनाएं। तब आपको [अक्षम करने योग्य प्राधिकरण] के साथ अपने ऐप में [प्राधिकरण] के सभी मामलों को प्रतिस्थापित करने की आवश्यकता है और सुनिश्चित करें कि हर कोई [अक्षम करने योग्य प्राधिकरण] का उपयोग करने के लिए याद रखता है। एक रखरखाव दुःस्वप्न के साथ-साथ 2 नए वर्गों से भी बचा जा सकता है जिन्हें टाला जा सकता है। और जैसे आपने कहा, गुणों को अक्षम करने के लिए आपको कितनी बार आवश्यकता है। तो क्यों सभी परेशानी से गुजरना है? [ExcludeFilter] विशेषता त्वरित और आसान है, अगर केवल एक बार उपयोग की जाती है। –

0

मैं साल पहले कि [AllowAnnonymous] विशेषता ASP.NET MVC करने के लिए जोड़ा नहीं किया गया था के लिए मान लेते हैं। आज मेरे पास सभी क्रिया विधियों के लिए लागू मेरे नियंत्रक के शीर्ष पर [Authorize] विशेषता हो सकती है और मैं बस क्रियाओं में इसे ओवरराइड करता हूं, मुझे विशिष्ट कार्यों में [AllowAnonymous] विशेषताओं को जोड़कर अनधिकृत उपयोगकर्ताओं की आवश्यकता होती है।

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