ASP.NET MVC अस्पष्ट कार्रवाई के तरीके


135

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

उदाहरण के लिए...

Items/{action}/ParentName/ItemName
Items/{action}/1234-4321-1234-4321

यहाँ मेरे एक्शन के तरीके हैं ( Removeएक्शन के तरीके भी हैं ) ...

// Method #1
public ActionResult Assign(string parentName, string itemName) { 
    // Logic to retrieve item's ID here...
    string itemId = ...;
    return RedirectToAction("Assign", "Items", new { itemId });
}

// Method #2
public ActionResult Assign(string itemId, string searchTerm, int? page) { ... }

और यहाँ मार्ग हैं ...

routes.MapRoute("AssignRemove",
                "Items/{action}/{itemId}",
                new { controller = "Items" }
                );

routes.MapRoute("AssignRemovePretty",
                "Items/{action}/{parentName}/{itemName}",
                new { controller = "Items" }
                );

मैं समझता हूं कि त्रुटि क्यों हो रही है, चूंकि pageपैरामीटर अशक्त हो सकता है, लेकिन मैं इसे हल करने का सबसे अच्छा तरीका नहीं समझ सकता। क्या मेरा डिजाइन शुरू करने के लिए खराब है? मैंने Method #1खोज मापदंडों को शामिल करने के लिए हस्ताक्षर के बारे में सोचा है और तर्क Method #2को एक निजी पद्धति में स्थानांतरित करने के लिए कहा है जिसे वे दोनों कहेंगे, लेकिन मुझे विश्वास नहीं है कि वास्तव में अस्पष्टता का समाधान होगा।

किसी भी तरह की सहायता का स्वागत किया जाएगा।


वास्तविक समाधान (लेवी के उत्तर पर आधारित)

मैंने निम्न वर्ग जोड़ा है ...

public class RequireRouteValuesAttribute : ActionMethodSelectorAttribute {
    public RequireRouteValuesAttribute(string[] valueNames) {
        ValueNames = valueNames;
    }

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
        bool contains = false;
        foreach (var value in ValueNames) {
            contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value);
            if (!contains) break;
        }
        return contains;
    }

    public string[] ValueNames { get; private set; }
}

और फिर एक्शन के तरीकों को सजाया ...

[RequireRouteValues(new[] { "parentName", "itemName" })]
public ActionResult Assign(string parentName, string itemName) { ... }

[RequireRouteValues(new[] { "itemId" })]
public ActionResult Assign(string itemId) { ... }

3
वास्तविक कार्यान्वयन को पोस्ट करने के लिए धन्यवाद। यह सुनिश्चित करता है कि समान समस्याओं वाले लोगों की मदद करें। जैसा कि मैंने आज किया था। :-P
पाउलो सैंटोस

4
गजब का! मामूली परिवर्तन सुझाव: (imo वास्तव में उपयोगी) 1) params string [] मान घोषित करने के लिए विशेषता घोषणा को और अधिक संक्षिप्त और (वरीयता 2) बनाने के लिए IsValidForRequest विधि निकाय को बदलेंreturn ValueNames.All(v => controllerContext.RequestContext.RouteData.Values.ContainsKey(v));
बेंजामिन पोडज़लर

2
मैं एक ही querystring पैरामीटर मुद्दा था। तुम बाहर उन मानकों की आवश्यकता के लिए माना जाता है, स्वैप की जरूरत है contains = ...: इस तरह कुछ करने के लिए खंडcontains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value) || controllerContext.RequestContext.HttpContext.Request.Params.AllKeys.Contains(value);
Patridge

3
उस पर चेतावनी का ध्यान दें: आवश्यक पैरामीटर बिल्कुल नाम के रूप में भेजा जाना चाहिए। यदि आपका एक्शन मेथड पैरामीटर एक जटिल प्रकार है जो इसके गुणों में नाम से गुजरता है (और MVC को उन्हें जटिल प्रकार में मालिश करने देता है), तो यह सिस्टम विफल हो जाता है, क्योंकि नाम querystring कुंजियों में नहीं है। उदाहरण के लिए, यह काम नहीं करेगा: ActionResult DoSomething(Person p)जहां Personविभिन्न सरल गुण हैं जैसे Name, और इसके लिए अनुरोध सीधे संपत्ति के नाम के साथ किए जाते हैं (जैसे, /dosomething/?name=joe+someone&other=properties)।
Patridge

4
यदि आप एमवीसी 4 का उपयोग कर रहे हैं, तो आपको controllerContext.HttpContext.Request[value] != nullइसके बजाय उपयोग करना चाहिए controllerContext.RequestContext.RouteData.Values.ContainsKey(value); लेकिन फिर भी काम का एक अच्छा टुकड़ा।
केविन फारुगिया

