2009-11-05 5 views
16

Ho implementato la gestione degli errori nel sito ASP.NET MVC in a way like suggests this post.ASP.NET MVC user-friendly 401 error

Con 404 errori tutto funziona correttamente. Ma come visualizzare correttamente la schermata user friendly per un errore ? Solitamente non generano eccezioni che possono essere gestite all'interno di Application_Error(), ma l'azione restituisce HttpUnauthorizedResult. Un modo possibile è aggiungere seguente codice alla fine del metodo Application_EndRequest()

if (Context.Response.StatusCode == 401) 
{ 
    throw new HttpException(401, "You are not authorised"); 
    // or UserFriendlyErrorRedirect(new HttpException(401, "You are not authorised")), witout exception 
} 

Ma dentro Application_EndRequest() Context.Session == null, errorController.Execute() riesce perché non è possibile utilizzare TempDataProvider predefinito.

// Call target Controller and pass the routeData. 
    IController errorController = new ErrorController(); 
    errorController.Execute(new RequestContext( 
     new HttpContextWrapper(Context), routeData)); // Additional information: The SessionStateTempDataProvider requires SessionState to be enabled. 

Quindi, puoi suggerire alcune best practice su come "gestire l'utente '401 nell'applicazione ASP.NET MVC?

Grazie.

+0

http://stackoverflow.com/questions/2580596/how-do-you-handle-ajax-requests -quando-utente-non-autenticato/2595436 # 2595436 – Cherian

risposta

14

Consultare HandleErrorAttribute. Sottoclassi da esso o aggiungi la tua implementazione che gestirà tutti i codici di stato che ti interessano. Puoi farlo per restituire una vista di errore separata per ogni tipo di errore.

Ecco un'idea di come creare il filtro dell'eccezione degli errori di handle. Ho buttato via la maggior parte delle cose per concentrarmi solo sui nostri elementi essenziali. Assolutamente dare un'occhiata all'implementazione originale per aggiungere assegni di argomenti e altre cose importanti.

public class HandleManyErrorsAttribute : FilterAttribute, IExceptionFilter 
{ 
    public virtual void OnException(ExceptionContext filterContext) 
    { 
     if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) 
      return; 

     Exception exception = filterContext.Exception; 

     string viewName = string.Empty; 
     object viewModel = null; 
     int httpCode = new HttpException(null, exception).GetHttpCode(); 
     if (httpCode == 500) 
     { 
      viewName = "Error500View"; 
      viewModel = new Error500Model(); 
     } 
     else if (httpCode == 404) 
     { 
      viewName = "Error404View"; 
      viewModel = new Error404Model(); 
     } 
     else if (httpCode == 401) 
     { 
      viewName = "Error401View"; 
      viewModel = new Error401Model(); 
     } 

     string controllerName = (string)filterContext.RouteData.Values["controller"]; 
     string actionName = (string)filterContext.RouteData.Values["action"]; 
     filterContext.Result = new ViewResult 
     { 
      ViewName = viewName, 
      MasterName = Master, 
      ViewData = viewModel, 
      TempData = filterContext.Controller.TempData 
     }; 
     filterContext.ExceptionHandled = true; 
     filterContext.HttpContext.Response.Clear(); 
     filterContext.HttpContext.Response.StatusCode = httpCode; 

     filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; 
    } 
} 

Poi si "decorare" le vostre azioni di controllo con questo attributo:

[HandleManyErrors] 
public ActionResult DoSomethingBuggy() 
{ 
    // ... 
} 
+0

Come ho capito, questa soluzione richiede di decorare con l'attributo HandleManyErrors ogni Controller o Azione potenzialmente sicura (azione che può portare a 401). Ma sarebbe bello avere una gestione globale 401 (in Global.asax per esempio), o non ho ragione e questa è una cattiva idea? – Roman

+0

