401 codice di risposta per richieste json con ASP.NET MVC

Come disabilitare la gestione ASP.NET standard del codice di risposta 401 (reindirizzamento alla pagina di accesso) per le richieste AJAX / JSON?

Per le pagine Web va bene, ma per AJAX ho bisogno di ottenere il codice di errore 401 corretto invece del 302/200 bello per la pagina di accesso.

Aggiornamento : Ci sono diverse soluzioni di Phil Haack, PM di ASP.NET MVC – http://haacked.com/archive/2011/10/04/prevent-forms-authentication-login-page-redirect-when-you-donrsquot -want.aspx

Il runtime di ASP.NET è sviluppato in modo tale da redirect sempre l’utente se HttpResponse.StatusCode è impostato su 401, ma solo se viene trovata la sezione di Web.config.

Rimozione della sezione di autenticazione richiederà di implementare il reindirizzamento alla pagina di accesso nel tuo attributo, ma questo non dovrebbe essere un grosso problema.

Nel classico ASP.NET si ottiene un codice di risposta http 401 quando si chiama un WebMethod con Ajax. Spero che lo cambieranno nelle versioni future di ASP.NET MVC. In questo momento sto usando questo trucco:

 protected void Application_EndRequest() { if (Context.Response.StatusCode == 302 && Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest") { Context.Response.Clear(); Context.Response.StatusCode = 401; } } 

Volevo sia l’autenticazione Forms che restituire un 401 per le richieste Ajax che non erano autenticate.

Alla fine, ho creato un AuthorizeAttribute personalizzato e decorato i metodi del controller. (Si tratta di .Net 4.5)

//web.config

   

// regolatore

 [Authorize(Roles = "Administrator,User"), Response302to401] [AcceptVerbs("Get")] public async Task GetDocuments() { string requestUri = User.Identity.Name.ToLower() + "/document"; RequestKeyHttpClient, string> client = new RequestKeyHttpClient, string>(requestUri); var documents = await client.GetManyAsync>(); return Json(documents, JsonRequestBehavior.AllowGet); } 

// AuthorizeAttribute

 public class Response302to401 : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { if (filterContext.HttpContext.Request.IsAjaxRequest()) { filterContext.Result = new JsonResult { Data = new { Message = "Your session has died a terrible and gruesome death" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet }; filterContext.HttpContext.Response.StatusCode = 401; filterContext.HttpContext.Response.StatusDescription = "Humans and robots must authenticate"; filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true; } } //base.HandleUnauthorizedRequest(filterContext); } } 

È anche ansible utilizzare Global.asax per interrompere questo processo con qualcosa del genere:

  protected void Application_PreSendRequestHeaders(object sender, EventArgs e) { if (Response.StatusCode == 401) { Response.Clear(); Response.Redirect(Response.ApplyAppPathModifier("~/Login.aspx")); return; } } 

Non vedo cosa dobbiamo modificare la modalità di autenticazione o il tag di autenticazione come dice la risposta corrente.

Seguendo l’idea di @TimothyLeeRussell (grazie tra l’altro), ho creato un attributo Authorize personalizzato (il problema con quello di @TimothyLeeRussell è che un’eccezione è lanciata perché prova a cambiare il filtroContext.Result che genera una HttpException, e rimuovendo quella parte, oltre al filterContext.HttpContext.Response.StatusCode = 401, il codice di risposta era sempre 200 OK). Quindi alla fine ho risolto il problema terminando la risposta dopo le modifiche.

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class BetterAuthorize : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (filterContext.HttpContext.Request.IsAjaxRequest()) { //Set the response status code to 500 filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; filterContext.HttpContext.Response.StatusDescription = "Humans and robots must authenticate"; filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true; filterContext.HttpContext.Response.End(); } else base.HandleUnauthorizedRequest(filterContext); } } 

È ansible scegliere di creare un FilterAttribute personalizzato che implementa l’interfaccia IAuthorizationFilter .

In questo attributo si aggiunge la logica per determinare se la richiesta deve restituire JSON. In tal caso, puoi restituire un risultato JSON vuoto (o fare quello che vuoi) dato che l’utente non ha effettuato l’accesso. Per altre risposte, dovrai semplicemente redirect l’utente come sempre.

Ancora meglio, potresti semplicemente scavalcare l’ OnAuthorization della class AuthorizeAttribute modo da non dover reinventare la ruota. Aggiungi la logica che ho menzionato sopra e intercetti se filterContext.Cancel è true (il filterContext.Result sarà impostato su un’istanza della class HttpUnauthorizedResult .

Maggiori informazioni su “Filtri in ASP.NET MVC CodePlex Preview 4” sul blog di Phil Haacks. Si applica anche all’anteprima più recente.

Puoi chiamare questo metodo all’interno della tua azione,

  HttpContext.Response.End(); 

Esempio

 public async Task Return401() { HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; HttpContext.Response.End(); return Json("Unauthorized", JsonRequestBehavior.AllowGet); } 

Da MSDN : il metodo End fa in modo che il server Web interrompa l’elaborazione dello script e restituisca il risultato corrente. Il contenuto rimanente del file non viene elaborato.