Como evitar que solicitações ajax sigam redirecionamentos usando jQuery

102

Eu uso as funções jQuery ajax para acessar um serviço web, mas o servidor, em vez de retornar uma resposta com um código de status descrevendo um problema, a solicitação é redirecionada para uma página com um cabeçalho 200, descrevendo o problema. Não posso fazer nenhuma alteração nisso, então preciso resolver isso no cliente de alguma forma.

Exemplo: Uma solicitação vai para algum URL que não foi encontrado, então recebo um Redirecionamento 302 para outro local. Uma nova solicitação é enviada e recebo 200 OK, evitando assim que o callback de erro seja disparado.

Existe alguma maneira de evitar que a solicitação ajax siga os redirecionamentos e, em vez disso, chame um retorno de chamada, de preferência o método de erro. Como alternativa, é possível detectar se um redirecionamento ocorreu no cliente?

Jørgen
fonte
8
É estranho que seu back-end esteja relatando 302 para um não encontrado, em vez de um 404.
Jake Feasel
Possível duplicata de Impedir redirecionamento de Xmlhttprequest
usuário de

Respostas:

96

Acho sua pergunta interessante, mas o problema como um todo me parece mais um mal-entendido. Ao menos tentarei explicar meu entendimento do problema.

O redirecionamento silencioso (transparente) é parte da XMLHttpRequestespecificação (veja aqui especialmente as palavras "... transparentemente siga o redirecionamento ..."). A menção padrão apenas que o agente de usuário (o navegador) pode prevenir ou notificar de certos tipos de redirecionamentos automáticos, mas não é uma parte XMLHttpRequest. É a parte da configuração do cliente HTTP (configuração do sistema operacional) ou a configuração do navegador da web. Portanto, jQuery.ajaxnão pode haver nenhuma opção onde você possa evitar o redirecionamento.

Você pode ver que o redirecionamento HTTP é parte do protocolo HTTP e não parte dele XMLHttpRequest. Portanto, está em outro nível de abstração ou na pilha da rede. Por exemplo, os dados do XMLHttpRequestpodem ser recuperados do proxy HTTP ou do cache do navegador local e fazem parte do protocolo HTTP. Principalmente o servidor que fornece os dados e não o cliente pode influenciar no armazenamento em cache.

Você pode comparar o requisito de sua pergunta com o requisito para evitar a alteração do endereço IP do servidor da web ou a alteração da rota IP durante a comunicação. Todas as coisas podem ser interessantes em alguns cenários, mas há partes de outro nível da pilha de comunicação e não podem ser gerenciadas por jQuery.ajaxou XMLHttpRequest.

O XMLHttpRequestpadrão diz que a configuração do cliente pode ter opções que evitam o redirecionamento. No caso do "mundo da Microsoft", que eu conheço melhor, você pode olhar a função WinHttpSetOption que pode ser usada para definir a WINHTTP_OPTION_DISABLE_FEATUREopção com o WINHTTP_DISABLE_REDIRECTSvalor. Outra forma é o uso da WINHTTP_OPTION_REDIRECT_POLICYopção com o WINHTTP_OPTION_REDIRECT_POLICY_NEVERvalor. Mais um recurso que pode ser usado no Windows é a função WinHttpSetStatusCallback , que pode definir a função de retorno de chamada recebida como algumas notificações WINHTTP_CALLBACK_FLAG_REDIRECT.

Portanto, é possível implementar seus requisitos em geral, mas a solução provavelmente não será independente do sistema operacional ou do navegador da Web e não estará no nível de jQuery.ajaxou XMLHttpRequest.