Con il modello MVC, non si sa sempre in anticipo se una rotta è ok o meno. Una rotta può essere risolta per raggiungere qualche azione del controllore ma dopo aver controllato il parametro fornito con il database si può decidere che il percorso con questo parametro non può recuperare la risorsa necessaria in modo da restituire un errore 4xx. Con questo comportamento dinamico, è più semplice controllare le cose e lanciare una HttpException dalle azioni del controller sapendo che verranno gestite dall'attributo per restituire una pagina di errore. –

+0

Per il comportamento statico, è possibile aggiungere l'ultima rotta "catchall" in modo che corrisponda a tutti i percorsi non catturati da regole definite in precedenza. Il catchall invocherà alcune azioni del controller che genereranno semplicemente un'HttpException. In questo modo si ha una logica di gestione degli errori statici in Global.asax accompagnata dalla gestione dinamica degli errori direttamente nelle rispettive azioni del controllore. –

0

Se stai usando ASP.NET MVC, è molto più che probabile di utilizzare IIS, quindi perché don' t hai appena configurato IIS per utilizzare la pagina di errore 401 personalizzata per tale applicazione Web/directory virtuale?

+0

Ho bisogno di un comportamento diverso per le richieste Postback completo e AJAX e un maggiore controllo sull'elaborazione degli errori – Roman

+3

Perché si potrebbe volere un controllo più preciso su esattamente come viene presentato il 404 (HTML e HTTP). Potresti anche volere altre informazioni sulla tua pagina 404 come contenuti dinamici che funzioneranno solo all'interno della tua app. potresti voler registrare messaggi specifici e tracciare i percorsi 404 con il tuo motore di registrazione delle app, quindi tutto deve essere fatto all'interno dell'applicazione. –

+0

e Asp Net MVC 3 funziona anche su Apache2. (con Mono) – JCasso

6

Sono riuscito a risolvere questo in un modo molto semplice. Volevo mostrare una pagina personalizzata per gli utenti loggati ("non hai il permesso bla bla ...") e reindirizzare gli utenti non autenticati alla pagina di accesso (il comportamento predefinito). Così ho implementato un personalizzato AuthorizeAttribute (diciamo CustomAuthorizeAttribute) con il metodo HandleUnauthorizedRequest sovrascritto in modo che, se l'utente è autenticato ho impostato la proprietà Risultato dell'argomento filterContext con un ViewResult (nella cartella condivisa) chiamata AccessDenied.aspx

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
{ 
    if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
    { 
     base.HandleUnauthorizedRequest(filterContext); 
    } 
    else 
    { 
     filterContext.Result = new ViewResult { ViewName = "AccessDenied" }; 
    } 
} 

Quindi è necessario utilizzare questo nuovo attributo. Saluti.

+0

Per chiunque si chieda, questo attributo è menzionato solo su MSDNs Framework 4.0. Non è disponibile in 3.5 – Gregory

+1

Potrebbe non funzionare con l'autenticazione "Windows". Ho provato e ha fallito purtroppo .. –

-1

In uno dei miei progetti, utilizzo il codice da uvita.

ho ASP.NET MVC2 e io uso autenticazione di Active Directory senza pagina di login. Ho una pagina NoAuth.aspx che utilizza la pagina principale del sito, integrare il layout dell'applicazione Web.

Questo è il web.config.

<system.web> 
    <authentication mode="Windows" /> 
    <roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider"> 
     <providers> 
      <clear /> 
      <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" 
      applicationName="/" /> 
     </providers> 
    </roleManager> 
</system.web> 

La nuova classe CustomAutorizeAttribute

using System.Web.Mvc; 
public class CustomAuthorizeAttribute : AuthorizeAttribute 
{ 
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
     { 
      base.HandleUnauthorizedRequest(filterContext); 
     } 
     else 
     { 
      filterContext.Result = new ViewResult { ViewName = "NoAuth"}; 
     } 
    } 
} 

e il controller

[CustomAuthorize(Roles = "ADRole")] 
public class HomeController : Controller 
{ 
    public HomeController() 
    { 
    } 
}