Pregunta Cómo administrar una solicitud de redireccionamiento después de una llamada jQuery Ajax


Estoy usando $.post() llamar a un servlet usando Ajax y luego usar el fragmento HTML resultante para reemplazar un div elemento en la página actual del usuario. Sin embargo, si la sesión expira, el servidor envía una directiva de redirección para enviar al usuario a la página de inicio de sesión. En este caso, jQuery está reemplazando el div elemento con los contenidos de la página de inicio de sesión, obligando a los ojos del usuario a ser testigos de una escena rara de hecho.

¿Cómo puedo gestionar una directiva de redirección de una llamada Ajax con jQuery 1.2.6?


1181
2017-10-13 21:29


origen


Respuestas:


Leí esta pregunta e implementé el enfoque que se ha establecido con respecto a establecer el código de estado de respuesta en 278 para evitar que el navegador maneje de manera transparente los redireccionamientos. Aunque esto funcionó, estaba un poco insatisfecho ya que es un poco hack.

Después de cavar más, dejé este enfoque y utilicé JSON. En este caso, todas las respuestas a las solicitudes ajax tienen el código de estado 200 y el cuerpo de la respuesta contiene un objeto JSON que está construido en el servidor. El javascript en el cliente puede usar el objeto JSON para decidir qué debe hacer.

Tuve un problema similar al tuyo. Realizo una solicitud de Ajax que tiene 2 respuestas posibles: una que redirige el navegador a una página nueva y otra que reemplaza un formulario HTML existente en la página actual por uno nuevo. El código jquery para hacer esto se ve algo así como:

$.ajax({
    type: "POST",
    url: reqUrl,
    data: reqBody,
    dataType: "json",
    success: function(data, textStatus) {
        if (data.redirect) {
            // data.redirect contains the string URL to redirect to
            window.location.href = data.redirect;
        }
        else {
            // data.form contains the HTML for the replacement form
            $("#myform").replaceWith(data.form);
        }
    }
});

El objeto JSON "datos" está construido en el servidor para tener 2 miembros: data.redirect y data.form. Encontré este enfoque mucho mejor.


631
2017-10-07 22:54



Resolví este problema por:

  1. Agregar un encabezado personalizado a la respuesta:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  2. Vinculando una función de JavaScript al ajaxSuccess evento y verificar si existe el encabezado:

    $(document).ajaxSuccess(function(event, request, settings) {
        if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
           window.location = '/';
        }
    });
    

213
2017-11-20 08:39



Ningún navegador maneja 301 y 302 respuestas correctamente. Y, de hecho, el estándar incluso dice que deben manejarlos "de forma transparente", lo cual es un dolor de cabeza MASIVO para los vendedores de la Biblioteca Ajax. En Ra-Ajax nos vimos obligados a usar el código de estado de respuesta HTTP 278 (solo un código de éxito "sin usar") para manejar redirecciones transparentes del servidor ...

Esto realmente me molesta, y si alguien aquí tiene algo de "atracción" en el W3C, agradecería que pudieras dejar que el W3C saber que realmente necesitamos manejar los códigos 301 y 302 nosotros mismos ...! ;)


106
2018-01-27 18:10



La solución que finalmente se implementó fue usar un contenedor para la función de devolución de llamada de Ajax y en este conteo verificar la existencia de un elemento específico en el fragmento HTML devuelto. Si se encontró el elemento, el contenedor ejecutó una redirección. De lo contrario, el contenedor reenvió la llamada a la función de devolución de llamada real.

Por ejemplo, nuestra función de envoltura era algo así como:

function cbWrapper(data, funct){
    if($("#myForm", data).length > 0)
        top.location.href="login.htm";//redirection
    else
        funct(data);
}

Luego, al hacer la llamada Ajax, usamos algo como:

$.post("myAjaxHandler", 
       {
        param1: foo,
        param2: bar
       },
       function(data){
           cbWrapper(data, myActualCB);
       }, 
       "html"
);

Esto funcionó para nosotros porque todas las llamadas Ajax siempre devolvieron HTML dentro de un elemento DIV que usamos para reemplazar una parte de la página. Además, solo necesitábamos redireccionar a la página de inicio de sesión.