जवाबों:


180

एमवीसी केवल हस्ताक्षर के आधार पर ओवरलोडिंग की विधि का समर्थन नहीं करता है, इसलिए यह विफल हो जाएगा:

public ActionResult MyMethod(int someInt) { /* ... */ }
public ActionResult MyMethod(string someString) { /* ... */ }

हालाँकि, यह विशेषता के आधार पर ओवरलोडिंग की विधि का समर्थन करता है :

[RequireRequestValue("someInt")]
public ActionResult MyMethod(int someInt) { /* ... */ }

[RequireRequestValue("someString")]
public ActionResult MyMethod(string someString) { /* ... */ }

public class RequireRequestValueAttribute : ActionMethodSelectorAttribute {
    public RequireRequestValueAttribute(string valueName) {
        ValueName = valueName;
    }
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
        return (controllerContext.HttpContext.Request[ValueName] != null);
    }
    public string ValueName { get; private set; }
}

उपरोक्त उदाहरण में, विशेषता केवल यह कहती है "यदि अनुरोध में कुंजी xxx मौजूद था , तो यह विधि मेल खाती है ।" यदि आप बेहतर तरीके से अपने उद्देश्यों के अनुकूल हैं, तो आप मार्ग के भीतर निहित जानकारी (कंट्रोलर कोंटेक्स्ट.रैवेस्टकंटेक्स्ट) को भी फ़िल्टर कर सकते हैं।


यह वही समाप्त हो रहा है जो मुझे चाहिए था। जैसा कि आपने सुझाव दिया था, मुझे कंट्रोलर कॉन्टेक्स्ट.इसेक्स्ट कॉन्टेक्स्ट का उपयोग करने की आवश्यकता थी।
जोनाथन फ्रीलैंड

4
अच्छा! मैंने अभी तक RequRequestValue विशेषता नहीं देखी थी। यह जानना अच्छा है।
कोडरडेन

1
हम कई स्रोतों से मान प्राप्त करने के लिए valueprovider का उपयोग कर सकते हैं:
जोन पोलवोरा

मैं ...RouteData.Valuesइसके बजाय चला गया , लेकिन यह "काम करता है"। बहस के लिए एक अच्छा पैटर्न खुला है या नहीं। :)
बमबम

1
मुझे मेरा पिछला संपादन अस्वीकार कर दिया गया है इसलिए मैं सिर्फ टिप्पणी करने जा रहा हूं: [AttUUsage (AttributeTargets.All, AllowMultiple = true)]
Mzn

7

आपके मार्गों में पैरामीटर {roleId}, {applicationName}और {roleName}आपके कार्य विधियों में पैरामीटर नामों से मेल नहीं खाते। मुझे नहीं पता कि क्या मायने रखता है, लेकिन इससे यह पता लगाना मुश्किल हो जाता है कि आपका इरादा क्या है।

क्या आपके आइटमआईड के पैटर्न के अनुरूप है जिसे रेगेक्स के माध्यम से मिलान किया जा सकता है? यदि ऐसा है, तो आप अपने मार्ग पर एक संयम जोड़ सकते हैं ताकि केवल उस यूआरएल से मेल खाए जो पैटर्न से पहचाना जाता है जिसमें एक आइटम शामिल है।

यदि आपके आइटम में केवल अंक होते हैं, तो यह काम करेगा:

routes.MapRoute("AssignRemove",
                "Items/{action}/{itemId}",
                new { controller = "Items" },
                new { itemId = "\d+" }
                );

संपादित करें: आप भी करने के लिए एक बाधा जोड़ सकते हैं AssignRemovePrettyताकि दोनों मार्ग {parentName}और {itemName}आवश्यक हैं।

संपादित करें 2: इसके अलावा, चूंकि आपकी पहली क्रिया सिर्फ आपकी दूसरी क्रिया को पुनर्निर्देशित कर रही है, आप पहले वाले का नाम बदलकर कुछ अस्पष्टता को दूर कर सकते हैं।

// Method #1
public ActionResult AssignRemovePretty(string parentName, string itemName) { 
    // Logic to retrieve item's ID here...
    string itemId = ...;
    return RedirectToAction("Assign", itemId);
}

// Method #2
public ActionResult Assign(string itemId, string searchTerm, int? page) { ... }

फिर उचित तरीकों को बुलाए जाने के लिए बाध्य करने के लिए अपने मार्गों में क्रिया नाम निर्दिष्ट करें:

routes.MapRoute("AssignRemove",
                "Items/Assign/{itemId}",
                new { controller = "Items", action = "Assign" },
                new { itemId = "\d+" }
                );

