Por que estou recebendo uma solicitação OPTIONS em vez de uma solicitação GET?

288
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script>
$.get("http://example.com/", function(data) {
     alert(data);
});
</script>

ele faz uma solicitação OPTIONS para esse URL e, em seguida, o retorno de chamada nunca é chamado com nada.

Quando não é um domínio cruzado, funciona bem.

O jQuery não deveria apenas fazer a chamada com um <script>nó e, em seguida, fazer o retorno de chamada quando carregado? Entendo que não poderei obter o resultado (já que é um domínio cruzado), mas tudo bem; Eu só quero que a ligação seja concluída. Isso é um bug ou estou fazendo algo errado?

Paul Tarjan
fonte
2
Pode ser cos de domínio cruzado. Por exemplo, se você estiver no seu arquivo File: // PATH_TO_WEBSITE em vez de usar localhost / WEBSITE_LINK
James111 22/12/2015

Respostas:

262

De acordo com a MDN ,

Solicitações comprovadas

Diferentemente das solicitações simples (discutidas acima), as solicitações "preflighted" primeiro enviam um cabeçalho de solicitação HTTP OPTIONS para o recurso no outro domínio, para determinar se a solicitação real é segura para enviar. As solicitações entre sites são comprovadas dessa maneira, pois podem ter implicações nos dados do usuário. Em particular, um pedido é comprovado se:

  • Ele usa métodos diferentes de GET ou POST. Além disso, se o POST for usado para enviar dados da solicitação com um Tipo de Conteúdo diferente de application / x-www-form-urlencoded, multipart / form-data ou text / plain, por exemplo, se a solicitação POST enviar uma carga XML para o servidor usando application / xml ou text / xml, a solicitação é comprovada.
  • Ele define cabeçalhos personalizados na solicitação (por exemplo, a solicitação usa um cabeçalho como X-PINGOTHER)
arturgrigor
fonte
43
esta fixado o nosso problema, mudando de "application / json" para "text / plain" parou as opções horríveis solicitar
Keeno
10
o que eu não entendo é por que o navegador está solicitando com o método OPTIONS apenas para verificar se a solicitação real é segura para enviar. mas em que sentido? o servidor também pode colocar restrições com determinados cabeçalhos de resposta. Por que isso é necessário?
hardik
11
@hardik Lembre-se de que ao adicionar o CORS, você está potencialmente aceitando solicitações de qualquer pessoa, nas quais elas possam manipular dados no seu servidor através de solicitações (POST, PUT, DELETE etc). Nessas situações, como ao usar cabeçalhos personalizados, o navegador apenas verifica com o servidor se o servidor está disposto a aceitar a solicitação antes de enviá-la, pois o envio de solicitações não solicitadas ao servidor pode ser realmente perigoso para seus dados e também o que é o ponto no navegador enviando cargas úteis potencialmente grandes se o servidor não quiser aceitá-las, portanto, verifique as OPÇÕES antes do voo.
Davidnknight
6
@davidnknight se o envio de seus dados para o servidor pode ser perigoso, o que significa que o servidor pode estar comprometido, é claro que o servidor mal-intencionado responderia à sua solicitação de OPÇÕES com "Claro, envie tudo!". Como é essa segurança? (pergunta honesta)
Matt
3
"solicitações de comprovação não são uma coisa de segurança. Antes, elas não mudam as regras". - Veja a resposta para Qual é a motivação por trás da introdução de solicitações de
comprovação
9

Se você está tentando postar

Verifique os JSON.stringifydados do formulário e envie como text/plain.

<form id="my-form" onSubmit="return postMyFormData();">
    <input type="text" name="name" placeholder="Your Name" required>
    <input type="email" name="email" placeholder="Your Email" required>
    <input type="submit" value="Submit My Form">
</form>

function postMyFormData() {

    var formData = $('#my-form').serializeArray();
    formData = formData.reduce(function(obj, item) {
        obj[item.name] = item.value;
        return obj;
    }, {});
    formData = JSON.stringify(formData);

    $.ajax({
        type: "POST",
        url: "https://website.com/path",
        data: formData,
        success: function() { ... },
        dataType: "text",
        contentType : "text/plain"
    });
}
Derek Soike
fonte
2

