Como adicionar compatibilidade com CORS a um proxy de API

Você está vendo a documentação da Apigee X.
Ver a documentação da Apigee Edge

O compartilhamento de recursos entre origens (CORS, na sigla em inglês) é um mecanismo padrão que permite que chamadas JavaScript XMLHttpRequest (XHR) executadas em uma página da Web interajam com recursos de domínios que não são de origem. O CORS é uma solução normalmente implementada na "política de mesma origem" que é aplicada por todos os navegadores. Por exemplo, se você fizer uma chamada XHR para a API Twitter a partir do código JavaScript em execução no seu navegador, a chamada falhará. Isso ocorre porque o domínio que veicula a página para o navegador não é o mesmo que usa a API Twitter. O CORS oferece uma solução para esse problema permitindo que os servidores optem por permitir o compartilhamento de recursos entre origens.

Vídeo: assista a um vídeo curto para saber como ativar o CORS em um proxy de API.

Caso de uso típico para CORS

O código JQuery a seguir chama um serviço de destino fictício. Se executada de dentro do contexto de um navegador (uma página da Web), a chamada falhará devido à política de mesma origem:

<script>
var url = "http://service.example.com";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This is where we end up!
            }
    });
  });
});
</script>

Uma solução para esse problema é criar um proxy da API Apigee que chame a API de serviço no back-end. Lembre-se de que o Apigee fica entre o cliente (um navegador, neste caso) e a API de back-end (o serviço). Como o proxy da API é executado no servidor, não em um navegador, ele consegue chamar o serviço com sucesso. Em seguida, basta anexar cabeçalhos CORS à resposta TargetEndpoint. Contanto que o navegador seja compatível com CORS, esses cabeçalhos indicam ao navegador que não há problema em "relaxar" a política de mesma origem, permitindo o sucesso da chamada da API de origem cruzada.

Depois que o proxy compatível com o CORS for criado, é possível chamar o URL do proxy da API em vez do serviço de back-end no código do lado do cliente. Exemplo:

<script>
var url = "http://myorg-test.apigee.net/v1/example";
$(document).ready(function(){
  $("button").click(function(){
    $.ajax({
        type:"GET",
        url:url,
        async:true,
        dataType: "json",
           success: function(json) {
              // Parse the response.
              // Do other things.
           },
           error: function(xhr, status, err) {
              // This time, we do not end up here!
            }
    });
  });
});
</script>

Como anexar uma política de CORS a um novo proxy de API

É possível adicionar o suporte a CORS a um proxy de API anexando uma política de "Adicionar CORS" ao proxy de API ao criá-lo. Para adicionar essa política, marque a caixa de seleção Adicionar cabeçalhos CORS na página "Segurança" do assistente "Criar um proxy".

Quando você marca essa caixa de seleção, uma política chamada "Adicionar CORS" é adicionada automaticamente ao sistema e anexada ao pré-fluxo de resposta do TargetEndpoint, como mostrado na figura a seguir:

Política &quot;Adicionar CORS&quot; adicionada ao navegador em &quot;Políticas&quot; e anexada ao pré-fluxo de resposta do TargetEndpoint à direita

A política "Adicionar CORS" é implementada como uma política AssignMessage, que adiciona os cabeçalhos apropriados à resposta. Basicamente, os cabeçalhos permitem que o navegador saiba com quais origens ele compartilhará os recursos, quais métodos ele aceita e assim por diante. Leia mais sobre esses cabeçalhos CORS na Recomendação W3C de compartilhamento de recursos de origem cruzada.

Modifique a política da seguinte maneira:

  • Adicione os cabeçalhos content-type e authorization (obrigatórios para oferecer suporte à autenticação básica ou OAuth2) ao cabeçalho Access-Control-Allow-Headers, como mostrado no trecho de código abaixo.
  • Para a autenticação OAuth2, talvez seja necessário corrigir um comportamento não compatível com o RFC.
  • É recomendável usar <Set> para definir os cabeçalhos do CORS em vez de <Add>, como mostrado no trecho abaixo. Ao usar <Add>, se o cabeçalho Access-Control-Allow-Origin já existir, você receberá o seguinte erro:

    The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

    Para mais informações, consulte o artigo da comunidade relevante.

