Stefanvds
Stefanvds

Reputation: 5916

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?

Upvotes: 4

Views: 7801

Answers (2)

Stefanvds
Stefanvds

Reputation: 5916

So what i did in the end (thanks Darin) is made some extra overloads to make this work.

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action) {
        return SecurityTrimmedActionLink(htmlHelper, linkText, action, null, null);
    }

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controller) {
        return SecurityTrimmedActionLink(htmlHelper, linkText, action, controller, null);
    }

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, object extraRVs) {
        return SecurityTrimmedActionLink(htmlHelper, linkText, action, null, extraRVs, null);
    }

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

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, RouteValueDictionary extraRVs) {
        return SecurityTrimmedActionLink(htmlHelper, linkText, action, null, extraRVs, null);
    }

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controller, RouteValueDictionary extraRVs) {
        return SecurityTrimmedActionLink(htmlHelper, linkText, action, controller, extraRVs, null);
    }

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controller, object extraRVs, object htmlAttributes) {

        RouteValueDictionary rv = new RouteValueDictionary(extraRVs);
        RouteValueDictionary html = new RouteValueDictionary(htmlAttributes);

        return SecurityTrimmedActionLink(htmlHelper, linkText, action, controller, extraRVs, html);
    }

    public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkText, string action, string controller, RouteValueDictionary extraRVs, IDictionary<String, Object> htmlAttributes) {
        if (controller == null) {
            RouteData routeData = htmlHelper.ViewContext.RouteData;
            controller = routeData.GetRequiredString("controller");
        }
        if (IsAccessibleToUser(action, controller)) {
            return htmlHelper.ActionLink(linkText, action, controller, extraRVs, htmlAttributes);
        } else {
            return MvcHtmlString.Empty;
        }
    }

Upvotes: 0

Darin Dimitrov
Darin Dimitrov

Reputation: 1038720

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);
}

Upvotes: 7

Related Questions