85
2017-08-23 19:21



Me gusta el método de Timmerz con un ligero toque de limón. Si alguna vez te devuelven tipo de contenido de texto / html cuando esperas JSON, lo más probable es que te redirijan. En mi caso, simplemente recargo la página y se redirecciona a la página de inicio de sesión. Ah, y comprueba que el estado jqXHR es 200, lo que parece una tontería, porque estás en la función de error, ¿no? De lo contrario, los casos de error legítimos obligarán a una recarga iterativa (oops)

$.ajax(
   error:  function (jqXHR, timeout, message) {
    var contentType = jqXHR.getResponseHeader("Content-Type");
    if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
        // assume that our login has expired - reload our current page
        window.location.reload();
    }

});

57
2017-10-13 21:54



Use el bajo nivel $.ajax() llamada:

$.ajax({
  url: "/yourservlet",
  data: { },
  complete: function(xmlHttp) {
    // xmlHttp is a XMLHttpRquest object
    alert(xmlHttp.status);
  }
});

Pruebe esto para una redirección:

if (xmlHttp.code != 200) {
  top.location.href = '/some/other/page';
}

44
2018-05-23 10:02



Solo quería compartir mi enfoque ya que esto podría ayudar a alguien:

Básicamente incluí un módulo de JavaScript que maneja la autenticación, como mostrar el nombre de usuario y también este caso manejando el redirigir a la página de inicio de sesión.

Mi escenario: básicamente tenemos un servidor ISA entre el cual escucha todas las solicitudes y responde con un 302 y un encabezado de ubicación a nuestra página de inicio de sesión.

En mi módulo de JavaScript mi acercamiento inicial era algo así como

$(document).ajaxComplete(function(e, xhr, settings){
    if(xhr.status === 302){
        //check for location header and redirect...
    }
});

El problema (como muchos ya se mencionó) es que el navegador maneja la redirección por sí mismo, por lo que mi ajaxComplete devolución de llamada nunca se llamó, pero en su lugar obtuve el respuesta de la página de inicio de sesión ya redirigida que obviamente era un status 200. El problema: ¿cómo se detecta si la respuesta exitosa 200 es su página de inicio de sesión real o simplemente alguna otra página arbitraria?

La solución

Como no pude capturar 302 respuestas de redireccionamiento, agregué un LoginPage encabezado en mi página de inicio de sesión que contenía la url de la página de inicio de sesión. En el módulo, ahora escucho el encabezado y hago un redireccionamiento:

if(xhr.status === 200){
    var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
    if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
        window.location.replace(loginPageRedirectHeader);
    }
}

... y eso funciona como encanto :). Puede que se pregunte por qué incluyo la url en el LoginPage encabezado ... básicamente porque no encontré la forma de determinar la URL de GET resultante de la redirección de ubicación automática de la xhr objeto...


31
2018-05-06 23:55



Sé que este tema es antiguo, pero daré otro enfoque que he encontrado y descrito anteriormente aquí. Básicamente estoy usando ASP.MVC con WIF (pero esto no es realmente importante para el contexto de este tema: la respuesta es adecuada, sin importar qué marcos se usen. La pista no cambia: se trata de problemas relacionados con fallas de autenticación al realizar solicitudes de Ajax).

El enfoque que se muestra a continuación se puede aplicar a todas las solicitudes ajax listas para usar (si no se redefine antes del evento Send, obviamente).

$.ajaxSetup({
    beforeSend: checkPulse,
    error: function (XMLHttpRequest, textStatus, errorThrown) {
        document.open();
        document.write(XMLHttpRequest.responseText);
        document.close();
    }
});

Antes de realizar cualquier solicitud de Ajax CheckPulse se invoca el método (el método del controlador que puede ser cualquier cosa más simple):

[Authorize]
public virtual void CheckPulse() {}

