8

I've noticed the returnurl URL parameter on the Stackoverflow login/logout links are not escaped but when I try to add path as a parameter to a route it gets escaped.

So /login?returnurl=/questions/ask shows /login?returnurl=%2fquestions%2fask and it's kind of ugly. How do I get it to not escape the returnurl value?

Here's what I'm doing in the code:

Html.ActionLink("Login", "Login", "Account", new { returnurl=Request.Path }, null)
1
  • Would love to know the answer to this too Commented Feb 5, 2009 at 3:41

5 Answers 5

8

How do I get it to not escape the returnurl value

How's about this?

var url = Url.Action("Login", "Account", new {returnurl = Request.Path});
var unEncodedUrl = HttpUtility.UrlDecode(url);
Response.Write("<a href='" + unEncodedUrl + "'>...</a>");

Be sure that's what you want though, URL encoding has its purpose.

Sign up to request clarification or add additional context in comments.

2 Comments

Quick solution. May have to use @Html.Raw(unEncodedUrl) if you are creating any JS content
This has been the only answer that I have seen so far that is appropriate!
1

I understand one of the comments about encoding happening for a reason; this would only be an exception, not the rule.

Here's what I put together, how can it be improved?

    public static string ActionLinkNoEscape(this HtmlHelper html, string linkText, string actionName, string controllerName, object values, object htmlAttributes)
    {
        RouteValueDictionary routeValues = new RouteValueDictionary(values);
        RouteValueDictionary htmlValues = new RouteValueDictionary(htmlAttributes);

        UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext, RouteTable.Routes);
        string url = urlHelper.Action(actionName, controllerName);
        url += "?";
        List<string> paramList = new List<string>();
        foreach (KeyValuePair<string, object> pair in routeValues)
        {
            object value = pair.Value ?? "";
            paramList.Add(String.Concat(pair.Key, "=", Convert.ToString(value, CultureInfo.InvariantCulture)));
        }
        url += String.Join("&", paramList.ToArray());

        TagBuilder builder = new TagBuilder("a");
        builder.InnerHtml = string.IsNullOrEmpty(linkText) ? "" : HttpUtility.HtmlEncode(linkText);
        builder.MergeAttributes<string, object>(htmlValues);
        builder.MergeAttribute("href", url);
        return builder.ToString(TagRenderMode.Normal);
    }

1 Comment

Works a little better if you have it return an MvcHtmlString.
1

The parameter is not unescaped. You'll notice the URL:

http://stackoverflow.com/users/login?returnurl=%2fquestions%2fask

does actually work - SO is reading and unescaping that parameter as normal. If you wanted to include other out-of-bounds characters such as '&' in the parameter you would still have to escape them.

The trick is merely that the '/' character in particular does not need to be %-escaped in query parameters. It does have to be escaped in other contexts such as in a path part, so URLEncode always encodes it, to be safe.

If you just want the URL to look prettier, simply escape the parameter as normal (which you must do to escape all the other characters that must be handled correctly), and then do a string replace on '%2f' with '/'.

3 Comments

I know the URL will work but I don't like the look, apparently Jeff and crew at SO felt the same way. This solution is too much of a hack.
It's guaranteed to remain a valid URL if you only unescape %2F in parameters. Your code above will fall over on any other character that isn't valid in parameters, eg. &, ; or %.
Like I said, it's an exception and not the rule.
0

My solutionto a similar problem was to write my own extension. After digging around in the code I couldn't find a way to do it otherwise. Yours might look like this.

public static class HtmlHelperExtensions
{
    public static string LoginLinkWithReturnUrl( this HtmlHelper helper,
                                                 string linkText,
                                                 string action,
                                                 string controller,
                                                 string returnUrl,
                                                 object htmlAttributes )
    {
         TagBuilder builder = new TagBuilder("a");
         builder.Attributes.Add( "href",
                                  string.Format( "/{0}/{1}?returnurl={2}",
                                                 controller,
                                                 action,
                                                 returnUrl ) );
         var attrDict = new RouteValueDictionary( htmlAttributes );
         builder.MergeAttributes( attrDict );
         builder.InnerHtml = linkText;
         return builder.ToString();
    }
}

I think I had the same problem making and using a UrlHelper so I went with the string.Format mechanism instead. YMMV.

2 Comments

The only problem with this is that you are hard-coding the route, which means this will break if you aren't using the default route.
It was just an example off the top of my head. Probably you could use the UrlHelper to build the route from the action/controller and then append the query parameters to the end of it.
0

I don't believe there's a way around it that's built into the framework. The actual construction of the URL happens in the System.Web.Routing.ParsedRoute.Bind method and there aren't any conditions used to prevent the escaping.

Looks like an extension method is the way to go but one that is slightly more robust than the one mentioned previously.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.