views:

2277

answers:

4

Using Forms Authentication in ASP.NET MVC when trying to log back into a site, it puts a ReturnUrl parameter in the query string. My Logon action method accepts a "returnUrl" string. However it seems that returnUrl string is always null, even when it is clearly in the query string. Any thoughts on why this might be the case or a possible fix?

+5  A: 

Maybe you don't include the ReturnURL parameter into you login form's action attribute, thus posting to a URL without that parameter?

çağdaş
Not sure I follow. Are you saying you think I am not including the ReturnURL param in the action on the controller?
aherrick
No I meant the HTML form tag's action attribute. You may have ReturnURL querystring on the login page. But if the login form posts it without that querystring parameter, then it's lost.
çağdaş
Hmm... No where in the code am explicitly setting the ReturnURL on any form post. It is automatically generated in the Query string by the framework.
aherrick
Set a breakpoint inside the action method in question, then examine the Request.RawUrl property. There is probably no ?ReturnUrl=... query string segment, indicating that your form is posting to <form action="/Account/Login" /> rather than <form action="/Account/Login?ReturnUrl=..." />.
Levi
Not sure why ?ReturnUrl= has to explicitly be in the Form action attribute. Isn't the MVC demo project using Html.Beginform() to post back automatically post to Account/Logon ?
aherrick
Yes, Html.BeginForm() will post back to the current URL, but you need to make sure that (a) you are actually calling BeginForm() instead of a different overload, (b) the original URL contains the ?ReturnUrl=... parameter, (c) something isn't misspelled, etc. But psychic debugging is difficult, and it would be much more useful to set a breakpoint in the action method, inspect the URL-related properties on the Request object, and work backward to try to figure out what's wrong.
Levi
+1 just stumbled across this problem and this answer helped me fix it
AdamRalph
What should the final code look like to get this working?
Jon
@Jon, Like this : <form action="your-url?ReturnUrl=<%=Request.QueryString["ReturnUrl"]%>" method="post">
çağdaş
A: 

Try the following:

        public static MvcForm BeginForm(this HtmlHelper htmlHelper, string id)
    {
        string formAction = htmlHelper.ViewContext.HttpContext.Request.RawUrl;

        TagBuilder tagBuilder = new TagBuilder("form");

        tagBuilder.MergeAttribute("id", id);
        tagBuilder.MergeAttribute("action", formAction);
        tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(FormMethod.Post), true);

        HttpResponseBase httpResponse = htmlHelper.ViewContext.HttpContext.Response;
        httpResponse.Write(tagBuilder.ToString(TagRenderMode.StartTag));

        return new MvcForm(htmlHelper.ViewContext.HttpContext.Response);
    }

First ensure you have set the login url in the web.config, Next, ensure your Signin Form does not contain anything like action, for example:

View:

<%using (Html.BeginForm()){%> <%}%>

If you specify action you will always get null for return url:

Controller:

[AcceptVerbs(HttpVerbs.Post)] public ActionResult SignIn(string userName, string password, bool? rememberMe, string returnUrl) { }

kazimanzurrashid
Ok so how do I set set the Id of the form without specifying an Action? This is what my Html.BeginForm looks like.<%using (Html.BeginForm("Logon", "Account", null, FormMethod.Post, new { id = "logonForm" })) {%>
aherrick
+6  A: 

This tends to happen when you're using one generic logon form, but you're explicitly specifying the Controller and ActionMethod (which is causing a form post, but losing the querystring)

Just to clarify, this is what your code should look like in your BeginForm:

Html.BeginForm("LogOn", "Account", new {ReturnUrl = Request.QueryString["ReturnUrl"] })
davewasthere
Thanks for the snippet dave.
Rake36
A: 

There are two ways I can think of to deal with logon and logoff scenarios. Dave Beer outlined one way, above. There is another approach that works in many situations. I used it when I coded the NerdDinner tutorial. The tutorial provides us with a logoff function that logs you off and takes you home. I did not want that. I wanted to return to the page I was on before I logged off. So I modified my Account controller logoff action to look like this

   public ActionResult LogOff()
    {
        FormsService.SignOut();
        return Redirect(Request.UrlReferrer.ToString());
    }

You can get fancier and pass in a returnUrl and test for it, in case you want to override this behavior. But I don't need that. This achieves the desired result. The Logon can work similarly. Maybe there are ways to use the MVC framework to do this for me, but until I learn them, this is VERY simple and works reliably.

David Allen