Como obter uma solicitação de postagem de compartilhamento de recursos de origem cruzada (CORS)

216

Eu tenho uma máquina na minha LAN local (machineA) que possui dois servidores web. O primeiro é o embutido no XBMC (na porta 8080) e exibe nossa biblioteca. O segundo servidor é um script python CherryPy (porta 8081) que estou usando para acionar uma conversão de arquivo sob demanda. A conversão do arquivo é acionada por uma solicitação AJAX POST da página veiculada no servidor XBMC.

  • Vá para http: // machineA: 8080, que exibe a biblioteca
  • Biblioteca é exibida
  • O usuário clica no link 'converter' que emite o seguinte comando -

Solicitação Ajax do jQuery

$.post('http://machineA:8081', {file_url: 'asfd'}, function(d){console.log(d)})
  • O navegador emite uma solicitação OPÇÕES HTTP com os seguintes cabeçalhos;

Cabeçalho da solicitação - OPÇÕES

Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://machineA:8080
Access-Control-Request-Method: POST
Access-Control-Request-Headers: x-requested-with
  • O servidor responde com o seguinte;

Cabeçalho de resposta - OPÇÕES (STATUS = 200 OK)

Content-Length: 0
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:40:29 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: text/html;charset=ISO-8859-1
  • A conversa então pára. O navegador, em teoria, deve emitir uma solicitação POST, pois o servidor respondeu com os cabeçalhos CORS corretos (?) (Access-Control-Allow-Origin: *)

Para solução de problemas, também emiti o mesmo comando $ .post em http://jquery.com . É aqui que sou surpreendido, a partir de jquery.com, a solicitação de postagem funciona, uma solicitação de OPÇÕES é enviada após um POST. Os cabeçalhos desta transação estão abaixo;

Cabeçalho da solicitação - OPÇÕES

Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://jquery.com
Access-Control-Request-Method: POST

Cabeçalho de resposta - OPÇÕES (STATUS = 200 OK)

Content-Length: 0
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:37:59 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: text/html;charset=ISO-8859-1

Cabeçalho da Solicitação - POST

Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://jquery.com/
Content-Length: 12
Origin: http://jquery.com
Pragma: no-cache
Cache-Control: no-cache

Cabeçalho de resposta - POST (STATUS = 200 OK)

Content-Length: 32
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:37:59 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: application/json

Não sei por que o mesmo pedido funcionaria em um site, mas não no outro. Espero que alguém possa apontar o que estou perdendo. Obrigado pela ajuda!

James
fonte
O CORS é necessário se os dois servidores da web estiverem na mesma máquina?
Jdigital
8
Que eu saiba, é uma solicitação do CORS por causa da porta diferente. Além disso, o pedido OPÇÕES indica que o navegador é tratá-la como um CORS solicitar
James

Respostas:

157

Finalmente encontrei este link " Uma solicitação CORS POST funciona com javascript simples, mas por que não com jQuery? ", Que observa que o jQuery 1.5.1 adiciona o

 Access-Control-Request-Headers: x-requested-with

cabeçalho para todos os pedidos CORS. O jQuery 1.5.2 não faz isso. Além disso, de acordo com a mesma pergunta, definir um cabeçalho de resposta do servidor de

Access-Control-Allow-Headers: *

não permite que a resposta continue. Você precisa garantir que o cabeçalho de resposta inclua especificamente os cabeçalhos necessários. ou seja:

Access-Control-Allow-Headers: x-requested-with 
James
fonte
70

SOLICITAÇÃO:

 $.ajax({
            url: "http://localhost:8079/students/add/",
            type: "POST",
            crossDomain: true,
            data: JSON.stringify(somejson),
            dataType: "json",
            success: function (response) {
                var resp = JSON.parse(response)
                alert(resp.status);
            },
            error: function (xhr, status) {
                alert("error");
            }
        });

RESPOSTA:

response = HttpResponse(json.dumps('{"status" : "success"}'))
response.__setitem__("Content-type", "application/json")
response.__setitem__("Access-Control-Allow-Origin", "*")

return response
Hassan Zaheer
fonte
3
se o servidor não aceitar origem cruzada, o crossdomain = true não precisará resolver o problema. usar dataType: "jsonp" e definir o retorno de chamada como jsonpCallback: "response" seria a melhor ideia para fazer isso. Veja também: api.jquery.com/jquery.ajax
BonifatiusK:
14