Oleg
fonte
2
Excelente resposta com grande percepção! Tenho alguma experiência com curl, para a qual você pode definir sinalizadores semelhantes aos que mencionou. Esperava que tais instruções pudessem ser repassadas ao navegador, mas pela sua resposta, entendo que o comportamento esperado seria seguir os redirecionamentos como se a solicitação inicial tivesse sido enviada para o local atribuído pelo servidor.
Jørgen
@ Jørgen: De nada! Na maioria dos cenários, o redirecionamento não é um problema. Você não descreve o contexto em que usa jQuery.ajax e se tem controle total sobre o servidor da Web para o qual envia a solicitação. Portanto, é difícil dar-lhe outros conselhos que possam realmente resolver o seu problema.
Oleg
Não controlo o lado do servidor, infelizmente. Acho que minha melhor opção é analisar ou validar o conteúdo da resposta.
Jørgen
@ Jørgen: Por que o redirecionamento é um problema no seu caso? Se o servidor moveu alguma página em outro local temporária ou permanentemente, ele pode redirecionar sua solicitação original para o novo local. Vai ficar tudo bem. Um administrador pode configurar o servidor da web para fazer o redirecionamento, por exemplo, durante a operação de restauração no servidor ou qualquer outro trabalho de suporte. Se você perguntar ao servidor por URL com DNS, o administrador pode alterar o mapeamento de IP para outro servidor. Ele pode fazer o redirecionamento HTTP da mesma forma que a reconfiguração do DNS. Qual é o seu problema?
Oleg
Meu problema é que o redirecionamento vai para uma página de erro genérica. Sei que o servidor deve ser configurado de forma diferente, mas, por enquanto, precisarei encontrar uma solução para a situação atual.
Jørgen
23

Não acredito que seja possível. A biblioteca subjacente (XHR) torna a nova solicitação de forma transparente. Dito isso, o que eu fiz nessas situações (geralmente um tipo de negócio de tempo limite de sessão que me leva a uma página de login) foi enviar de volta um cabeçalho de resposta personalizado. Eu também configurei um manipulador global de ajax que verifica a presença desse cabeçalho e responde apropriadamente quando presente (por exemplo, redirecionando a página inteira para a tela de login).

Caso você esteja interessado, aqui está o código jQuery que devo observar para esse cabeçalho personalizado:

/* redirects main window when AJAX request indicates that the session has expired on the backend. */
function checkSession(event, xhr, ajaxOptions)
{
    if (xhr.readyState == 4)
    {
        if(xhr.getResponseHeader("Login-Screen") != null && xhr.getResponseHeader("Login-Screen").length)
        {
            window.location.href='sessionExpired.html'; //whatever
        }
    }
}

$(document).ajaxComplete(checkSession)
Jake Feasel
fonte
1
Obrigado, acho que terei que me contentar com uma abordagem semelhante, analisando a resposta.
Jørgen
11

Eu encontrei um recurso para verificar se sua chamada foi redirecionada. É xhr.state (): se for "rejeitado", então ocorreu um redirecionamento.

Exemplo com retorno de chamada bem-sucedido:

request.success(function(data, textStatus, xhr)
{
    if(xhr.state() == "resolved")
    {
        //no redirection
    }
    if(xhr.state() == "rejected")
    {
        //redirection
    }
});

Exemplo com retorno de chamada de erro:

request.error(function(xhr, textStatus)
{
    if (xhr.state() == "rejected")
    {
        //redirection
        location.href = "loginpage";
    } else
    {
        //some other error happened
        alert("error");
    }
});
Takman
fonte
1
A dica em sua resposta realmente nos ajudou a fazer as coisas funcionarem. Por uma questão de notas que ajudariam outras pessoas, temos o WebSphere Portal com sua segurança integrada com o SiteMinder. Temos um portlet que precisa de chamadas Ajax para uma url de recurso e quando o tempo se esgota, os redirecionamentos transparentes acontecem, mas não estamos conseguindo lidar com como redirecionar para a página de login. Verificar se jqXHR.state () está sendo "rejeitado" certamente ajudou. Muito obrigado novamente.
Uresh Kuruhuri
Ótima descoberta, no entanto, pode haver falsos positivos, pois o estado "rejeitado" pode ser acionado mesmo se uma promessa for rejeitada e não apenas no redirecionamento. jQuery explica quando o estado "rejeitado" é enviado api.jquery.com/deferred.state
Nitin
1

