Documentation for a newer release is available. View Latest

Inicio rápido de HTTP

La integración con un sistema externo vía HTTP es ligeramente diferente en comparación con JMS o Kafka. Esta guía aplica a la integración saliente (es decir, donde IPF hace solicitudes a un sistema externo); para la integración entrante vía HTTP (es decir, donde IPF recibe solicitudes de otro sistema) consulte Inicio rápido de flujo de recepción HTTP.

Para un ejemplo de uso del HTTP Connector Transport para integrarse con la API de Twitter, vea Solicitudes encadenadas Request-Reply con OAuth.

Al implementar una integración saliente vía HTTP, en lugar de definir un par de sending y receiving connectors se requiere un RequestReplySendConnector. Este único conector realiza la solicitud y también maneja la respuesta del sistema externo de forma asíncrona.

Paso 1: Agregar la dependencia connector-http

La dependencia que debe agregar a su pom.xml es:

<dependency>
    <groupId>com.iconsolutions.ipf.core.connector</groupId>
    <artifactId>connector-http</artifactId>
</dependency>

Si importa el Icon BOM, o usa el Icon BOM como padre, no es necesario proporcionar una versión por separado.

Paso 2: Config

La configuración del connector, en general, está fuertemente impulsada por configuración. La configuración nos permite especificar:

  • Host/puerto del servidor HTTP

  • Configuración de proxy

  • Parámetros de reinicio (en caso de fallo)

Aquí hay un ejemplo de un bloque de configuración para un HttpConnectorTransportConfiguration que conectaremos con nuestro ConnectorTransport, para el sistema de fraude de un banco ficticio que expone un endpoint REST:

bank.fraud-system { (1)
  http {
    client {
      host = "olaf" (2)
      endpoint-url = "/test" (3)
      proxy-host = "localhost" (4)
      ssl {
        trust-store-location = "target/test-classes/tls/client/trust.p12" (5)
        trust-store-password = "password" (6)
        trust-store-type = "PKCS12" (7)
        key-store-location = "target/test-classes/tls/client/keys.p12" (8)
        key-store-password = "password" (9)
        key-store-type = "PKCS12" (10)
        key-password = "password" (11)
      }
      buffer-size = 1
      concurrency = 1
      status-codes-treated-as-errors = [404, 5xx] (12)
      restart-settings { (13)
        min-backoff = 1s
        max-backoff = 30s
        random-factor = 0.2
        max-restarts = 200
        max-restarts-within = 12h
      }
      connection-pool { (14)
        max-connections = 5 (15)
      }
    }
  }
}
1 Esto se conoce como la ruta raíz de configuración y se referenciará en el código. Indica dónde, en la configuración de la aplicación, buscar la configuración de este Send Connector Transport
2 El nombre de host del servicio HTTP
3 La ruta del endpoint dentro del servicio
4 Nombre de host del proxy, si se usa
5 Ruta al archivo del truststore
6 Contraseña para acceder al keystore
7 El tipo de truststore a usar. Debe ser PKCS12 o JKS
8 Ruta al archivo del keystore
9 Contraseña para acceder al keystore
10 El tipo de keystore a usar. Debe ser PKCS12 o JKS
11 Contraseña para acceder al keystore
12 Códigos de estado que se tratarán como errores. También se admiten patrones de códigos estándar del tipo 4xx y 5xx.
13 Bloque de configuración opcional que anulará los RestartSettings predeterminados del stream. Ver: doc.akka.io/libraries/akka-core/current//stream/stream-error.html#delayed-restarts-with-a-backoff-operator para más detalles. Tenga en cuenta que esto solo se usa en el HttpConnectorTransportConfiguration.
14 Bloque de configuración opcional que anulará la configuración predeterminada del connection pool de Akka
15 Valor personalizado para el número máximo de conexiones, como ejemplo. La lista detallada de atributos con valores predeterminados y descripción se puede encontrar en doc.akka.io/docs/akka-http/current/client-side/configuration.html#pool-settings

Paso 3: Crear la configuración del HTTP Connector Transport

Puede omitir este paso proporcionando configRootPath a HttpConnectorTransport.Builder<T> en lugar de HttpTransportConfiguration.

Este ejemplo logra lo mismo que el ejemplo en Paso 4:

var connectorTransport = new HttpConnectorTransport.Builder<T>()
                .withActorSystem(actorSystem)
                .withName("exampleHttpConnectorTransport")
                .withConfigRootPath("bank.fraud-system")
                .withTreatErrorResponseAsFailureWhen(httpResponse -> {
                    if(httpResponse.status().intValue() == 505) {
                        return false;
                    }
                    return true;
                })
                .build();

La clase HTTP Connector Transport Configuration actúa como envoltorio para la configuración HOCON que definimos en Paso 2, la aplicación accede a la configuración a través de esta clase.

Aquí se muestra cómo crear una clase HTTP Connector Transport Configuration para configurar un HTTP Connector Transport:

var connectorTransportConfiguration = HttpConnectorTransportConfiguration.create(actorSystem.classicSystem().settings().config(), (1)
"bank.fraud-system"); (2)
1 Esto recupera la configuración de la aplicación desde el actor system. El actorSystem normalmente puede inyectarse como un bean de Spring.
2 Esto indica la ruta a la configuración desde donde la aplicación debe recuperar los valores de configuración.

Paso 4: Crear el HTTP Connector Transport

Aquí hay un ejemplo de cómo se puede crear un HttpConnectorTransport:

var connectorTransport = new HttpConnectorTransport.Builder<T>() (1)
                .withName("exampleHttpConnectorTransport") (2)
                .withActorSystem(actorSystem)
                .withConfigRootPath("example-transport-configuration") (3)
                .withTreatErrorResponseAsFailureWhen(httpResponse -> { (4)
                    if(httpResponse.status().intValue() == 505) {
                        return false;
                    }
                    return true;
                })
                .build();
1 Reemplace T con el tipo de destino que está enviando
2 Asigne un nombre significativo al ConnectorTransport
3 Proporcione una ruta raíz de configuración para su HTTP connector transport (consulte Paso 3 para un ejemplo)
4 Opcionalmente puede personalizar cómo manejar códigos de respuesta HTTP específicos. Recibe un Predicate<HttpResponse>, en el ejemplo vamos a tratar un código 505 como una respuesta exitosa. Esto podría usarse junto con la configuración de Resilience para personalizar el comportamiento de reintentos. Por ejemplo, al no tratar una respuesta como un fallo puede evitar el manejo de reintentos predeterminado. Esta configuración se puede evitar si establecemos el parámetro status-codes-treated-as-errors en nuestra ruta raíz de configuración. Los estados que establecemos allí se tratarán como errores, y los que no estén en la lista no.
si establece tanto withTreatErrorResponseAsFailureWhen como la configuración status-codes-treated-as-errors, esta última será ignorada. Si solo está verificando códigos de estado HTTP para éxito o fallo, puede usar la clave de configuración sin tener que escribir código.
Si el endpoint al que se conecta utiliza Bearer Authentication, consulte la guía de Bearer Authentication aquí

Paso 5: Crear el HTTP Receive Connector Transport

Aquí hay un ejemplo de cómo se puede crear un HttpReceiveConnectorTransport:

var configRootPath = "receive-example"; (1)
var config = actorSystem.classicSystem().settings().config().getConfig(configRootPath + ".http.receive");
var connectorTransport =  HttpReceiveConnectorTransport.builder()
        .withName("exampleHttpConnectorTransport") (2)
        .withActorSystem(actorSystem)
        .withHost(config.getString("host"))
        .withPort(config.getString("port"))
        .withSslConfiguration(HttpReceiveTransportConfiguration.SSLConfig.buildSslContextFromConfiguration(config))
        .withResponder(req -> HttpResponse.create().withStatus(202))
        .withConfigRootPath(configRootPath)
        .build();
1 Configure configRootPath
2 Asigne un nombre significativo al ConnectorTransport

Paso 6: Crear el HTTP Request Reply Send Connector

Aquí hay un ejemplo de cómo se puede crear un RequestReplySendConnector:

var connectorConfigRootPath = "ipf.example-project.transport.connector";
var transportRootPath = "ipf.example-project.transport";
var resiliencySettingsRootPath = connectorConfigRootPath + ".resiliency-settings";

var rrSendConnector = new RequestReplySendConnector.Builder<REQ_D, REQ_T, REP_D, REP_T>(
        "RRSendConnector", connectorConfigRootPath, actorSystem)
        .withConnectorTransport(new HttpConnectorTransport.Builder<REQ_D>("RRSendConnectorTransport", actorSystem, transportRootPath).build())
        .withMessageLogger(messageLogger)
        .withCorrelationIdExtractor(payload -> null)
        .withReceiveTransportMessageConverter(t -> convertToDomain(t.getPayload().toString()))
        .withTargetToDomainTypeConverter(payload -> payload)
        .withSendTransportMessageConverter(settingsApiGetAllTransportMessageConvertor)
        .build();

Paso 7: Enviar mensajes

Habiendo creado una instancia del send connector, ahora se puede usar para enviar mensajes al endpoint de destino, de la siguiente manera:

rrSendConnector.send(PROCESSING_CONTEXT, message) (1)
        .handle((response, e) -> { (2)
            if (response != null) {
                return ... // caso exitoso
            }

            if (ExceptionUtils.getRootCause(e) instanceof HttpStatusErrorException se) { (3)
                var transportMessage = se.getResponseMessage(); (4)
                var errorPayload = transportMessage.getPayload().toString();
                var mappedResponse = ... (5)
                return mappedResponse;
            } else {
                ... (6)
            }
        });
1 Enviar mensajes usando HTTP y un Request Reply Send Connector comienza igual que con los sending connectors asíncronos que usan JMS o Kafka.
2 A diferencia de JMS o Kafka, donde los mensajes que indican errores de negocio se envían a una cola/topic de respuesta preasignada, aquí los errores de negocio se devolverán instantáneamente vía la respuesta HTTP. Las respuestas HTTP que contengan códigos de estado que se tratan como errores darán como resultado CompletableFutures fallidos devueltos por el Request Reply connector. Aquí accedemos al error que causó el fallo del future vía un método handle, pero exceptionally, whenComplete o cualquiera de sus alternativas también funcionarían.
3 Las excepciones que fallan los futures tendrán como causa raíz una instancia de HttpStatusErrorException.
4 HttpStatusErrorException lleva dentro un TransportMessage que representa la respuesta de error recibida.
5 TransportMessage solo lleva el payload bruto de la respuesta, que es de tipo String o byte[]. Por eso casi siempre se necesitará un paso de mapping en sus manejadores de excepciones.
6 Su future puede fallar con una excepción que no sea HttpStatusErrorException, y estas también deben manejarse de alguna manera.