Resolvi meu próprio problema ao usar a API de matriz de distância do Google, definindo meu cabeçalho de solicitação com Jquery ajax. dê uma olhada abaixo.

var settings = {
          'cache': false,
          'dataType': "jsonp",
          "async": true,
          "crossDomain": true,
          "url": "https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=place_id:"+me.originPlaceId+"&destinations=place_id:"+me.destinationPlaceId+"&region=ng&units=metric&key=mykey",
          "method": "GET",
          "headers": {
              "accept": "application/json",
              "Access-Control-Allow-Origin":"*"
          }
      }

      $.ajax(settings).done(function (response) {
          console.log(response);

      });

Observe o que eu adicionei nas configurações
**

"headers": {
          "accept": "application/json",
          "Access-Control-Allow-Origin":"*"
      }

**
Espero que isso ajude.

Miracool
fonte
esta é a única solução funcionou para nós sem changeing nada no lado do servidor ... thanx MiraCool
Timsta
1
@ Timsta, estou feliz por minha sugestão ter funcionado para você. Grato por empilhar estouro também. Tenha um ótimo dia.
Miracool 16/07
13

Levei algum tempo para encontrar a solução.

Caso o seu servidor responda corretamente e a solicitação seja o problema, você deve adicionar withCredentials: trueà xhrFieldssolicitação:

$.ajax({
    url: url,
    type: method,
    // This is the important part
    xhrFields: {
        withCredentials: true
    },
    // This is the important part
    data: data,
    success: function (response) {
        // handle the response
    },
    error: function (xhr, status) {
        // handle errors
    }
});

Nota: jQuery> = 1.5.1 é necessário

