Empiece a realizar un seguimiento de su progreso
Inicio de Trailhead
Inicio de Trailhead

Llamadas Apex REST

Objetivos de aprendizaje

Después de completar este módulo, podrá:
  • Realizar una llamada para recibir datos de un servicio externo.
  • Realizar una llamada para enviar datos a un servicio externo.
  • Probar llamadas mediante llamadas simuladas.

Fundamentos de HTTP y llamadas

Las llamadas REST se basan en HTTP. Para comprender cómo funcionan las llamadas, es útil conocer una serie de aspectos relacionados con HTTP. Cada solicitud de llamada se asocia a un método HTTP y un extremo. El método HTTP indica el tipo de acción.
Llamadas Apex a un servicio externo

Las solicitud más simple es una solicitud GET (GET es un método HTTP). Una solicitud GET implica que el remitente desea obtener información sobre un recurso del servidor. Cuando el servidor recibe y procesa esta solicitud, devuelve la información solicitada al destinatario. Una solicitud GET es similar a la acción de ir a una dirección en el navegador. Cuando visita una página web, el navegador realiza una solicitud GET en segundo plano. En el navegador, el resultado de la navegación es la apertura de una nueva página HTML. En el caso de una llamada, el resultado es el objeto de respuesta.

Para ver cómo funciona una solicitud GET, abra el navegador y vaya al siguiente URI: https://th-apex-http-callout.herokuapp.com/animals. En el navegador se muestra una lista de animales en un formato un poco extraño, ya que el servicio devuelve la respuesta en un formato llamado JSON. En algunos casos, la respuesta de GET se puede generar en formato XML.

Las siguientes son descripciones de métodos HTTP comunes.

Tabla 1. Algunos métodos HTTP comunes
Método HTTP Descripción
GET Se recuperan datos identificados mediante una dirección URL.
POST Se crea un recurso o se publican datos en el servidor.
DELETE Se elimina un recurso identificado mediante una dirección URL.
PUT Se crea o reemplaza el recurso enviado en el cuerpo de la solicitud.

Si tiene algún tiempo libre, revise la lista exhaustiva de métodos HTTP en la sección Recursos.

Además del método HTTP, cada solicitud establece un URI, que es la dirección del extremo en el que se ubica el servicio. Por ejemplo, un extremo puede ser http://www.example.com/api/resource. En el ejemplo de la unidad “Fundamentos de HTTP y llamadas”, el extremo es https://th-apex-http-callout.herokuapp.com/animals.

Cuando el servidor procesa la solicitud, envía un código de estado en la respuesta. El código de estado indica si la solicitud se ha procesado correctamente o si se han generado errores. Si la solicitud es correcta, el servidor envía el código de estado 200. Es probable que haya visto otros códigos de estado, como 404 (archivo no encontrado) o 500 (error interno del servidor).

Si aún tiene tiempo después de revisar la lista de métodos HTTP, consulte la lista de todos los códigos de estado de respuesta en la sección Recursos. Si le cuesta conciliar el sueño, hay dos recursos que pueden ayudarle.

Nota

Nota

Además del extremo y el método HTTP, puede definir otras propiedades para una solicitud. Por ejemplo, una solicitud puede contener encabezados que proporcionan más información sobre la solicitud, como el tipo de contenido. Además, una solicitud puede contener datos para su envío al servicio, como solicitudes POST. Verá un ejemplo de una solicitud POST en un paso posterior. Una solicitud POST es similar a hacer clic en un botón en una página web para enviar datos de formulario. En el caso de una llamada, envía los datos como parte del cuerpo de la solicitud en lugar de ingresar manualmente los datos en una página web.

Obtener datos de un servicio

Ha llegado el momento de que ponga en práctica sus nuevos conocimientos sobre HTTP mediante la realización de algunas llamadas Apex. En este ejemplo se envía una solicitud GET a un servicio web para obtener una lista de criaturas de la naturaleza. El servicio envía la respuesta en formato JSON. Dado que JSON es básicamente una cadena, la clase JSONParser integrada lo convierte en un objeto. A continuación, podemos usar este objeto para escribir el nombre de cada animal en el registro de depuración.

Para poder ejecutar los ejemplos de esta unidad, debe autorizar la dirección URL de extremo de la llamada, https://th-apex-http-callout.herokuapp.com, mediante los pasos que se indican en la sección Autorizar direcciones de extremo.

  1. Abra Developer Console desde el icono de engranaje de Configuración (Icono de engranaje).
  2. En Developer Console, seleccione Debug | Open Execute Anonymous Window (Depurar | Abrir ventana de ejecución anónima).
  3. Elimine el código existente e inserte el siguiente miniprograma.
    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. Seleccione Open Log (Abrir registro) y, a continuación, haga clic en Execute (Ejecutar).
  5. Una vez que se abra el registro, seleccione Debug Only (Solo depurar) para ver la salida de las declaraciones System.debug.
    Se muestran los nombres de los animales.

