简体   繁体   中英

ASP.NET MVC: return Redirect and ViewData

I have a login box in my MasterPage. Whenever the login information is not correct, I valorize ViewData["loginError"] to show the error message to the user.

Login is an action of the UserController, so the form that contains the login has action = "/User/Login" .

As a user can try to log in from any page, in case of success I redirect him to his personal page, but in case of error I want him to stay on the very same page where he tried to login. I've found that this works:

return Redirect(Request.UrlReferrer.ToString());

but it seems that, as I'm not returning a proper view, the data on ViewData is lost, so I cannot show the error message.

Any suggestion on how to solve this and similar problems?

Thanks

您可能希望使用TempData属性,这将持久保存到下一个HTTP请求。

Why not handle the login via AJAX instead a full post? You could easily supply the status, a redirect URL, and any error messages via JSON.

public ActionResult Logon( string username, string password )
{
     ...

     // Handle master page login
     if (Request.IsAjaxRequest())
     {
          if (success)
          {
              return Json( new { Status = true, Url = Url.Action( "Index", "Home" ) } );
          }
          else
          {
              return Json( new { Status = false, Message = ... } );
          }
     }
     else // handle login page logon or no javascript
     {
          if (success)
          {
              return RedirectToAction( "Index", "Home" );
          }
          else
          {
              ViewData["error"] = ...
              return View("Logon");
          }
      }
  }

Client-side

  $(function() {
      $('#loginForm input[type=submit]').click( function() {
          $('#loginError').html('');
          $.ajax({
             url: '<%= Url.Action("Logon","Account") %>',
             dataType: 'json',
             type: 'post',
             data: function() { return $('#loginForm').serialize(); },
             success: function(data,status) {
                 if (data.Status) {
                     location.href = data.Url;
                 }
                 else {
                     $('#loginError').html( data.Message );
                 }
             }
          });
          return false;
      });
  });

Normally for most web sites, when user fail to authenticate (due to password or so), it will go to another page which help the user with (like password retrieve, or ask the user to sign up) which is rare to stay in the very same page. I think you can re-consider that you really need the navigation that you are using.

OK, one solution if you really want to stick to your model, is that you can attach the login error to the URL. For example, http://www.example.com/index.aspx?login_error=1 indicates that error occurs, and you can use BEGIN_REQUEST (or HTTP Module) to capture this, and tell the model state about the error:

ModelState.AddModelError(...);

BTW, add model error is actually a more proper way to inform the view about any error rather than using ViewState (this is similar to throwing exception vs returning an integer about the execution result in old days).

While using AJAX to login (as suggested by tvanfosson) is perfectly achievable and it sometimes excel in user experience, classic full-post is still inreplacable (consider some user will disable javascript, or even on my dump WM6 handset that doesn't support javascript).

I'm confused. Doesn't

return View();

just return the current page back to you?

So in your case, when login fails, set your viewdata and call return View();

ie

if (!FailedLogin) {
  //Go to success page
}else{
  //Add error to View Data or use ModelState to add error
  return View();
}

Are you using the [Authorize] decorator? The MVC login process auto prompts with the login page and then returns you to the controller action you were trying to execute. Cuts down on a lot of redirecting.

The following example would hopefully help you out in resolving this issue:

View.aspx

<%= Html.ValidationSummary("Login was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) { %>
    <div>
        <fieldset>
            <legend>Account Information</legend>
            <p>
                <label for="username">Username:</label>
                <%= Html.TextBox("username") %>
                <%= Html.ValidationMessage("username") %>
            </p>
            <p>
                <label for="password">Password:</label>
                <%= Html.Password("password") %>
                <%= Html.ValidationMessage("password") %>
            </p>
            <p>
                <%= Html.CheckBox("rememberMe") %> <label class="inline" for="rememberMe">Remember me?</label>
            </p>
            <p>
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
<% } %>

AccountController.cs

private bool ValidateLogOn(string userName, string password)
{
    if (String.IsNullOrEmpty(userName))
    {
        ModelState.AddModelError("username", "You must specify a username.");
    }
    if (String.IsNullOrEmpty(password))
    {
        ModelState.AddModelError("password", "You must specify a password.");
    }
    if (!MembershipService.ValidateUser(userName, password))
    {
        ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
    }
    return ModelState.IsValid;
}

You won't be able capture the information added in ViewData after Redirect action. so the right approach is to return the same View() and use ModelState for errors as mentioned by "xandy" as well.

Hope this would give ua head start with form validation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM