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.
Siga el proceso con Trail Together
¿Desea seguir el proceso con un experto a medida que realiza este paso? Eche un vistazo a este video, parte de la serie Trail Together en Trailhead Live.
(Este video comienza en el minuto 07:54, en caso de que desee rebobinar y mirar el comienzo del paso nuevamente).
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.
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.
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.
- Abra Developer Console desde Configuración ().
- En Developer Console, seleccione Debug (Depurar) | Execute Anonymous Window (Abrir ventana de ejecución anónima).
- 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); } }
- Seleccione Open Log (Abrir registro) y, a continuación, haga clic en Execute (Ejecutar).
- 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 una herramienta JSON2Apex. Solo tiene que pegar en JSON para que la herramienta genere el código Apex que necesita. Estas herramientas generan código Apex muy específico para el análisis de una estructura JSON. ¡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 video 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.
- Abra Developer Console desde Configuración ().
- En Developer Console, seleccione Debug (Depurar) | Execute Anonymous Window (Abrir ventana de ejecución anónima).
- 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()); }
- Seleccione Open Log (Abrir registro) y, a continuación, haga clic en Execute (Ejecutar).
- Una vez que se abra el registro de depuración, 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.
- En Developer Console, seleccione File (Archivo) | New (Nuevo) | Apex Class (Clase de Apex).
- Para el nombre de clase, ingrese
AnimalsCallouts
y, a continuación, haga clic en OK (Aceptar).
- 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; } }
- Presione 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.
- En Developer Console, seleccione File (Archivo) | New (Nuevo) | Static Resource (Recurso estático).
- Para el nombre, ingrese
GetAnimalResource
.
- Para el tipo MIME, seleccione text/plain (texto/sin formato), aunque estemos usando JSON.
- Haga clic en Submit (Enviar).
- En la ficha que se abre para el recurso, inserte el siguiente contenido. 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"]}
[Asegúrese de que todo el contenido se incluye en una sola línea y no se fragmenta en la siguiente línea].
- Presione CTRL+S para guardar.
Ha creado el recurso estático correctamente. Ahora, vamos a agregar una prueba para la llamada que usa este recurso.
- En Developer Console, seleccione File (Archivo) | New (Nuevo) | Apex Class (Clase de Apex).
- Para el nombre de clase, ingrese
AnimalsCalloutsTest
y, a continuación, haga clic en OK (Aceptar).
- 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 Assert.areNotEqual(null,result, 'The callout returned a null response.'); // Verify status code Assert.areEqual(200,result.getStatusCode(), 'The status code is not 200.'); // Verify content type Assert.areEqual('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'); Assert.areEqual(3, animals.size(), 'The array should only contain 3 items.'); } }
- Presione CTRL+S para guardar.
- Seleccione Test | Always Run Asynchronously (Prueba - Ejecutar siempre de forma asíncrona).
Si no selecciona Always Run Asynchronously, las ejecuciones de pruebas que incluyen una sola clase se realizan de forma síncrona. Puede abrir los registros desde la ficha Tests (Pruebas) solo para las ejecuciones de pruebas asíncronas.
- Para ejecutar la prueba, seleccione Test (Prueba) | New Run (Nueva ejecución).
- En la lista de clases de prueba, seleccione AnimalsCalloutsTest.
- 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
.
- En Developer Console, seleccione File (Archivo) | New (Nuevo) | Apex Class (Clase de Apex).
- Para el nombre de clase, ingrese
AnimalsHttpCalloutMock
y, a continuación, haga clic en OK (Aceptar).
- 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; } }
- Presione 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 especificó en el método de respuesta
de la implementación simulada.
- 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'); Assert.isTrue(contentType == 'application/json'); String actualValue = response.getBody(); System.debug(response.getBody()); String expectedValue = '{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}'; Assert.areEqual(expectedValue, actualValue); Assert.areEqual(200, response.getStatusCode()); }
- Presione CTRL+S para guardar.
- Seleccione Test (Prueba) | New Run (Nueva ejecución).
- En la lista de clases de prueba, seleccione AnimalsCalloutsTest.
- Haga clic en Add Selected | Run (Agregar selección - 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
- Documentos web MDN: métodos de solicitud HTTP
- Documentos web MDN: códigos de estado de respuesta de HTTP
- Guía del desarrollador de Apex: Invocar llamadas usando Apex
- Wikipedia: Representational state transfer
- W3C Note: Simple Object Access Protocol (SOAP) 1.1