El formato JSON de nuestro ejemplo es bastante sencillo y fácil de analizar. En el caso de las estructuras JSON más complejas, puede usar JSON2Apex. Esta herramienta genera código Apex muy específico para el análisis de una estructura JSON. Solo tiene que pegar en JSON para que la herramienta genere el código Apex que necesita. ¡Una solución magnífica!

Enviar datos a un servicio

Otro caso de uso común de las llamadas HTTP es el envío de datos a un servicio. Por ejemplo, si compra el último álbum de Justin Bieber o comenta su vídeo favorito de un gato disfrazado de tiburón que persigue a un pato sobre un robot de limpieza, el navegador hace una solicitud POST para enviar los datos. Veamos cómo se envían los datos en Apex.

En este ejemplo se envía una solicitud POST al servicio web para agregar un nombre de animal. El nuevo nombre se agrega al cuerpo de la solicitud como una cadena JSON. El encabezado Content-Type de la solicitud se establece para permitir que el servicio sepa que los datos se envían en formato JSON para poder procesarlos correctamente. El servicio responde mediante el envío de un código de estado y una lista de todos los animales, incluido el que ha agregado. Si la solicitud se procesa correctamente, se devuelve el código de estado 201, ya que se ha creado un recurso. La respuesta se envía al registro de depuración si se devuelve un código distinto del 201.

  1. Abra Developer Console desde el icono de engranaje de Configuración (Icono de engranaje).
  2. En Developer Console, seleccione Debug | Open Execute Anonymous Window (Depurar | Abrir ventana de ejecución anónima).
  3. Elimine cualquier código existente e inserte el siguiente miniprograma.
    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. Seleccione Open Log (Abrir registro) y, a continuación, haga clic en Execute (Ejecutar).
  5. Una vez que se abra el registro, seleccione Debug Only (Solo depurar) para ver la salida de la declaración System.debug. El último elemento de la lista de animales es "mighty moose".

Probar llamadas

En el caso de las pruebas de llamadas, hay una ventaja y un inconveniente. El inconveniente es que los métodos de prueba de Apex no admiten llamadas y las pruebas para realizar las llamadas generan un error. La ventaja es que el tiempo de ejecución de las pruebas permite “simular” la llamada. Las llamadas simuladas permiten especificar la respuesta que se devuelve en la prueba en lugar de llamar realmente al servicio web. Básicamente, está indicando al tiempo de ejecución lo siguiente: “dado que sé lo que devuelve este servicio web, en lugar de llamar a dicho servicio durante las pruebas, solo necesito la devolución de estos datos”. El uso de llamadas simuladas en las pruebas contribuye a garantizar que obtiene la cobertura de código adecuada y que no se omite ninguna línea de código a causa de las llamadas.

Requisitos

Antes de escribir las pruebas, vamos a crear una clase que contenga los ejemplos de solicitudes GET y POST que hemos ejecutado de forma anónima en la unidad “Enviar datos a un servicio”. Estos ejemplos se han modificado ligeramente de modo que las solicitudes se incluyan en métodos y devuelvan valores, pero coinciden en lo esencial con los ejemplos anteriores.

  1. En Developer Console, seleccione File | New | Apex Class (Archivo | Nuevo | Clase de Apex).
  2. Para el nombre de clase, ingrese AnimalsCallouts y, a continuación, haga clic en OK (Aceptar).
  3. Sustituya el código generado automáticamente por la siguiente definición de clase.
    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. Pulse CTRL+S para guardar.

Probar una llamada con StaticResourceCalloutMock

Para probar las llamadas, use llamadas simuladas mediante la implementación de una interfaz o el uso de recursos estáticos. En este ejemplo, usamos recursos estáticos y una interfaz simulada más adelante. El recurso estático contiene el cuerpo de la respuesta que se devuelve. De nuevo, al usar una llamada simulada, la solicitud no se envía al extremo. En lugar de esto, el tiempo de ejecución de Apex sabe buscar la respuesta especificada en el recurso estático y la devuelve en su lugar. El método Test.setMock informa al tiempo de ejecución de que se usan llamadas simuladas en el método de prueba. Veamos las llamadas simuladas en acción. En primer lugar, creamos un recurso estático que contiene una cadena en formato JSON para su uso en la solicitud GET.

  1. En Developer Console, seleccione File | New | Static Resource (Archivo | Nuevo | Recurso estático).
  2. Para el nombre, ingrese GetAnimalResource.
  3. Para el tipo MIME, seleccione text/plain (texto/sin formato), aunque estemos usando JSON.
  4. Haga clic en Submit (Enviar).
  5. En la ficha que se abre para el recurso, inserte el siguiente contenido. Asegúrese de que todo el contenido se incluye en una sola línea y no se fragmenta en la siguiente línea. Este contenido es lo que devuelve la llamada simulada. Se trata de una matriz de tres criaturas de la naturaleza.
    {"animals": ["pesky porcupine", "hungry hippo", "squeaky squirrel"]}
  6. Pulse CTRL+S para guardar.