Não acredito que o jQuery faça naturalmente uma solicitação JSONP quando receber um URL como esse. No entanto, ele fará uma solicitação JSONP quando você informar qual argumento usar para um retorno de chamada:

$.get("http://metaward.com/import/http://metaward.com/u/ptarjan?jsoncallback=?", function(data) {
     alert(data);
});

O script de recebimento depende inteiramente do argumento (que não precisa ser chamado "jsoncallback"), portanto, nesse caso, a função nunca será chamada. Mas, como você declarou que deseja apenas que o script em metaward.com seja executado, isso seria necessário.

VoteyDisciple
fonte
O meu retorno de chamada ainda seria notificado de que o elemento de script foi totalmente carregado? Eu só quero ter certeza de que a atualização aconteceu antes de consultar a API.
22415 Paul Tarjan
Você o fará se o script de recebimento suportar JSONP e desejar chamar a função que você identifica. Se o script não gerar nada além de gerar um bloco de dados JSON sem nenhum outro comportamento, você não saberá quando terminar o carregamento. Se for essencial saber quando o carregamento está concluído, considere implementar um script em seu próprio servidor que atue como um proxy.
VoteyDisciple
1

De fato, solicitações AJAX (XMLHttp) entre domínios não são permitidas por motivos de segurança (pense em buscar uma página da Web "restrita" do lado do cliente e enviá-la de volta ao servidor - isso seria um problema de segurança).

A única solução alternativa são retornos de chamada. Isto é: criando um novo objeto de script e apontando o src para o JavaScript do lado final, que é um retorno de chamada com valores JSON (myFunction ({data}), myFunction é uma função que faz algo com os dados (por exemplo, armazená-lo em uma variável).

Adrián Navarro
fonte
1
certo, mas eu posso carregá-lo em um <script src = ""> ou <img src = ""> e o navegador terá prazer em acessá-lo. Eu só quero saber quando ele está totalmente carregado para que eu possa consultar o resultado da importação.
22415 Paul Tarjan
1

Apenas mude "application / json" para "text / plain" e não esqueça o JSON.stringify (request):

var request = {Company: sapws.dbName, UserName: username, Password: userpass};
    console.log(request);
    $.ajax({
        type: "POST",
        url: this.wsUrl + "/Login",
        contentType: "text/plain",
        data: JSON.stringify(request),

        crossDomain: true,
    });
David Lopes
fonte
1

Eu tive o mesmo problema. Minha correção foi adicionar cabeçalhos ao meu script PHP, que estão presentes apenas quando no ambiente de desenvolvimento.

Isso permite solicitações entre domínios:

header("Access-Control-Allow-Origin: *");

Isso informa à solicitação de comprovação que não há problema em o cliente enviar os cabeçalhos que deseja:

header("Access-Control-Allow-Headers: *");

Dessa forma, não há necessidade de modificar a solicitação.

Se você possui dados confidenciais em seu banco de dados de desenvolvimento que podem vazar, então pense duas vezes sobre isso.

fivebit
fonte
1

No meu caso, o problema não estava relacionado ao CORS, pois eu estava emitindo um jQuery POST no mesmo servidor da web. Os dados eram JSON, mas eu havia omitido o parâmetro dataType: 'json'.

Eu não tinha (nem adicionei) um parâmetro contentType, como mostrado na resposta de David Lopes acima.

GarDavis
fonte
0

Parece que o Firefox e o Opera (testados no Mac também) não gostam do domínio cruzado disso (mas o Safari está bem com isso).

Pode ser necessário chamar um código local do servidor para ondular a página remota.

helloandre
fonte
0

Consegui corrigi-lo com a ajuda dos seguintes cabeçalhos

Access-Control-Allow-Origin
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Access-Control-Allow-Methods

Se você estiver no Nodejs, aqui está o código que você pode copiar / colar.

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin','*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH');
  next();
});
obai
fonte