Dekel
fonte
versão jquery ok? você definiu o withCredentials: true? Tem certeza de que possui os cabeçalhos relevantes?
Dekel
Sim, 1comCredential: true , jquery version: 3.2.1`. Na verdade, está trabalhando no meio carteiro, mas não passando por cromo navegador
Mox Shah
1
Tenho quase certeza de que o carteiro não deve ter problemas com o CORS porque não é um navegador e seu comportamento é diferente. Você tem certeza de que os cabeçalhos corretos e relevantes são enviados do servidor para o cliente? Novamente - observe que essa alteração não é suficiente. Você precisa garantir que a resposta do servidor com os cabeçalhos corretos .
precisa saber é o seguinte
Você poderia me dizer qual é o cabeçalho de resposta necessário no servidor?
Mox Shah
@MoxShah esta é uma questão completamente diferente e há uma série de recursos lá :)
Dekel
9

Bem, eu lutei com esse problema por algumas semanas.

A maneira mais fácil, compatível e não hacky de fazer isso é provavelmente usar uma API JavaScript do provedor que não faça chamadas baseadas no navegador e possa lidar com solicitações de origem cruzada.

Por exemplo, API JavaScript do Facebook e API JS do Google.

Caso o seu provedor de API não seja atual e não ofereça suporte ao cabeçalho Origem dos Recursos de Origem '*' em sua resposta e não tenha uma API JS (sim, estou falando de você, Yahoo), você terá uma de três opções:

  1. Usando jsonp em suas solicitações, que adiciona uma função de retorno de chamada à sua URL, onde você pode lidar com sua resposta. Nota: isso mudará o URL da solicitação, para que o servidor da API esteja equipado para lidar com o? Callback = no final do URL.

  2. Envie a solicitação ao seu servidor de API, que é seu controlador e está no mesmo domínio que o cliente ou tem o Compartilhamento de Recursos de Origem Cruzada ativado de onde você pode fazer proxy da solicitação ao servidor de API de terceiros.

  3. Provavelmente mais útil nos casos em que você está fazendo solicitações de OAuth e precisa lidar com a interação do usuário Haha! window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')

Pritam Roy
fonte
4

Usar isso em combinação com o Laravel resolveu meu problema. Basta adicionar esse cabeçalho à sua solicitação de jquery Access-Control-Request-Headers: x-requested-withe verifique se a resposta do lado do servidor tem esse cabeçalho definido Access-Control-Allow-Headers: *.

MK
fonte
6
Não há razão para adicionar cabeçalhos CORS à solicitação manualmente. O navegador sempre adicionará os cabeçalhos CORS prop à solicitação.
Raio Nicholus
1
O verdadeiro desafio é fazer com que o servidor responda com um Access-Control-Allow-Headersfornecimento correto e JQ correto Access-Control-Request-Headers(mais qualquer um que você adicione via código), nenhum dos quais pode ser curinga. leva apenas um cabeçalho "ruim" para explodir o pré-vôo, por exemplo, usar If-None-Matchpara um GET condicional, se o servidor não tiver o listado.
escape-llc
1

Por alguma razão, uma pergunta sobre solicitações GET foi mesclada com essa, então responderei aqui.

Essa função simples obterá de forma assíncrona uma resposta de status HTTP a partir de uma página habilitada para CORS. Se você executá-lo, verá que apenas uma página com os cabeçalhos adequados retornará um status 200 se acessada via XMLHttpRequest - seja GET ou POST. Nada pode ser feito no lado do cliente para contornar isso, exceto possivelmente usando JSONP se você precisar apenas de um objeto json.

O seguinte pode ser facilmente modificado para manter os dados mantidos no objeto xmlHttpRequestObject:

function checkCorsSource(source) {
  var xmlHttpRequestObject;
  if (window.XMLHttpRequest) {
    xmlHttpRequestObject = new XMLHttpRequest();
    if (xmlHttpRequestObject != null) {
      var sUrl = "";
      if (source == "google") {
        var sUrl = "https://www.google.com";
      } else {
        var sUrl = "https://httpbin.org/get";
      }
      document.getElementById("txt1").innerHTML = "Request Sent...";
      xmlHttpRequestObject.open("GET", sUrl, true);
      xmlHttpRequestObject.onreadystatechange = function() {
        if (xmlHttpRequestObject.readyState == 4 && xmlHttpRequestObject.status == 200) {
          document.getElementById("txt1").innerHTML = "200 Response received!";
        } else {
          document.getElementById("txt1").innerHTML = "200 Response failed!";
        }
      }
      xmlHttpRequestObject.send();
    } else {
      window.alert("Error creating XmlHttpRequest object. Client is not CORS enabled");
    }
  }
}
<html>
<head>
  <title>Check if page is cors</title>
</head>
<body>
  <p>A CORS-enabled source has one of the following HTTP headers:</p>
  <ul>
    <li>Access-Control-Allow-Headers: *</li>
    <li>Access-Control-Allow-Headers: x-requested-with</li>
  </ul>
  <p>Click a button to see if the page allows CORS</p>
  <form name="form1" action="" method="get">
    <input type="button" name="btn1" value="Check Google Page" onClick="checkCorsSource('google')">
    <input type="button" name="btn1" value="Check Cors Page" onClick="checkCorsSource('cors')">
  </form>
  <p id="txt1" />
</body>
</html>

Victor Stoddard
fonte
1

Eu tinha exatamente o mesmo problema em que o jquery ajax só me dava problemas com solicitações de mensagens em que as solicitações funcionavam bem - eu cansei de tudo acima sem resultados. Eu tinha os cabeçalhos corretos no meu servidor etc. Mudar para usar XMLHTTPRequest em vez de jquery corrigiu meu problema imediatamente. Não importa qual versão do jquery eu usei, não a corrigiu. A busca também funciona sem problemas se você não precisar de compatibilidade com o navegador anterior.

        var xhr = new XMLHttpRequest()
        xhr.open('POST', 'https://mywebsite.com', true)
        xhr.withCredentials = true
        xhr.onreadystatechange = function() {
          if (xhr.readyState === 2) {// do something}
        }
        xhr.setRequestHeader('Content-Type', 'application/json')
        xhr.send(json)

Espero que isso ajude outras pessoas com os mesmos problemas.

ozzieisaacs
fonte
1

Este é um resumo do que funcionou para mim:

Defina uma nova função (empacotada $.ajaxpara simplificar):

jQuery.postCORS = function(url, data, func) {
  if(func == undefined) func = function(){};
  return $.ajax({
    type: 'POST', 
    url: url, 
    data: data, 
    dataType: 'json', 
    contentType: 'application/x-www-form-urlencoded', 
    xhrFields: { withCredentials: true }, 
    success: function(res) { func(res) }, 
    error: function() { 
            func({}) 
    }
  });
}

Uso:

$.postCORS("https://example.com/service.json",{ x : 1 },function(obj){
      if(obj.ok) {
           ...
      }
});

Também trabalha com .done, .fail, etc:

$.postCORS("https://example.com/service.json",{ x : 1 }).done(function(obj){
      if(obj.ok) {
           ...
      }
}).fail(function(){
    alert("Error!");
});

No lado do servidor (neste caso, em que example.com está hospedado), defina esses cabeçalhos (adicionado algum código de exemplo no PHP):

header('Access-Control-Allow-Origin: https://not-example.com');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 604800');
header("Content-type: application/json");
$array = array("ok" => $_POST["x"]);
echo json_encode($array);

Esta é a única maneira que eu conheço verdadeiramente para POST entre domínios de JS.

JSONP converte o POST em GET, que pode exibir informações confidenciais nos logs do servidor.

lepe
fonte
0

Se por algum motivo, ao tentar adicionar cabeçalhos ou definir uma política de controle, você ainda não chegar a lugar algum, considere usar o apache ProxyPass…

Por exemplo, em um <VirtualHost>que usa SSL, adicione as duas diretivas a seguir:

SSLProxyEngine On
ProxyPass /oauth https://remote.tld/oauth

Certifique-se de que os seguintes módulos apache estejam carregados (carregue-os usando a2enmod):

  • procuração
  • proxy_connect
  • proxy_http

Obviamente, você terá que alterar seu URL de solicitações AJAX para usar o proxy apache…

llange
fonte
-3

É um pouco tarde para a festa, mas estou lutando com isso há alguns dias. É possível e nenhuma das respostas que encontrei aqui funcionou. É enganosamente simples. Aqui está a chamada .ajax:

    <!DOCTYPE HTML>
    <html>
    <head>
    <body>
     <title>Javascript Test</title>
     <script src="http://code.jquery.com/jquery-latest.min.js"></script>
     <script type="text/javascript">
     $(document).domain = 'XXX.com';
     $(document).ready(function () {
     $.ajax({
        xhrFields: {cors: false},
        type: "GET",
        url: "http://XXXX.com/test.php?email='[email protected]'",
        success: function (data) {
           alert(data);
        },
        error: function (x, y, z) {
           alert(x.responseText + " :EEE: " + x.status);
        }
    });
    });
    </script> 
    </body>
    </html>

Aqui está o php no lado do servidor:

    <html>
    <head>
     <title>PHP Test</title>
     </head>
    <body>
      <?php
      header('Origin: xxx.com');
      header('Access-Control-Allow-Origin:*');
      $servername = "sqlxxx";
      $username = "xxxx";
      $password = "sss";
      $conn = new mysqli($servername, $username, $password);
      if ($conn->connect_error) {
        die( "Connection failed: " . $conn->connect_error);
      }
      $sql = "SELECT email, status, userdata  FROM msi.usersLive";
      $result = $conn->query($sql);
      if ($result->num_rows > 0) {
      while($row = $result->fetch_assoc()) {
        echo $row["email"] . ":" . $row["status"] . ":" . $row["userdata"] .  "<br>";
      }
    } else {
      echo "{ }";
    }
    $conn->close();
    ?>
    </body>

Steve Johnson
fonte
1
Pelo que vale, o cabeçalho Origin é um cabeçalho de solicitação, não um cabeçalho de resposta. Seu script php não deve estar definido.
Noodles
Hmmph. Isso aumentou minhas esperanças e as frustrou quando vi que você estava criando um AJAX "GET" em seu código, quando o OP disse claramente que estava tentando EVITAR usando "GET" e queria usar "POST".
26617 Steve Sauder
Os cabeçalhos do CORS funcionam da mesma maneira, independentemente do verbo envolvido. Podemos invocar todos os verbos via $.ajax()um servidor configurado corretamente. A parte mais difícil é Access-Control-Request-Headersacertar, mas mesmo isso não é muito difícil. Conforme observado nos pôsteres anteriores, este não deve ser um curinga, mas uma lista de permissões de cabeçalhos.
escape-llc