CORS para XHR no IE10

A quarta plataforma do IE10 simplifica a criação de cenários intersites que funcionam de maneira consistente em todos os navegadores através do CORS (Cross Origin Resource Sharing, compartilhamento de recursos de origem cruzada) para XHR (XMLHttpRequest). O CORS para XHR torna o compartilhamento de dados em sites mais simples e flexível. No cenário mais básico, o CORS permite criar fontes de dados acessíveis de qualquer site e, com simples ajustes, você pode escolher restringir sites permitidos, oferecer suporte à modificação de dados e até permitir autenticação. O mais importante de tudo é que o CORS mantém sites existentes seguros, exigindo participação do servidor.

XHR de origem cruzada simples

Vejamos como uma solicitação XHR de origem cruzada se compara a uma solicitação de mesma origem. No script, a única diferença é a URL passada para o método aberto. Por exemplo, vamos supor que estejamos trabalhando em um script que busca uma lista de álbuns de fotos.

XHR tradicional

// Script running on http://photos.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "/albums", true);

xhr.send();

Agora, queremos acessar a lista de álbuns de uma outra origem. A outra origem pode ser de domínio completamente diferente ou de um host diferente com o mesmo domínio base. De qualquer forma, apontar para a URL completa de outro site é o suficiente para definir que o navegador envie automaticamente uma solicitação de CORS.

XHR com CORS habilitada

// Script running on http://www.contoso.com

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "http://photos.contoso.com/albums", true);

xhr.send();

Sites podem fornecer fallback para navegadores mais antigos, quebrando-os em detecção de recurso. Verificar “withCredentials” é o melhor método, já que se relaciona diretamente ao suporte CORS para XHR.

XHR com CORS habilitada e detecção de recurso

// Script running on http://www.contoso.com

var xhr = new XMLHttpRequest();

if ("withCredentials" in xhr) {

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("GET", "http://photos.contoso.com/albums", true);

xhr.send();

} else {

// Fallback behavior for browsers without CORS for XHR

}

Neste ponto, nosso código do cliente faz uma solicitação de CORS diretamente para "http://photos.contoso.com", mas a solicitação não retorna nenhum dado. A falha ocorre porque o servidor ainda não está participando. Dando uma olhada rápida nas ferramentas do desenvolvedor, podemos ter uma ideia do que deu errado.

Captura de tela mostrando ferramentas F12, indicando que o cabeçalho 'Access-Control-Allow-Origin' não foi encontrado.

Aqui podemos observar que o servidor precisa enviar um cabeçalho “Access-Control-Allow-Origin” na resposta. Em nosso cenário, não estamos abrindo nossos álbuns para que outros sites tenham acesso, mas queremos habilitar o acesso somente de “http://www.contoso.com”. Essa ação requer que o servidor tenha permissão para identificar de onde veio a solicitação. A análise de nossa solicitação de saída revela um novo cabeçalho que contém exatamente esta informação, “Origin”.

Cabeçalhos simples de solicitação de CORS

GET http://photos.contoso.com/albums HTTP/1.1

Origin: http://www.contoso.com

...

Usando esta informação, o servidor pode escolher limitar o acesso para qualquer grupo de sites. Se o servidor sempre adiciona um cabeçalho “Access-Control-Allow-Origin” com um valor de '*', todos os sites terão acesso. Para nosso cenário, o servidor verificará a origem e definirá “Access-Control-Allow-Origin” para permitir somente “http://www.contoso.com”.

Cabeçalhos simples de resposta de CORS

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.contoso.com

...

Com as atualizações acima definidas, nosso “http://www.contoso.com” cliente pode acessar listas de álbuns do servidor em “http://photos.contoso.com”.

XHR de origem cruzada com simulação

As solicitações CORS discutidas até agora são ótimas tanto para cenários básicos, somente para leitura, como para download de álbum de fotos. Dar o próximo passo modificando o compartilhamento de dados de sites requer um pouco mais de trabalho do servidor. Por exemplo, vamos supor que estejamos adicionando o código no cliente para criar um novo álbum.

var xhr = new XMLHttpRequest();

xhr.onerror = _handleError;

xhr.onload = _handleLoad;

xhr.open("PUT", "http://photos.contoso.com/albums", true);

xhr.send(JSON.stringify({ name: "New Album" }));

Executar do jeito que está não funciona. A análise do tráfego de rede revela que uma solicitação foi enviada, mas não a que esperávamos.

Captura de tela das ferramentas F12 mostrando uma solicitação simulada OPTIONS (opções).

O que o navegador realmente enviou é chamado de solicitação simulada. As solicitações simuladas são enviadas antes das solicitações que possam resultar em modificação dos dados no servidor. Essas solicitações são identificadas pela presença de propriedades não simples como definidas na especificação do CORS. Estas propriedades abrangem de certos métodos HTTP como “PUT” até cabeçalhos HTTP personalizados. Os navegadores enviam solicitações simuladas pedindo permissão ao servidor para enviar a solicitação real. No nosso exemplo, o navegador está verificando se uma solicitação “PUT” é permitida.

Solicitação simulada

OPTIONS http://photos.contoso.com/albums HTTP/1.1

Origin: http://www.contoso.com

Access-Control-Request-Method: PUT

...

Para que o navegador envie a solicitação real, são necessárias algumas mudanças no servidor. Mais uma vez, podemos olhar nas ferramentas do desenvolvedor para obter mais informações.

Captura de tela mostrando ferramentas F12 indicando que a lista 'Access-Control-Allow-Methods' não foi encontrada.

O primeiro passo é fazer o servidor reconhecer a solicitação simulada “OPTIONS” como diferente das outras solicitações para a mesma URL. Depois que o servidor verificar a solicitação simulada assegurando que “Access-Control-Request-Method” está pedindo por “PUT” de uma origem permitida, o servidor enviará a aprovação apropriada pelo cabeçalho “Access-Control-Allow-Methods”.

Resposta simulada

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.contoso.com

Access-Control-Allow-Methods: PUT

...

Quando a simulação é removida e aprovada, a solicitação real a substitui.

Solicitação real

PUT http://photos.contoso.com/albums HTTP/1.1

Origin: http://www.contoso.com

...

A adição do álbum está, tecnicamente, completa neste momento, mas nosso código do cliente não saberá a menos que o servidor responda corretamente. O servidor ainda deve, especificamente, incluir “Access-Control-Allow-Origin” na resposta.

Resposta real

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.contoso.com

...

Com isto, o cliente pode adicionar um novo álbum de origem cruzada e reconhecer se a ação foi ou não concluída com sucesso.

Próximas etapas

Unir o CORS a outros novos recursos de plataforma ativa cenários interessantes. Um exemplo é o Test drive de carregamento intersite que acompanha carregamentos de arquivo de origem cruzada utilizando CORS, XHR, FileAPI e eventos em andamento.

—Tony Ross, gerente de programa, Internet Explorer