2009-07-05 13 views
10

Ho una casella di login nella mia MasterPage. Ogni volta che le informazioni di accesso non sono corrette, valuto ViewData["loginError"] per mostrare il messaggio di errore all'utente.ASP.NET MVC: return Redirect e ViewData

Login è un'azione di UserController, quindi il modulo che contiene il login ha action = "/User/Login".

Come un utente può provare ad accedere da qualsiasi pagina, in caso di successo lo reindirizzamento alla sua pagina personale, ma in caso di errore voglio che rimanga sulla stessa pagina in cui ha tentato l'accesso. Ho trovato che questo funziona:

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

ma sembra che, come io non sto ritornando una vista corretta, i dati sul Viewdata è perso, quindi non posso mostrare il messaggio di errore.

Qualche suggerimento su come risolvere questo e problemi simili?

Grazie

risposta

14

Probabilmente si desidera utilizzare la proprietà TempData, questa verrà mantenuta fino alla successiva richiesta HTTP.

+1

+1 Proprio quello che stavo pensando. C'è anche una serie di pratici filtri azione in MvcContrib che copia ModelState da e verso TempState in modo da avere le informazioni sulla convalida disponibili dopo il reindirizzamento. Vedere l'articolo 13 in questo articolo per un esempio http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx –

+4

Ricordare che TempData utilizza la sessione stato però - con tutte le implicazioni. – UpTheCreek

2

Perché non gestire il login tramite la tecnologia AJAX invece un post pieno? È possibile fornire facilmente lo stato, un URL di reindirizzamento e qualsiasi messaggio di errore tramite 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"); 
      } 
     } 
    } 

lato client

$(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; 
     }); 
    }); 
+1

Ciao, So che posso gestirlo via ajax e questo è quello che sto per fare, ma mi piacerebbe sapere come risolvere il problema così come è possibile che ci siano volte quando si usa un POST semplice è più appropriato, o anche semplicemente richiesto da un cliente. – pistacchio

+0

Si potrebbe avere la vista che deve essere sottoposta a rendering dell'errore come parametro nascosto del modulo inviato. Sfortunatamente, probabilmente dovresti anche serializzare tutti i dati del modello contenuti in quella vista per passare insieme alla richiesta di accesso poiché altrimenti non c'è modo di mantenerlo tra le richieste. Farlo con AJAX sarà molto più pulito. Si noti che il codice sopra reindirizzerà alla pagina di accesso (non alla vista originale, comunque) se javascript non è disponibile. Ecco come gestirlo - rilasciare l'errore nella pagina di accesso e consentire all'utente di eseguire nuovamente l'autenticazione da lì. – tvanfosson

1

Normalmente per la maggior parte dei siti web, quando l'utente non riesce a eseguire l'autenticazione (a causa di password o giù di lì), si andrà ad un'altra pagina che aiutano l'utente con (come la password recuperare, o chiedere all'utente di registrarsi) che è raro rimanere nella stessa pagina. Penso che puoi ri-considerare che hai davvero bisogno della navigazione che stai usando.

OK, una soluzione se si vuole davvero attenersi al modello, è che è possibile collegare l'errore di accesso all'URL. Ad esempio, http://www.example.com/index.aspx?login_error=1 indica si verifica tale errore, ed è possibile utilizzare BEGIN_REQUEST (o modulo HTTP) per catturare questo, e dire lo stato modello sull'errore:

ModelState.AddModelError(...); 

BTW, aggiungere errore di modello è in realtà un modo più corretto per informare la vista su qualsiasi errore piuttosto che utilizzare ViewState (questo è simile all'eccezione di lancio e alla restituzione di un intero relativo al risultato dell'esecuzione nei vecchi giorni).

Durante l'utilizzo di AJAX per accedere (come suggerito da tvanfosson) è perfettamente realizzabile e talvolta eccellono nella user experience, classico full-post è ancora inreplacable (prendere in considerazione qualche utente disattiverà JavaScript, o anche sul mio discarica WM6 portatile che doesn 't supporto javascript).

+0

Mentre il mio esempio di codice non è completo, penso che la sua struttura gestirà il caso in cui javascript non è disponibile. In tal caso, il modulo di accesso farà un post completo e tutti gli errori verranno reindirizzati alla pagina di accesso come descritto. – tvanfosson

+0

Accetto di utilizzare entrambi i moduli AJAX + se non JS, e per i miei progetti vorrei utilizzare entrambi. Ma il problema del pistacchio persiste ancora. – xandy

1

Sono confuso. Non

return View(); 

restituire la pagina corrente a voi?

Quindi nel tuo caso, quando l'accesso fallisce, imposta il tuo viewdata e chiama return View();

cioè

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

Si sta utilizzando il decoratore [Autorizza]? Il processo di accesso MVC richiede automaticamente la pagina di accesso e quindi torna all'azione del controller che si stava tentando di eseguire. Taglia su un sacco di reindirizzamento.

+2

View() restituisce la vista predefinita per l'azione, che può essere o meno la vista corrente. Se si utilizzano viste con modelli, non includerà anche il modello utilizzato dalla vista corrente o altri parametri. – Suncat2000

1

L'esempio seguente si spera dare una mano a risolvere questo problema:

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; 
} 

Non sarà in grado di acquisire le informazioni aggiunte in ViewData dopo l'azione di reindirizzamento. quindi l'approccio corretto è restituire lo stesso View() e utilizzare ModelState per gli errori come menzionato da "xandy".

Spero che questo ti possa dare un vantaggio con la convalida del modulo.