Mat|r permite comunicar tu aplicación con servicios externos HTTP, de manera rápida y sencilla. Utilizando una configuración visual accesible desde el editor Model que permite asociar el resultado de una llamada a un servicio HTTP a un modelo de tu aplicación.

Contenido

Creación y configuración de un nuevo Endpoint

Para crear un nuevo endpoint por medio del menu de configuración de un servicio http lo hacemos a través del botón “create new endpoint”:

URL Data

La pantalla principal de configuración muestra el menu URL Data por defecto que nos permite configurar los siguientes datos:

  1. Name: Nombre con el cual se referencia la llamada al servicio, debe ser un nombre de función de mat|r script valida.

  2. HTTP Verb: Método de petición HTTP.

    • GET
    • POST
    • PUT
    • DELETE
    • PATCH

  1. URI: dirección HTTP del servicio que deseamos consumir, donde es posible configurar parámetros a reemplazar en tiempo de ejecución, definiendo en la misma con un número identificatorio con el formato {n} con n = 0,1,2…n.

Headers

El tab Headers permite agregar, consultar y modificar información con formato clave/valor a la cabecera de la petición HTTP. Los valores configurados mediante este metodo permaneceran como parte de la ejecución de todas las llamadas al servicio, contrario a los encabezados temporales que se pueden configurar mediante el metodo setHeader.


Headers de codificación del cuerpo de la petición y del cuerpo de la respuesta

Existen 2 headers especiales en las peticiones HTTP que afectan el formato de codificación de la información enviada en el cuerpo de la petición Body Request y de la información a interpretar desde el cuerpo de la respuesta Body Response. Estos son, respectivamente:

  1. “Content-Type”, los valores soportados son:
    • application/json (por defecto)
    • application/x-www-form-urlencoded
    • multipart/form-data
  2. “Accept”, lo valores soportados son:
    • application/json (por defecto)

Parameters

Desde el tab Parameters se configuran los parámetros que se envían en la petición del servicio desde le URI o desde el body.

QueryString parameters

Una vez configurada la URL con el formato correcto como se especificó en el campo URI, necesitamos desde el tab Parameters agregar la configuración de cada uno de ellos: nombre y tipo de dato asociado. El nombre sirve solo para su descripción; el número y orden en el que fueron agregados son los que definen en dónde se reemplaza el valor correspondiente en la URI final durante la ejecución mediante el método .call() .

Request body parameters

El servicio puede requerir el envío de información por body, que se puede realizar utilizando un modelo que será mapeado y enviado en el cuerpo de la petición, codificado de acuerdo al “Content-Type” configurado en los Headers del Endpoint.
En esta sección se puede configurar el nombre del modelo parámetro en el campo “body type”, que será el tipo de dato esperado por la función setBody()

En la ejecución, se necesita pasar como argumento del metodo setBody() el valor instanciado del modelo previo a la llamada .call().
Ejemplo:

ModelParametro aModelParam = ModelParametro() //info en body request
service.nombreServicio.setBody(aModelParam)
service.nombreServicio.call()

Response

La respuesta de un servicio HTTP se asocia a un modelo o un Array definido en nuestra aplicación. Mat|r acepta como formato de mensajes de respuesta el formato JSON.

En esta sección se pueden configurar los siguientes campos:

  1. Response Type:
    • Array: Si el servicio retorna un array de objetos todos del mismo tipo.
    • Model: Si el servicio retorna un solo objeto.
    • None: Si el servicio no retorna valor o se quiere ignorar su retorno.
  2. Response Root: camino desde la raíz de la estructura retornada por el servicio hasta donde empieza el Array o Modelo de respuesta que nos interesa. Si todo el objeto retornado es útil, se deja este campo en blanco.

  3. Response Model: modelo definido en la aplicación que almacena la respuesta del servicio HTTP. Este modelo debe tener la estructura análoga en mat|r script del objeto JSON que retorna la llamada al servicio web.

  4. Response Mapping: la asociación entre los datos de la respuesta del servicio y el modelo respuesta en la aplicación se configura en esta sección, simplemente agregando el atributo del modelo a qué atributo de la respuesta pertenece y aceptando con el botón “+ MAP”.

Ejecución de un Endpoint

Una vez configurado y agregado el servicio HTTP, podemos observar su disponibilidad en la barra de la derecha en “My Endpoints”:

Una vez configurados nuestros Endpoints, estos son accesibles utilizando el namespace service, junto con el nombre del Endpoint.
Para ejecutar un determinado servicio, debemos utilizar el método call(), con tantos argumentos como se haya configurado la URI. En ese momento, los valores de los parámetros utilizados en la llamada serán asignados a los configurados, en el mismo orden.

Por ejemplo, para un endpoint “nombreServicio”, con un solo parámetro de tipo String, la llamada se debe efectuar de la siguiente forma:

ModeloRespuesta mr = service.nombreServicio.call("aStringParam")

Durante la ejecución de la llamada, en el retorno de una petición HTTP, el código de estado HTTP Status es analizado, con lo que puede ocurrir:

  1. HTTP Status se encuentre dentro del rango [200, 299]: en este caso, se interpreta que la petición fue satisfactoriamente recibida, aceptada e interpretada por el servidor. Por lo que se procede al mapeo de la respuesta al tipo de dato configurado.
  2. HTTP Status se encuentre fuera del rango [200, 299]: en este caso se produce la excepción HTTPServiceError, sobre la cual se puede indagar el código HTTP de retorno y su descripción HTTP. No se efectúa mapeo alguno.

Ejemplo de código con chequeo de HTTP Status:

try {
    ModeloRespuesta unModelo = service.nombreServicio.call()
    if (unModelo != null) {
        //Petición HTTP exitosa
    }    
} catch (e) {
    //error en la llamada http
    if (e.httpStatusCode() > 0) {
        //excepción http
        broker.ui.showAlert("HTTP ERROR, CODIGO: " + e.httpStatusCode().toString(), "Descripción: " + e.httpErrorMessage())
    } else {
        //Excepción no http
        broker.ui.showAlert("Excepción Inesperada", "Descripción: " + e.reason())
    }
}

METODOS PUBLICOS

[Model | Array] call(Basic primerArg, Basic segArg, …)

Este método toma tantos parámetros como se haya configurado la URI del endpoint siendo ejecutado. En la llamada, todos los valores de los parámetros serán asignados a los configurados, en el mismo orden. Luego se produce la llamada HTTP según todos los parámetros configurados en el Endpoint. Esta llamada es bloqueante y un indicador de carga se muestra hasta que la misma retorne devolviendo, según la configuración, un modelo o un Array.

Argumentos  
primerArg [Integer | Double | Bool | String]: primer parámetro a reemplazar para construir la URI final.
segArg [Integer | Double | Bool | String]: segundo parámetro a reemplazar para construir la URI final.
Excepciones  
WrongTypeOfArgumentsInCall – Wrong {argumentNumber} argument type. Found ‘{paramType}‘, required; ‘{expectedType}’ in call of method ‘call’. Se produce cuando uno de los argumentos de la llamada no es del tipo configurado.
WrongNumberOfArgumentsInCall – Wrong number of arguments in call of method ‘call’. Se produce cuando el número de argumentos con los que se efectúa la llamada no coincide con el número de argumentos configurados.
ModelNotFound – Model ‘{modelName}’ not found. El modelo configurado en la respuesta no existe.
HTTPServiceResponseRootNotFound – Root JSON object ‘{root key}’ not found in response when calling HTTP service ‘{serviceName}’. Se produce cuando no se encuentra el “root” configurado desde el tab Response, en la respuesta del servicio.
HTTPServiceResponseTypeMismatch – Type mismatch when parsing json response of HTTP service ‘{serviceName}’. Se produce cuando se encuentran tipos diferentes en algúna entrada de la respuesta del servicio a la esperada en el atributo del modelo a mapear.
HTTPServiceError – Error when executing HTTP service ‘{serviceName}’. HTTP Code: ‘{httpCode}’, details: ‘{reason}’. El código de respuesta HTTP no está dentro del rango [200,299], por lo que la petición HTTP no fue recibida o no fue entendida o no fue aceptada.

void setBody(Model modelCuerpo)

Configura el modelo que será mapeado y enviado en el cuerpo de la petición, codificado de acuerdo al “Content-Type” configurado en los Headers del Endpoint.

Argumentos  
modelCuerpo Model: modelo a codificar como información en el body del endpoint.
Excepciones  
WrongTypeOfArgumentsInCall – Wrong {argumentNumber} argument type. Found ‘{paramType}‘, required; ‘{expectedType}’ in call of method ‘setBody’. El argumento modelCuerpo no es de tipo Modelo.


void setHeader(String clave, String valor)

Agrega al encabezado de la petición el conjunto clave/valor de sus parámetros. Los encabezados configurados con este metodo son temporales: estarán presentes en la siguiente llamada al servicio utlizando el metodo call() y luego serán removidos.

Argumentos  
clave String: clave del encabezado.
valor String: valor para la clave.


void removeHeader(String clave)

Elimina el conjunto clave valor configurado en el encabezado de la petición para la clave parámetro.

Argumentos  
clave String: clave de header.


void setDateFormatter(DateFormatter formato)

