Question

I have read many SO posts about returnUrl as it pertains to forms authentication by default of [authorize] MyController, but I haven't read anything on simply passing the returnUrl where the only authentication takes place after the [HttpPost] with a login or register form and anonymous users. In this case, I want the redirect to come from the original link and passed to the action where the users are authenticated via form authentication. This redirect should take the user back to the page being viewed after they 1) click register or login ActionLinks and then 2) submit the form successfully. This is on a dev server so HTTPS is not a requirement ATM.

Here is the elements without the necessary syntax/code for passing returnUrl

_LoginPartial:

<li>@Html.ActionLink("Register", "Register", "Account", routeValues: null}, htmlAttributes: new { id = "registerLink" })</li> //returnUrl???
<li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li> // returnUrl???

Login View:

@using (Html.BeginForm()){...} //new { returnUrl } ???

Login Get ActionResult:

[AllowAnonymous]
public ActionResult Login(string returnUrl)
{   
    //There is other ways to store the route
    TempData["ReturnUrl"] = returnUrl;
    return View();
}

Login Post ActionResult:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model)
{
    if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password,     persistCookie: model.RememberMe))
    {
        return RedirectToLocal(TempData["ReturnUrl"].ToString());
    }

    // If we got this far, something failed, redisplay form
    ModelState.AddModelError("", "The user name or password provided is incorrect.");
    return View(model);
}

Solution Thanks to SlightlyMoist I was able to resolve the issue. While the code is small in size the ability and know-how of ViewContext.RouteData.Values["key"] seems to be invaluable IMO. So the only modification done as per SM was done inside the ActionLinks of the _LoginPartial view:

<li>
@Html.ActionLink("Register", "Register", "Account", routeValues: new {@returnUrl = Url.Action(ViewContext.RouteData.Values["action"].ToString(), ViewContext.RouteData.Values["controller"].ToString(), ViewContext.RouteData.Values["id"])}, htmlAttributes: new { id = "registerLink" })
</li>

<li>
@Html.ActionLink("Log in", "Login", "Account", routeValues: new {@returnUrl = Url.Action(ViewContext.RouteData.Values["action"].ToString(), ViewContext.RouteData.Values["controller"].ToString(), ViewContext.RouteData.Values["id"])}, htmlAttributes: new { id = "loginLink" })
</li>

ALSO As per SM yet again, the ViewContext.HttpContext.Request.Url.PathAndQuery works as well:

<li>
@Html.ActionLink("Register", "Register", "Account", routeValues: new {@returnUrl = ViewContext.HttpContext.Request.Url.PathAndQuery},htmlAttributes: new { id = "registerLink" })
</li>
Was it helpful?

Solution

Simply parse the current route / URL as a routeValue in your login action link.

Something like this should do the trick

@Html.ActionLink("Login", "Login", "Account", 
    new {@returnUrl = Url.Action(ViewContext.RouteData.Values["action"].ToString(), ViewContext.RouteData.Values["controller"].ToString())})

OTHER TIPS

Similar idea, but I simply added ViewBag.ReturnUrl in the view, and modified the Register and Login methods. This is because not all Login and Register locations will want to return to the page, and this way you have control on a view by view basis.

@{
    ViewBag.Title = "My Page";
    ViewBag.ReturnUrl = "/Home/Live";
}

And in the _LoginPartial:

@Html.ActionLink( "Log in", "Login", new { area = "", controller = "Account", ReturnUrl=ViewBag.ReturnUrl }, new { id = "loginLink" } )
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top