Ha creado el recurso estático correctamente. Ahora, vamos a agregar una prueba para la llamada que usa este recurso.

  1. En Developer Console, seleccione File | New | Apex Class (Archivo | Nuevo | Clase de Apex).
  2. Para el nombre de clase, ingrese AnimalsCalloutsTest y, a continuación, haga clic en OK (Aceptar).
  3. Sustituya el código generado automáticamente por la siguiente definición de clase de prueba.
    @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. Pulse CTRL+S para guardar.
  5. Seleccione Test (Prueba) | Always Run Asynchronously (Ejecutar siempre de forma asíncrona). Si no selecciona Always Run Asynchronously (Ejecutar siempre de forma asíncrona), las ejecuciones de pruebas que incluyen una sola clase se realizan de forma síncrona. Puede abrir los registros en la ficha Tests (Pruebas) solo para las ejecuciones de pruebas síncronas.
  6. Para ejecutar la prueba, seleccione Test (Prueba) | New Run (Nueva ejecución).
  7. En la lista de clases de prueba, seleccione AnimalsCalloutsTest.
  8. Haga clic en Add Selected (Agregar selección) | Run (Ejecutar).

El resultado de la prueba se muestra en la ficha Tests (Pruebas) debajo de un Id. de ejecución de prueba. Cuando finalice la ejecución de la prueba, expanda esta ejecución de prueba para ver los detalles. A continuación, haga doble clic en AnimalCallouts en el panel Overall Code Coverage (Cobertura de código total) para ver qué líneas cubren las pruebas.

Probar una llamada con HttpCalloutMock

Para probar la llamada POST, proporcionamos una implementación de la interfaz HttpCalloutMock. Esta interfaz le permite especificar la respuesta que se envía en el método respond. La clase de prueba indica al tiempo de ejecución de Apex que envíe esta respuesta emulada mediante otra llamada a Test.setMock. Para el primer argumento, pase HttpCalloutMock.class. Para el segundo argumento, pase una nueva instancia de AnimalsHttpCalloutMock, que es la implementación de interfaz de HttpCalloutMock. (Escribiremos AnimalsHttpCalloutMock en el ejemplo posterior a este).

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

Ahora, vamos a agregar la clase que implementa la interfaz HttpCalloutMock para interceptar la llamada. Si se invoca una llamada HTTP en el contexto de prueba, la llamada no se realiza. En su lugar, recibe una respuesta simulada que debe especificar en la implementación del método respond en AnimalsHttpCalloutMock.

  1. En Developer Console, seleccione File | New | Apex Class (Archivo | Nuevo | Clase de Apex).
  2. Para el nombre de clase, ingrese AnimalsHttpCalloutMock y, a continuación, haga clic en OK (Aceptar).
  3. Sustituya el código generado automáticamente por la siguiente definición de clase.
    @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. Pulse CTRL+S para guardar.

En la clase de prueba, cree el método testPostCallout para establecer la llamada simulada, como se muestra en el siguiente ejemplo. El método testPostCallout llama a Test.setMock y, a continuación, llama al método makePostCallout en la clase AnimalsCallouts. Después, verifica si la respuesta que se devuelve es lo que ha especificado en el método de respuesta de la implementación simulada.

  1. Modifique la clase de prueba AnimalsCalloutsTest para agregar un segundo método de prueba. Haga clic en la ficha Class (Clase) y agregue el siguiente método antes del corchete de cierre.
    @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. Pulse CTRL+S para guardar.
  3. Seleccione Test (Prueba) | New Run (Nueva ejecución).
  4. En la lista de clases de prueba, seleccione AnimalsCalloutsTest.
  5. Haga clic en Add Selected (Agregar selección) | Run (Ejecutar).

    El resultado de la prueba se muestra en la ficha Tests (Pruebas) debajo de un nuevo Id. de ejecución de prueba. Cuando finalice la ejecución de la prueba, expanda la ejecución de prueba para ver los detalles de ambas pruebas.

Más información...

Obtenga información sobre cómo usar las llamadas en desencadenadores y en Apex asíncrono, y sobre cómo realizar llamadas asíncronas.

Cuando realiza una llamada desde un método, el método espera a que el servicio externo devuelva la respuesta de la llamada antes de ejecutar las líneas de código posteriores. De forma alternativa, puede incluir el código de llamada en un método asíncrono anotado con @future(callout=true) o usar Apex apto para la inclusión en cola. De este modo, la llamada se ejecuta en un subproceso independiente y la ejecución del método de llamada no se bloquea.

Cuando se realiza una llamada desde un desencadenador, la llamada no debe bloquear el proceso del desencadenador mientras se espera la respuesta. Para que el desencadenador pueda realizar una llamada, el método que contiene el código de llamada se debe anotar con @future(callout=true) para la ejecución en un subproceso independiente.

Recursos

Nota

Nota

Recuerde, este módulo está pensado para Salesforce Classic. Cuando inicie su organización para realizar prácticas, cambie a Salesforce Classic para completar este reto.