Callouts REST do Apex

Objetivos de aprendizagem

Após concluir este módulo, você estará apto a:
  • Fazer um callout para receber dados de um serviço externo.
  • Fazer um callout para enviar dados a um serviço externo.
  • Testar callouts usando callouts simulados.

Noções básicas de HTTP e callouts

Os callouts REST têm HTTP como base. Para compreender como os callouts funcionam, é bom compreender algumas coisas sobre HTTP. Cada solicitação de callout está associada a um método HTTP e um ponto de extremidade. O método HTTP indica qual é o tipo de ação necessária.
Callouts do Apex para um serviço externo

O tipo mais simples de solicitação é a GET (que configura um método HTTP). Uma solicitação GET significa que o remetente quer receber informações sobre um recurso do servidor. Quando o servidor recebe e processa essa solicitação, ele envia as informações necessárias ao destinatário. As solicitações GET são como navegar para um endereço no navegador. Quando visitamos uma página da Web, o navegador faz uma solicitação GET nos bastidores. No navegador, o resultado é a exibição de uma nova página HTML. No caso dos callouts, o resultado é o objeto da resposta.

Para ilustrar como as solicitações GET funcionam, abra seu navegador e acesse esta URI: https://th-apex-http-callout.herokuapp.com/animals. O seu navegador exibirá uma lista de animais em um formato esquisito porque o serviço enviará a resposta em um formato chamado JSON. Às vezes, as respostas de uma solicitação GET também são enviadas em formato XML.

A seguir, veremos descrições de métodos HTTP comuns.

Tabela 1. Exemplos de métodos HTTP comuns
Método HTTP Descrição
GET Recuperar dados identificados por uma URL.
POST Criar um recurso ou publicar dados no servidor.
DELETE Excluir um recurso identificado por uma URL.
PUT Criar ou substituir o recurso enviado no corpo da solicitação.

Quando tiver um tempinho livre, dê uma olhada na lista completa de métodos HTTP que está na seção Recursos.

Além do método HTTP, cada solicitação define uma URI, que corresponde ao endereço do ponto de extremidade no qual o serviço fica localizado. Por exemplo, este poderia ser um ponto de extremidade: http://www.example.com/api/resource. No caso da unidade “Noções básicas de HTTP e callouts”, o ponto de extremidade é: https://th-apex-http-callout.herokuapp.com/animals.

Quando o servidor processa a solicitação, ele envia um código de estado na resposta. Esse código de estado indica se a solicitação foi processada corretamente ou se houve alguma falha. Quando a solicitação dá certo, o servidor envia o código de estado 200. Você já deve ter visto outros códigos de estado, como o 404 (para arquivos que não são encontrados) ou o 500 (para erros internos do servidor).

Caso ainda tenha um tempo após navegar pela lista de métodos HTTP, confira a lista de todos os códigos de estado de resposta na seção Recursos. Caso você não esteja conseguindo dormir direito, talvez esses dois recursos ajudem.

Nota

Nota

Além do ponto de extremidade e do método HTTP, é possível definir outras propriedades para uma solicitação. Por exemplo: uma solicitação pode conter cabeçalhos que exibem mais informações sobre ela, como o tipo de conteúdo. Ela também pode conter dados que serão enviados ao serviço, como as solicitações POST. Veremos um exemplo de uma solicitação POST mais para frente. Fazer uma solicitação POST é como clicar no botão de uma página da Web para enviar os dados de um formulário. Com um callout, você envia os dados no corpo da solicitação em vez de fazer a inserção manual das informações em uma página da Web.

Como receber dados de um serviço

Chegou a hora de colocar seu novo conhecimento sobre HTTP em prática com alguns callouts do Apex. Este exemplo envia uma solicitação GET a um serviço da Web para receber uma lista de criaturas da floresta. O serviço envia a resposta no formato JSON. O JSON é basicamente uma sequência de caracteres, então a classe integrada JSONParser converte esse formato em um objeto. Depois, poderemos usar esse objeto para escrever o nome de cada animal no registro de depuração.

Antes de executar os exemplos desta unidade, você precisará autorizar a URL do ponto de extremidade do callout, https://th-apex-http-callout.herokuapp.com, seguindo os passos indicados na seção Autorizar endereços dos pontos de extremidade.

  1. Abra o Developer Console na engrenagem de configurações (Ícone de engrenagem de configuração).
  2. No Developer Console, selecione Debug (Depurar) | Open Execute Anonymous Window (Abrir janela Executar no modo anônimo).
  3. Exclua o código existente e insira o trecho a seguir.
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
    request.setMethod('GET');
    HttpResponse response = http.send(request);
    // If the request is successful, parse the JSON response.
    if (response.getStatusCode() == 200) {
        // Deserialize the JSON string into collections of primitive data types.
        Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
        // Cast the values in the 'animals' key as a list
        List<Object> animals = (List<Object>) results.get('animals');
        System.debug('Received the following animals:');
        for (Object animal: animals) {
            System.debug(animal);
        }
    }
  4. Selecione Open Log (Abrir registro) e clique em Execute (Executar).
  5. Após a abertura do registro de depuração, selecione Debug Only (Apenas a depuração) para visualizar o resultado dos demonstrativos System.debug.
    Os nomes dos animais serão exibidos.