Configura un formato de fecha para ser utilizado en el mapeo de fechas entre el resultado del servicio HTTP y el modelo sofia asociado. Si no se configura un formato se utiliza por defecto el siguiente: DateFormatter(format: “yyyy-MM-dd’T’HH:mm:ss Z”, utcTimeZoneOffset: 0 )

Argumentos  
formato DateFormatter: formato de fecha configurado.

void addMediaFile(MediaFile media, String nombre, String nombreArchivo)

Agrega el binario configurado en el argumento media al cuerpo de la petición, para enviar un archivo al servicio HTTP. Por defecto se agrega el encabezado: “content-type”,”multipart/form-data”.

Argumentos  
media MediaFile: archivo de media configurado con el binario que deseamos enviar.
nombre String: nombre asociado al binario.
nombreArchivo String: nombre de sugerencia para el servidor.

Ejemplos de Configuración de un servicio HTTP

GET:

Los siguiente pasos muestran cómo configurar un servicio HTTP utilizando el método GET que nos permite acceder a nuestra ip pública. Para esto utilizaremos la URL: https://api.ipify.org/?format={0} que recibe como parametro el tipo de formato de la respuesta.

  1. URL Data: completamos los campos name, HTTP Verb y URI.

  1. Headers: no necesitamos headers para este ejemplo.

  2. Parameters: se utiliza un solo parámetro (format={0}) que es un string llamado format, luego de configurarlo en los QueryString parameters aceptamos utilizando el boton “+ add”:

  1. Response: para configurar el tipo de respuesta, necesitamos analizar como será el mapeo del JSON de respuesta a un modelo Mat|r.
    Observando un ejemplo de JSON Respuesta:

    {
      "ip": "152.231.62.170"
    }

    Creamos un modelo que se adapta y representa la estructura del json:

Model MyIp {
    String ip
}

Así completamos en Response Type los campos type y model:

Y luego en la sección Response Mapping, configuramos el mapeo de la respuesta a nuestro modelo. Esto es: el atributo “ip” de la respuesta es asociada a el atributo del mismo nombre del modelo respuesta:

Una vez agregado el servicio lo podemos consumir desde nuestra app:


Experience Main {
    String userIp label("My ip")
    Decision getMyIp action("MainContext.firstRule") label("Run")
}

Model MyIp {
    String ip
}

RuleContext MainContext {

    Rule getMyIp {

        Main m = broker.ui.getDataSource()

        //Llamada a servicio HTTP con único parámetro "Query String" configurado de tipo String.
        MyIp mi = service.myIp.call("json")
        m.userIp = mi.ip   //Peticion HTTP exitosa
    }
}

POST

Los siguiente pasos muestran cómo configurar un servicio HTTP utilizando el método POST que nos permite enviar una imagen.

Para este ejemplo utilizaremos una URL ficticia: http://myserver.com/upload y asumiremos que el servicio retorna la URL final del archivo que fue subida, como el siguiente JSON de respuesta:

{
  "filename": "nombre de archivo guardado"
}
  1. URL Data: completamos los campos name, HTTP Verb y URI.

  1. Headers: necesitamos agregar el par “content-type”/”multipart/form-data” pero lo haremos programáticamente.

  2. Parameters: no necesitamos configurar parámetros.

  3. Response: para configurar el tipo de respuesta, necesitamos analizar como será el mapeo del JSON de respuesta a un modelo Mat|r. Observando el JSON de respuesta, definimos un modelo que se adapta y representa la estructura del json:

Model Response {
    String filename
}

Así completamos en Response Type los campos type y model:

Y luego en la sección Response Mapping, configuramos el mapeo de la respuesta a nuestro modelo. Esto es: el atributo “ip” de la respuesta es asociada al atributo del mismo nombre del modelo respuesta:

Una vez agregado el servicio podemos consumirlo desde nuestra app de la siguiente forma:

Experience Main {
    String filename label("File name")
    Decision enviarImagen action("MainContext.enviarImagen") label("Enviar imagen")
}

//Response Model to post_image endpoint
Model Response {
    String filename
}

RuleContext MainContext {

    Rule enviarImagen  {
        MediaGalleryConfigure mgc = MediaGalleryConfigure()
        MediaFile mf = broker.media.open(mgc)

        if(mf != null) {
            service.post_image.addMediaFile(mf,"image",mf.getURL())
            service.post_image.setHeader("content-type","multipart/form-data")
            Response res = Response()
            try {
                res = service.post_image.call()                
                Main m = broker.ui.getDataSource()
                m.filename = res.filename
            } catch(e) {
                broker.ui.showAlert("Ups, ha ocurrido una excepción: ", e.reason())
            }
        }
    }
}