Si el usuario no está autenticado (el token ha expirado) no se puede acceder a dicho método (protegido por Authorize atributo). Debido a que el marco maneja la autenticación, mientras token expira, pone el estado http 302 a la respuesta. Si no desea que su navegador maneje la respuesta 302 de forma transparente, recójalo en Global.asax y cambie el estado de la respuesta; por ejemplo, 200 OK. Además, agregue el encabezado, que le indica que procese dicha respuesta de manera especial (más adelante en el lado del cliente):

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302
        && (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
    {                
        Context.Response.StatusCode = 200;
        Context.Response.AddHeader("REQUIRES_AUTH", "1");
    }
}

Finalmente, en el lado del cliente, compruebe si hay un encabezado personalizado. Si está presente, se debe realizar una redirección completa a la página de inicio de sesión (en mi caso window.location es reemplazado por la url de la solicitud que mi marco de trabajo maneja automáticamente).

function checkPulse(XMLHttpRequest) {
    var location = window.location.href;
    $.ajax({
        url: "/Controller/CheckPulse",
        type: 'GET',
        async: false,
        beforeSend: null,
        success:
            function (result, textStatus, xhr) {
                if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
                    XMLHttpRequest.abort(); // terminate further ajax execution
                    window.location = location;
                }
            }
    });
}

26
2017-10-23 16:27



Creo que una mejor manera de manejar esto es aprovechar los códigos de respuesta del protocolo HTTP existentes, específicamente 401 Unauthorized.

Así es como lo resolví:

  1. Lado del servidor: si la sesión expira, y la solicitud es ajax. enviar un encabezado de código de respuesta 401
  2. Lado del cliente: Enlace a los eventos de Ajax

    $('body').bind('ajaxSuccess',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    }).bind('ajaxError',function(event,request,settings){
    if (401 == request.status){
        window.location = '/users/login';
    }
    });
    

IMO esto es más genérico y no está escribiendo una nueva especificación / encabezado personalizado. Tampoco debería tener que modificar ninguna de sus llamadas ajax existentes.

Editar: El siguiente comentario de Per @ Rob, 401 (el código de estado HTTP para los errores de autenticación) debería ser el indicador. Ver 403 Forbidden vs 401 respuestas HTTP no autorizadas para más detalles. Dicho esto, algunos frameworks web usan 403 tanto para la autenticación como para los errores de autorización, por lo que deben adaptarse en consecuencia. Gracias Rob.


21
2018-05-01 21:42



Otra solución que encontré (especialmente útil si quieres establecer un comportamiento global) es usar $.ajaxsetup() método junto con el statusCode propiedad. Como otros señalaron, no use un código de estado de redireccionamiento (3xx), en su lugar use un 4xx código de estado y manejar la redirección del lado del cliente.

$.ajaxSetup({ 
  statusCode : {
    400 : function () {
      window.location = "/";
    }
  }
});

Reemplazar 400 con el código de estado que desea manejar. Como ya mencioné 401 Unauthorized podría ser una buena idea Yo uso el 400 ya que es muy inespecífico y puedo usar el 401 para casos más específicos (como credenciales de inicio de sesión incorrectas). Entonces, en lugar de redireccionar directamente, su backend debería devolver un 4xx código de error cuando se agotó el tiempo de espera de la sesión y usted maneja la redirección del lado del cliente. Funciona perfecto para mí incluso con frameworks como backbone.js


17
2017-10-27 13:31



Este problema puede aparecer luego utilizando el método ASP.NET MVC RedirectToAction. Para evitar que el formulario muestre la respuesta en div, simplemente puede hacer algún tipo de filtro de respuesta ajax para respuestas entrantes con $ .ajaxSetup. Si la respuesta contiene la redirección MVC, puede evaluar esta expresión en el lado JS. Código de ejemplo para JS a continuación:

$.ajaxSetup({
    dataFilter: function (data, type) {
        if (data && typeof data == "string") {
            if (data.indexOf('window.location') > -1) {
                eval(data);
            }
        }
        return data;
    }
});

Si los datos son: "window.location = '/ Acount / Login'" el filtro anterior lo detectará y evaluará para hacer la redirección en lugar de dejar que se muestren los datos.


17
2018-01-06 01:33