O JSON do nosso exemplo é bem simples e fácil de analisar. Para estruturas JSON mais complexas, use JSON2Apex. Essa ferramenta gera códigos Apex altamente tipificados para a análise de uma estrutura JSON. Basta colar o JSON no local e a ferramenta gera o código Apex do qual você precisa. Uma maravilha!

Envio de dados para um serviço

Outro caso de uso comum para os callouts HTTP é o envio de dados para um serviço. Por exemplo: quando alguém compra o álbum mais recente do Justin Bieber ou deixa um comentário no melhor vídeo de um “Gato vestido com uma fantasia de tubarão corre atrás de um pato em cima de um aspirador Roomba”, seu navegador manda uma solicitação POST para enviar esses dados. Vejamos como as informações são enviadas no Apex.

Neste exemplo, enviaremos uma solicitação POST para o serviço da Web pedindo que o nome de um animal seja adicionado. O novo nome será adicionado ao corpo da solicitação por meio de uma sequência de caracteres JSON. O cabeçalho Content-Type da solicitação está configurado para avisar o serviço de que os dados enviados estão no formato JSON, para que o processamento dos dados seja feito da forma correta. O serviço gerará uma resposta, enviando um código de estado e uma lista de todos os animais, incluindo o que você acabou de adicionar. Se a solicitação for processada corretamente, o código de estado devolve 201 devido à criação de um recurso. A resposta será enviada ao registro de depuração sempre que não for devolvido 201.

  1. Abra o Developer Console na engrenagem de configurações (Ícone de engrenagem de configuração).
  2. No Developer Console, selecione Debug (Depurar) | Open Execute Anonymous Window (Abrir janela Executar no modo anônimo).
  3. Exclua qualquer código existente e insira o trecho a seguir.
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
    request.setMethod('POST');
    request.setHeader('Content-Type', 'application/json;charset=UTF-8');
    // Set the body as a JSON object
    request.setBody('{"name":"mighty moose"}');
    HttpResponse response = http.send(request);
    // Parse the JSON response
    if (response.getStatusCode() != 201) {
        System.debug('The status code returned was not expected: ' +
            response.getStatusCode() + ' ' + response.getStatus());
    } else {
        System.debug(response.getBody());
    }
  4. Selecione Open Log (Abrir registro) e clique em Execute (Executar).
  5. Ao ocorrer a abertura do registro de depuração, selecione Debug Only (Apenas a depuração) para visualizar o resultado do demonstrativo System.debug. O último item da lista de animais será "alce possante".

Testar callouts

Temos uma notícia boa e outra ruim sobre o teste dos callouts. A notícia ruim é que os métodos de teste do Apex não dão suporte aos callouts, e os testes com callouts dão errado. A boa notícia é que o tempo de execução do teste possibilita que o usuário faça uma “simulação” do callout. Por meio dos callouts simulados, é possível especificar a resposta que será dada no teste em vez de realmente acessar o serviço da Web. É praticamente como falar o seguinte para o tempo de execução: “Sei o que esse serviço da Web me enviará. Em vez de fazer um acesso durante o teste, já envie os dados”. Usar callouts simulados nos seus testes ajuda a garantir uma cobertura de código adequada e a evitar que alguma linha seja pulada em decorrência dos callouts.

Pré-requisitos

