Search code examples
asp.net-mvcactionlinkroutevalues

RouteValueDictionary/object problem in mvc ActionLink


so here it goes:

I have a html helper which renders ActionLinks with the optional parameters of the current url in it. this html helper also lets you add some more optional parameters as you please and merges them in 1 RouteValueDictionary.

    public static string ActionLinkwParams(this HtmlHelper helper, string linktext, string action, string controller, object extraRVs, object htmlAttributes) {

        //get current optional params from current URL
        NameValueCollection c = helper.ViewContext.RequestContext.HttpContext.Request.QueryString;

        //put those in a dict
        RouteValueDictionary r = new RouteValueDictionary();
        foreach (string s in c.AllKeys) {
            r.Add(s, c[s]);
        }

        RouteValueDictionary htmlAtts = new RouteValueDictionary(htmlAttributes);

        RouteValueDictionary extra = new RouteValueDictionary(extraRVs);

        //merge them
        RouteValueDictionary m = RouteValues.MergeRouteValues(r, extra);

        return helper.ActionLink(linktext, action, controller, m, htmlAtts).ToHtmlString();
    }

this works perfect, but I now added SecurityAware Actionlinks.

so

        return helper.ActionLink(linktext, action, controller, m, htmlAtts).ToHtmlString();

becomes

        return helper.SecurityTrimmedActionLink(linktext, action, controller, m, htmlAtts);

which then calls:

   public static string SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controller, object extraRVs, object htmlAttributes) {
        return SecurityTrimmedActionLink(htmlHelper, linkText, action, controller, extraRVs, htmlAttributes, false);
    }

    public static string SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controller, object extraRVs, object htmlAttributes, bool showDisabled) {
        if (controller == null) {
            RouteData routeData = htmlHelper.ViewContext.RouteData;
            controller = routeData.GetRequiredString("controller");
        }
        if (IsAccessibleToUser(action, controller)) {
            return htmlHelper.ActionLink(linkText, action, controller, extraRVs, htmlAttributes).ToHtmlString();
        } else {
            return showDisabled ? String.Format("<span>{0}</span>", linkText) : "";
        }
    }

Now this does NOT work. it compiles, but my URL looks not good.

   <a count="3" keys="System.Collections.Generic.Dictionary`2+KeyCollection[System.String,System.Object]" values="System.Collections.Generic.Dictionary`2+ValueCollection[System.String,System.Object]" href="/2011-2012/Instelling?Count=3&amp;Keys=System.Collections.Generic.Dictionary%602%2BKeyCollection%5BSystem.String%2CSystem.Object%5D&amp;Values=System.Collections.Generic.Dictionary%602%2BValueCollection%5BSystem.String%2CSystem.Object%5D">Back to List</a>

as you see, what previously did work, now doesnt because it takes the RouteValueDictionarys as objects, which gives me not the result I want.

so I thought, What if i make them RouteValueDictionarys again.

       if (IsAccessibleToUser(action, controller)) {

            RouteValueDictionary parsedextraRVs = null;
            if (extraRVs != null && extraRVs.GetType().Name == "RouteValueDictionary") {
                parsedextraRVs = (RouteValueDictionary)extraRVs;
            }

            RouteValueDictionary parsedHtmlAttributes = null;
            if (htmlAttributes != null && htmlAttributes.GetType().Name == "RouteValueDictionary") {
                parsedHtmlAttributes = (RouteValueDictionary)htmlAttributes;
            }


            return htmlHelper.ActionLink(linkText, action, controller, parsedextraRVs == null ? extraRVs : parsedextraRVs, parsedHtmlAttributes == null ? htmlAttributes : parsedHtmlAttributes).ToHtmlString();
        }

but this too gives me the url i just posted above. Why did this work in my ActionLinkwParams method, but not when the ActionLinkwParams calls the SecurityTrimmedActionLink method? and how do I fix this?


Solution

  • Modify the signature of the SecurityTrimmedActionLink method to this:

    public static string SecurityTrimmedActionLink(
        this HtmlHelper htmlHelper, 
        string linkText, 
        string action, 
        string controller, 
        RouteValueDictionary extraRVs, 
        RouteValueDictionary htmlAttributes
    )
    

    Notice the difference between this and this. In your case (the one that doesn't work) you are calling the second overload taking objects but in your case you are not passing anonymous objects but a RouteValueDictionary which is treated as if it was an anonymous object and its public properties (Count, Keys, Values) are serialized as attributes.

    Remark: Your helper methods are not correct. They return strings. This is not how it is supposed to be. Helper methods should return MvcHtmlString.

    So instead of

    public static string ActionLinkwParams(...)
    {
        ...
        return helper.ActionLink(linktext, action, controller, m, htmlAtts).ToHtmlString();
    }
    

    it should be:

    public static MvcHtmlString ActionLinkwParams(...)
    {
        ...
        return helper.ActionLink(linktext, action, controller, m, htmlAtts);
    }