Pass returnUrl from ActionLink from view to Login Form that redirects back to view

yardpenalty.com picture yardpenalty.com · Apr 2, 2014 · Viewed 20k times · Source

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>

Answer

SlightlyMoist picture SlightlyMoist · Apr 2, 2014

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