routes.MapRoute("AssignRemovePretty",
                "Items/Assign/{parentName}/{itemName}",
                new { controller = "Items", action = "AssignRemovePretty" },
                new { parentName = "\w+", itemName = "\w+" }
                );

1
क्षमा करें डेनिस, पैरामीटर वास्तव में मेल खाते हैं। मैंने सवाल तय कर दिया है। मैं रेगेक्स संयम की कोशिश करूंगा और आपके पास वापस आऊंगा। धन्यवाद!
जोनाथन फ्रीलैंड

आपके दूसरे संपादन ने मेरी मदद की, लेकिन अंततः यह लेवी का सुझाव था जिसने इस सौदे को सील कर दिया। एक बार फिर धन्यवाद!
जोनाथन फ्रीलैंड

7

दूसरा तरीका यह है कि किसी एक पद्धति का नाम बदला जाए ताकि कोई टकराव न हो। उदाहरण के लिए

// GET: /Movies/Delete/5
public ActionResult Delete(int id = 0)

// POST: /Movies/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id = 0)

देखें http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part9-+


3

हाल ही में मैंने उन परिदृश्यों की एक विस्तृत श्रृंखला का समर्थन करने के लिए @ लेवी के उत्तर को बेहतर बनाने का मौका लिया, जिनसे मुझे निपटना था, जैसे: कई पैरामीटर समर्थन, उनमें से किसी से मेल खाते (सभी के बजाय) और यहां तक ​​कि उनमें से किसी से भी मेल नहीं खाते।

यहाँ मैं अब उपयोग कर रहा हूँ विशेषता है:

/// <summary>
/// Flags an Action Method valid for any incoming request only if all, any or none of the given HTTP parameter(s) are set,
/// enabling the use of multiple Action Methods with the same name (and different signatures) within the same MVC Controller.
/// </summary>
public class RequireParameterAttribute : ActionMethodSelectorAttribute
{
    public RequireParameterAttribute(string parameterName) : this(new[] { parameterName })
    {
    }

    public RequireParameterAttribute(params string[] parameterNames)
    {
        IncludeGET = true;
        IncludePOST = true;
        IncludeCookies = false;
        Mode = MatchMode.All;
    }

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        switch (Mode)
        {
            case MatchMode.All:
            default:
                return (
                    (IncludeGET && ParameterNames.All(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p)))
                    || (IncludePOST && ParameterNames.All(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p)))
                    || (IncludeCookies && ParameterNames.All(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p)))
                    );
            case MatchMode.Any:
                return (
                    (IncludeGET && ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p)))
                    || (IncludePOST && ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p)))
                    || (IncludeCookies && ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p)))
                    );
            case MatchMode.None:
                return (
                    (!IncludeGET || !ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p)))
                    && (!IncludePOST || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p)))
                    && (!IncludeCookies || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p)))
                    );
        }
    }

    public string[] ParameterNames { get; private set; }

    /// <summary>
    /// Set it to TRUE to include GET (QueryStirng) parameters, FALSE to exclude them:
    /// default is TRUE.
    /// </summary>
    public bool IncludeGET { get; set; }

    /// <summary>
    /// Set it to TRUE to include POST (Form) parameters, FALSE to exclude them:
    /// default is TRUE.
    /// </summary>
    public bool IncludePOST { get; set; }

    /// <summary>
    /// Set it to TRUE to include parameters from Cookies, FALSE to exclude them:
    /// default is FALSE.
    /// </summary>
    public bool IncludeCookies { get; set; }

    /// <summary>
    /// Use MatchMode.All to invalidate the method unless all the given parameters are set (default).
    /// Use MatchMode.Any to invalidate the method unless any of the given parameters is set.
    /// Use MatchMode.None to invalidate the method unless none of the given parameters is set.
    /// </summary>
    public MatchMode Mode { get; set; }

    public enum MatchMode : int
    {
        All,
        Any,
        None
    }
}

आगे की जानकारी के लिए और कैसे-कैसे कार्यान्वयन के नमूने इस ब्लॉग पोस्ट की जांच करते हैं जो मैंने इस विषय पर लिखा था।


धन्यवाद, महान सुधार! लेकिन ParameterNames ctor में सेट नहीं है
nvirth

0
routes.MapRoute("AssignRemove",
                "Items/{parentName}/{itemName}",
                new { controller = "Items", action = "Assign" }
                );

अपने मार्गों का परीक्षण करने के लिए MVC Contribs परीक्षण मार्गों के पुस्तकालय का उपयोग करने पर विचार करें

"Items/parentName/itemName".Route().ShouldMapTo<Items>(x => x.Assign("parentName", itemName));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.