Não posso acrescentar nada à sabedoria perspicaz dos codificadores anteriores que responderam, mas acrescentarei um caso específico que outros podem achar útil conhecer.

Eu me deparei com esse redirecionamento 302 silencioso no contexto do SharePoint. Tenho um código de cliente Javascript simples que executa ping em um subsite do SharePoint e, se receber uma resposta HTTP 200, ele se realoca nesse site, via window.location. Se receber mais alguma coisa, avisa ao usuário que o site não existe.

No entanto, no caso em que o site existe, mas o usuário não tem permissão, o SharePoint redireciona silenciosamente para uma página AccessDenied.aspx. O SharePoint já fez o handshake de autenticação HTTP 401 no nível do servidor / farm - o usuário tem acesso ao SharePoint. Mas o acesso ao subsite é manipulado, suponho, usando sinalizadores de banco de dados de algum tipo. O redirecionamento silencioso ignora minha cláusula "else", então não posso lançar meu próprio erro. No meu caso, isso não é um obstáculo - é um comportamento previsível consistente. Mas foi um pouco surpreendente e aprendi algo sobre solicitações HTTP no processo!

Chris van Hasselt
fonte
1

Eu estava interessado na mesma coisa e não consegui encontrar o state()método mencionado por Takman e fiz algumas pesquisas por mim mesmo. Para o bem das pessoas que vêm aqui em busca de uma resposta, aqui estão minhas descobertas:

Conforme declarado várias vezes, você não pode evitar redirecionamentos, mas pode detectá-los. De acordo com o MDN, você pode usar o responseURLde XMLHttpRequestObject, que conterá o URL final de onde veio a resposta, após todos os redirecionamentos. A única ressalva é que não é compatível com o Internet Explorer (Edge tem). Como o xhr/ jqXHRpassado para a função success/ donedo jquery é uma extensão do real XMLHttpRequest, ele também deve estar disponível lá.

Rialgar
fonte
0

Suponho que você receba uma resposta 200 porque na segunda vez não há redirecionamento, porque a página 404 não expira, ela é salva no cache. Isso quer dizer que na segunda vez o navegador lhe dá a página no cache. Existe uma propriedade "cache" no jquery ajax. http://api.jquery.com/jQuery.ajax/

Você deve escrever como "falso"

netadictos
fonte
0

Embora não seja possível desativar o redirecionamento de local a seguir em XmlHttpRequests , é ao usar fetch () :

fetch('url', {redirect: manual});
Cweiske
fonte
A busca não informa qual foi o URL redirecionado. Você pode desativar o comportamento, mas "redirect: manual" não tem a intenção de permitir que o usuário faça o redirecionamento por conta própria, tem o nome intents.
lcjury de
-2

Não tenho certeza se isso se aplica ao seu caso, mas você pode escrever um código para responder a códigos de status específicos na função AJAX -

$.ajax({
    url: '/admin/secret/data',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    statusCode: {
        200: function (data) {
            alert('302: Occurred');
            // Bind the JSON data to the UI
        },
        401: function (data) {
            alert('401: Occurred');
            // Handle the 401 error here.
        }
    }
});
ipr101
fonte
9
Eu não acho que isso se aplica porque OP disse que ele sempre recebe um código de status 200 devido ao redirecionamento acontecendo em segundo plano.
Sim Barry
-4

Nos cabeçalhos de solicitação, no caso de solicitação ajax, você terá o seguinte

X-Requested-With    XMLHttpRequest

Por este critério no lado do servidor, você pode filtrar as solicitações.

Gfox
fonte
Ele queria verificar a resposta, não a solicitação (sobre a qual ele já tem controle)
Sim Barry
Talvez Gfox tenha sugerido que para as solicitações de ajax que retornam 3xx não fazem sentido, você pode filtrar esse tipo de solicitação e retornar 403, por exemplo. quando você tem um site que serve páginas que requerem autenticação de formulários e essas páginas também fazem chamadas ajax e você quer colocar a lógica de autorização em um lugar, então para mim é uma resposta válida.
maciejW