Antes de preparar seus testes, vamos criar uma classe com os exemplos de solicitações GET e POST que executamos de forma anônima na unidade “Envio de dados para um serviço”. Esses exemplos foram um pouco modificados para as solicitações ficarem em valores de métodos e retornos, mas eles são praticamente iguais aos exemplos anteriores.

  1. No Developer Console, selecione File (Arquivo) | New (Novo) | Apex Class (Classe do Apex).
  2. Para o nome da classe, insira AnimalsCallouts e clique em OK.
  3. Substitua o código gerado automaticamente pela seguinte definição de classe.
    public class AnimalsCallouts {
    
        public static HttpResponse makeGetCallout() {
            Http http = new Http();
            HttpRequest request = new HttpRequest();
            request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
            request.setMethod('GET');
            HttpResponse response = http.send(request);
            // If the request is successful, parse the JSON response.
            if (response.getStatusCode() == 200) {
                // Deserializes the JSON string into collections of primitive data types.
                Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
                // Cast the values in the 'animals' key as a list
                List<Object> animals = (List<Object>) results.get('animals');
                System.debug('Received the following animals:');
                for (Object animal: animals) {
                    System.debug(animal);
                }
            }
            return response;
        }
    
        public static HttpResponse makePostCallout() {
            Http http = new Http();
            HttpRequest request = new HttpRequest();
            request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
            request.setMethod('POST');
            request.setHeader('Content-Type', 'application/json;charset=UTF-8');
            request.setBody('{"name":"mighty moose"}');
            HttpResponse response = http.send(request);
            // Parse the JSON response
            if (response.getStatusCode() != 201) {
                System.debug('The status code returned was not expected: ' +
                    response.getStatusCode() + ' ' + response.getStatus());
            } else {
                System.debug(response.getBody());
            }
            return response;
        }        
    
    }
  4. Pressione Ctrl+S para salvar.

Testar um callout com o StaticResourceCalloutMock

Para testar seus callouts, use callouts simulados implementando uma interface ou usando recursos estáticos. No exemplo em questão, usamos recursos estáticos e posteriormente uma interface de simulação. O recurso estático contém o corpo de resposta que será devolvido. E lembre-se: se usar um callout simulado, a solicitação não será enviada para o ponto de extremidade. Em vez disso, o tempo de execução do Apex sabe como procurar a resposta especificada no recurso estático e apresentar exatamente essa informação. O método Test.setMock informa ao tempo de execução que callouts simulados estão sendo usados no método de teste. Vejamos os callouts simulados na prática. Em primeiro lugar, criaremos um recurso estático com uma sequência de caracteres em formato JSON que será usada para a solicitação GET.

  1. No Developer Console, selecione File (Arquivo) | New (Novo) | Static Resource (Recurso estático).
  2. Para o nome, insira GetAnimalResource.
  3. Para o tipo MIME, selecione text/plain (texto/simples) (apesar de estarmos usando JSON).
  4. Clique em Submit (Enviar).
  5. Na guia que abre para o recurso, insira o conteúdo a seguir. Confira se está tudo na mesma linha, sem gerar uma quebra para a próxima. Esse é o conteúdo devolvido pelo callout simulado. Um conjunto de três criaturas da floresta.
    {"animals": ["pesky porcupine", "hungry hippo", "squeaky squirrel"]}
  6. Pressione Ctrl+S para salvar.

Pronto, você criou um recurso estático! Agora, vamos adicionar um teste para o callout que use esse recurso.

  1. No Developer Console, selecione File (Arquivo) | New (Novo) | Apex Class (Classe do Apex).
  2. Para o nome da classe, insira AnimalsCalloutsTest e clique em OK.
  3. Substitua o código gerado automaticamente pela seguinte definição de classe de teste.
    @isTest
    private class AnimalsCalloutsTest {
    
        @isTest static  void testGetCallout() {
            // Create the mock response based on a static resource
            StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
            mock.setStaticResource('GetAnimalResource');
            mock.setStatusCode(200);
            mock.setHeader('Content-Type', 'application/json;charset=UTF-8');
            // Associate the callout with a mock response
            Test.setMock(HttpCalloutMock.class, mock);
            // Call method to test
            HttpResponse result = AnimalsCallouts.makeGetCallout();
            // Verify mock response is not null
            System.assertNotEquals(null,result,
                'The callout returned a null response.');
            // Verify status code
            System.assertEquals(200,result.getStatusCode(),
              'The status code is not 200.');
            // Verify content type   
            System.assertEquals('application/json;charset=UTF-8',
              result.getHeader('Content-Type'),
              'The content type value is not expected.');  
            // Verify the array contains 3 items     
            Map<String, Object> results = (Map<String, Object>) 
                JSON.deserializeUntyped(result.getBody());
            List<Object> animals = (List<Object>) results.get('animals');
            System.assertEquals(3, animals.size(),
              'The array should only contain 3 items.');          
        }   
    
    }
  4. Pressione Ctrl+S para salvar.
  5. Selecione Test (Teste) | Always Run Asynchronously (Sempre executar de forma assíncrona). Se não selecionar a opção Sempre executar de forma assíncrona, as execuções de teste com apenas uma classe serão feitas em sincronia. É possível abrir registros a partir da guia Testes apenas para execuções de testes síncronas.
  6. Para executar o teste, selecione Test (Teste) | New Run (Nova execução).
  7. Na lista Classes de teste, selecione AnimalsCalloutsTest.
  8. Clique em Add Selected (Adicionar itens selecionados) | Run (Executar).