<AssignMessage async="false" continueOnError="false" enabled="true" name="add-cors">
    <DisplayName>Add CORS</DisplayName>
    <FaultRules/>
    <Properties/>
    <Set>
        <Headers>
            <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header>
            <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, content-type, authorization</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
        </Headers>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

Como adicionar cabeçalhos CORS a um proxy atual

É necessário criar manualmente uma nova política "Atribuir mensagem" e copiar o código da política "Adicionar CORS" na seção anterior. Em seguida, anexe a política ao pré-fluxo de resposta do TargetEndpoint do proxy da API. É possível modificar os valores do cabeçalho conforme necessário. Para mais informações sobre como criar e anexar políticas, consulte O que é uma política?.

Como processar solicitações de pré-lançamento do CORS

Pré-lançamento do CORS refere-se ao envio de uma solicitação ao servidor para verificar se ele é compatível com o CORS. As respostas simuladas típicas incluem as origens de que o servidor aceitará solicitações CORS, uma lista de métodos HTTP compatíveis com solicitações CORS, cabeçalhos que podem ser usados como parte da solicitação de recurso e o tempo máximo de resposta de pré-lançamento armazenados em cache, entre outras. Se o serviço não indicar que é compatível com o CORS ou não quiser aceitar solicitações de origem cruzada do cliente, a política de origem cruzada do navegador será aplicada e todas as solicitações em vários domínios feitas do cliente para interagir com os recursos hospedados nesse servidor.

Normalmente, as solicitações de pré-lançamento do CORS são feitas com o método HTTP OPTIONS. Quando um servidor compatível com CORS recebe uma solicitação OPTIONS, ele retorna um conjunto de cabeçalhos CORS para o cliente que indica o nível de compatibilidade com CORS. Como resultado desse handshake, o cliente sabe o que é permitido solicitar do domínio que não é de origem.

Para mais informações, consulte a Recomendação W3C de compartilhamento de recursos entre origens (em inglês). Há também vários blogs e artigos no CORS que você pode consultar.

O Apigee não inclui uma solução de pré-lançamento do CORS pronta para uso, mas é possível implementá-la, conforme descrito nesta seção. O objetivo é que o proxy avalie uma solicitação OPTIONS em um fluxo condicional. O proxy pode enviar uma resposta apropriada de volta ao cliente.

Vamos analisar um fluxo de amostra e discutir as partes que processam a solicitação de pré-lançamento:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <Flows>
        <Flow name="OptionsPreFlight">
            <Request/>
            <Response>
                <Step>
                    <Name>add-cors</Name>
                </Step>
            </Response>
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
        </Flow>
    </Flows>

    <PreFlow name="PreFlow">
        <Request/>
        <Response/>

    </PreFlow>
    <HTTPProxyConnection>
        <BasePath>/v1/cnc</BasePath>
        <VirtualHost>default</VirtualHost>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
   </RouteRule>
   <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</ProxyEndpoint>

As principais partes desse ProxyEndpoint são as seguintes:

  • Uma RouteRule é criada para um destino NULL com uma condição para a solicitação OPTIONS. Não há um TargetEndpoint especificado. Se a solicitação OPTIONS for recebida e os cabeçalhos de solicitação de Origem e Access-Control-Request-Method não forem nulos, o proxy retornará imediatamente os cabeçalhos CORS em uma resposta ao cliente (ignorando o "back-end" de destino real padrão). Para detalhes sobre as condições de fluxo e a RouteRule, consulte Condições com variáveis de fluxo.

    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
    </RouteRule>
    
  • É criado um fluxo OptionsPreFlight que adiciona uma política "Adicionar CORS" com os cabeçalhos CORS ao fluxo se uma solicitação OPTIONS for recebida e os cabeçalhos Access-Control-Request-Method não forem nulos.

     <Flow name="OptionsPreFlight">
                <Request/>
                <Response>
                    <Step>
                        <Name>add-cors</Name>
                    </Step>
                </Response>
            <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
     </Flow>
    

Como usar a solução de CORS de amostra

Há uma solução CORS de amostra, implementada como um fluxo compartilhado, disponível no GitHub. Importe o pacote de fluxo compartilhado para seu ambiente e anexe-o usando hooks de fluxo ou diretamente aos fluxos de proxy da API. Para detalhes, consulte o arquivo README-Flow-Shared-Flow fornecido com a amostra.