O resultado do teste será exibido na guia Testes com uma ID de execução de teste. Quando a execução do teste terminar, expanda a execução de teste para ver os detalhes. Agora, clique duas vezes em AnimalCallouts no painel Cobertura de código geral para ver que linhas foram incluídas nos seus testes.

Testar um callout com HttpCalloutMock

Oferecemos uma implementação da interface HttpCalloutMock para testar seus callouts de POST. Essa interface possibilita que você especifique a resposta enviada para o método respond. Sua classe de teste fará o tempo de execução do Apex enviar uma resposta falsa chamando o Test.setMock de novo. Para o primeiro argumento, passe HttpCalloutMock.class. Para o segundo argumento, passe uma nova instância de AnimalsHttpCalloutMock, que é sua interface de implementação do HttpCalloutMock. (Escreveremos AnimalsHttpCalloutMock no próximo exemplo.)

Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());

Agora, adicione a classe que implementa a interface do HttpCalloutMock para interceptar o callout. Se um callout HTTP for invocado em um contexto de teste, o callout não é realizado. Em vez disso, você receberá a resposta da simulação especificada na implementação do método respond em AnimalsHttpCalloutMock.

  1. No Developer Console, selecione File (Arquivo) | New (Novo) | Apex Class (Classe do Apex).
  2. Para o nome da classe, insira AnimalsHttpCalloutMock e clique em OK.
  3. Substitua o código gerado automaticamente pela seguinte definição de classe.
    @isTest
    global class AnimalsHttpCalloutMock implements HttpCalloutMock {
        // Implement this interface method
        global HTTPResponse respond(HTTPRequest request) {
            // Create a fake response
            HttpResponse response = new HttpResponse();
            response.setHeader('Content-Type', 'application/json');
            response.setBody('{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}');
            response.setStatusCode(200);
            return response; 
        }
    }
  4. Pressione Ctrl+S para salvar.

Na sua classe de teste, crie o método testPostCallout para configurar o callout simulado, conforme veremos no exemplo a seguir. O método testPostCallout chama Test.setMock que, por sua vez, chama o método makePostCallout na classe AnimalsCallouts. Depois, ele verifica se a resposta devolvida é o que você especificou no método de resposta da implementação simulada.

  1. Altere a classe de teste AnimalsCalloutsTest para adicionar um segundo método de teste. Clique na guia de classe e acrescente o seguinte método antes de fechar o parênteses.
    @isTest static void testPostCallout() {
        // Set mock callout class 
        Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock()); 
        // This causes a fake response to be sent
        // from the class that implements HttpCalloutMock. 
        HttpResponse response = AnimalsCallouts.makePostCallout();
        // Verify that the response received contains fake values
        String contentType = response.getHeader('Content-Type');
        System.assert(contentType == 'application/json');
        String actualValue = response.getBody();
        System.debug(response.getBody());
        String expectedValue = '{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}';
        System.assertEquals(actualValue, expectedValue);
        System.assertEquals(200, response.getStatusCode());
    }
  2. Pressione Ctrl+S para salvar.
  3. Selecione Test (Teste) | New Run (Nova execução).
  4. Na lista Classes de teste, selecione AnimalsCalloutsTest.
  5. Clique em Add Selected (Adicionar itens selecionados) | Run (Executar).

    O resultado do teste será exibido na guia Testes com uma nova ID de execução de teste. Quando a execução do teste terminar, expanda a execução de teste para ver os detalhes de ambos.

Quero saber mais...

Saiba mais sobre como usar os callouts nos acionadores e no Apex assíncrono, assim como sobre criar callouts assíncronos.

Ao fazer um callout a partir de um método, esse método espera o serviço externo enviar a resposta do callout antes de executar as próximas linhas de código. Outra opção é colocar o código do callout em um método assíncrono com uma anotação @future(callout=true) ou usar o Apex que permite a execução em fila. Dessa forma, o callout é executado em um thread separado e a execução do método de chamada não fica bloqueada.

Ao fazer um callout a partir de um acionador, o callout não deve bloquear o processo de acionamento enquanto espera por uma resposta. Para que o acionador consiga fazer um callout, o método que contém o código do callout deve ter a anotação @future(callout=true) para ser executado em um outro thread.

Continue a aprender de graça!
Inscreva-se em uma conta para continuar.
O que você ganha com isso?
  • Receba recomendações personalizadas para suas metas de carreira
  • Pratique suas habilidades com desafios práticos e testes
  • Monitore e compartilhe seu progresso com os empregadores
  • Conecte-se a orientação e oportunidades de carreira