1 HTTPClient

HTTP client is paper-thin wrapper around HttpURLConnection. It supports most HTTP communication cases when you talk to REST services and your data is JSON.

  • Supports GET, POST, PUT, DELETE
  • add HTTP headers (per request, per client or globally)
  • convert params to x-www-form-urlencoded body or URI search params
  • fluent API
  • org.json support (JSONObject, JSONArray) as payload in both directions (up/down)
  • wraps all Exceptions in a WebbException (a RuntimeException)
  • automatically sets many boiler-plate HTTP headers (like 'Accept', 'Content-Type', 'Content-Length')
  • GZip-compression for uploads (POST/PUT)
  • Un-compress gzip/deflate downloads
  • supports HTTPS and enables relaxing SSL-handshake (self-signed certificates, hostname verification)
  • pass-through to "real" connection for special cases
  • option to retry the request in case of special errors (503, 504, 'connection reset by peer')
  • multi-valued parameters
  • use streams as input and output (automatically closing underlying HttpURLConnection when stream is closed)
  • Comfortable Basic Authentication
Return Method Description
HTTPClient setSSLTrustAllFactory() Disables server certificate verification. It's incompatible with the use of setSSLSocketFactory method.
HTTPClient getTrustAllCerts() Static function returning an Array of TrustManagers (TrustManager[]) that allows to disable server certificate verification in SSL/TLS Connections
HTTPClient setSSLSocketFactory(String protocol, JSKeyStore ksClient, String ksClientPassword, JSKeyStore ksCACert) Sets SSLSocket parameters:
protocol: SSL or TSL
ksClientJavaScript Keystore: Ax.ks.KeyStore() to obtain Client Certificate for authentication (Only first cert in keystore is used)
ksClientPassword: password to open client keystore
ksCACert: KeyStore with x509 Certificate to validate server certificate (Only first cert in keystore is used)
HTTPClient setSSLSocketFactory(String protocol, JSKeyStore ksClient, String ksClientPassword, TrustManager[] trustManagers) This is another constructor for setSSLSocketFactory, but allows to pass result from getTrustAllCerts() as a server certificate validator.
HTTPClient setSSLSocketFactory(String protocol, KeyStore ksClient, String ksClientPassword, KeyStore ksCACert) Constructor of setSSLSocketFactory allowing to pass pure Java Keystores instead of keystores opened from Javascript (Ax.ks.KeyStore())
HTTPClient setSSLSocketFactory(String protocol, KeyStore ksClient, String ksClientPassword, TrustManager[] trustManagers) Allows to pass a pure Java KeyStore as Client Certificate Keystore and an array of TrustManagers for server certificate trusting
HTTPClient setSSLSocketFactory(String protocol, JSKeyStore ksClient, String ksClientPassword) This is another constructor for setSSLSocketFactory, but automatically trusts all server certificates.
HTTPClient setProxy() Sets a proxy object to be used for opening the connection.
HTTPClient setDefaultHeader(String name, Object value) Set the value for a named header which is valid for all requests created by this instance.
HTTPClient setAuthorization(String username, String password) Set the authorization header (Basic authorization)
HTTPClient setBasicAuthorization(String username, String password) Set credentials for Basic authentication
HTTPClient setDigestAuthorization(String username, String password) Set credentials for Digest authentication
HTTPClient setBearerAuthorization(String token) Set the Bearer authorization header token
Request get(String uri) Creates a GET HTTP request with the specified URI.
Request put(String uri) Creates a PUT HTTP request with the specified URI.
Request post(String uri) Creates a POST HTTP request with the specified URI.
Request delete(String uri) Creates a DELETE HTTP request with the specified URI.

1.1 Request and Response

In response to get, post, put or delete operations all methods returns a Request object. The Request can be configured before getting the Response.

1.1.1 Request

Request is a builder for an HTTP request.

Return Method Description
Configure request
Request multipleValues() Turn on a mode where one parameter key can have multiple values.
Request param(String name, Object value) Set (or overwrite) a parameter. The parameter will be used to create a query string for GET-requests and as the body for POST-requests with MIME-type application/x-www-form-urlencoded
Request param(String name, Iterable values) Set (or overwrite) a parameter with multiple values.
Request param(Map<String, Object> values) Set (or overwrite) many parameters via a map.
Request header(String name, Object value) Set (or overwrite) an HTTP header value.
Request body(Object value) Set the payload for the request. Payload can be a String, a Native JSON, a JSONObject, JSONArray, byte[], File and InputStream.
Request compress() Enable compression for uploaded data.
Request useCaches(boolean useCaches) If true, the protocol is allowed to use caching whenever it can and it removes the automatic Cache-Control: no-cache & Pragma: no-cache headers inserted by defauit in HTTP request.
Request ifModifiedSince(long ifModifiedSince) A nonzero value gives a time as the number of milliseconds since January 1, 1970, GMT.
Request connectTimeout(int connectTimeout) Sets a specified timeout value, in milliseconds.
Request readTimeout(int readTimeout) Sets the read timeout to a specified timeout, in milliseconds.
Request ensureSuccess() By calling this method, the HTTP status code is checked and a HTTPException is thrown if the status code is not something like 2xx
Request retry(int retryCount, boolean waitExponential) Set the number of retries after the first request failed. When `waitExponential` is set, then there will be a exponential wait between the retries
Execute and fetch response
Response asString() Execute the request and expect the result to be convertible to String
Response asJsonObject() Execute the request and expect the result to be convertible to JSONObject.
Response asJsonArray() Execute the request and expect the result to be convertible to JSONArray.
Response asBytes() Execute the request and expect the result to be convertible to byte[].
Response asStream() Execute the request and expect the result to be convertible to InputStream.
Response asVoid() Execute the request and expect no result payload (only status-code and headers).

1.1.2 Response

Response holds data about the response message returning from HTTP request.

Return Method Description
T getBody() Returns the payload of the response converted to the given type.
Object getErrorBody() Get the body which was returned in case of error (HTTP-Code >= 400).
int getStatusCode() An int representing the three digit HTTP Status-Code.
String getStatusLine() The first line returned by the web-server, like "HTTP/1.1 200 OK".
boolean isSuccess() Was the request successful (returning a 2xx status code)?.
String getResponseMessage() Returns the text explaining the status code.
String getContentType() Returns the MIME-type of the response body.
long getDate() Returns the date when the request was created (server-time).
long getExpiration() Returns the value of the expires header field.
long getLastModified() Returns the value of the last-modified header field.
String getHeaderField (String name) Returns the value of the named header field.

1.2 HTTP Connection Examples

1.2.1 Authentication

Basic auth

Set the basic auth credentials

Copy
var result = new Ax.net.HttpClient()
			.setBasicAuthorization( "guest", "guest")
			.get("https://jigsaw.w3.org/HTTP/Basic/")
			.ensureSuccess()
			.asString();

console.log(result.getStatusCode())
200

DIGEST auth

Use Digest auth mechanism

Copy
var result = new Ax.net.HttpClient()
			.setDigestAuthorization( "guest", "guest")
			.get("https://jigsaw.w3.org/HTTP/Digest/")
			.ensureSuccess()
			.asString();

console.log(result.getStatusCode())
200

Bearer auth

Use Bearer auth mechanism

Copy
var result = new Ax.net.HttpClient()
                .setBearerAuthorization( "xxxxxxxxxxx")
                .get("yourServerIP/URL")
                .ensureSuccess()
                .asString();

    console.log(result.getStatusCode())
200

1.2.2 NASA API example (HTTP Get)

The following example connects to NASA public web service API to download the picture of the day.

Copy
<script>
    var result = new Ax.net.HttpClient()
        .get("https://api.nasa.gov/planetary/apod")
        .param("api_key", "DEMO_KEY")
        .param("hd", "false")
        .ensureSuccess()
        .asJsonObject()
        .getBody()
        // Convert Java JSONObject to map to be handled by javascript!
        .toMap();
    
    console.log(result);
    console.log(result.explanation);
    
    // Log and High resolution image URL
    var imageLURL = result.url;
    var imageHURL = result.hdurl;
    
    var image = new Ax.net.HttpClient()
        .get(imageLURL)
        .ensureSuccess()
        .asBytes();
        
    var file = new Ax.io.File("/tmp/nasa.png");
    var size = file.write(image.getBody());     
    console.log(size + " bytes written to " + file);
    
    return new Ax.sql.Blob(file);
</script>
{date=2019-02-23, media_type=image, hdurl=https://apod.nasa.gov/apod/image/1902/heic1901aTriangulum.jpg, service_version=v1, explanation=Like grains of sand on a cosmic beach, stars of the Triangulum Galaxy are resolved in this sharp mosaic from the Hubble Space Telescope's Advanced Camera for Surveys (ACS). The inner region of the galaxy spanning over 17,000 light-years is covered at extreme resolution, the second largest image ever released by Hubble. At its center is the bright, densely packed galactic core surrounded by a loose array of dark dust lanes mixed with the stars in the galactic plane. Also known as M33, the face-on spiral galaxy lies 3 million light-years away in the small northern constellation Triangulum. Over 50,000 light-years in diameter, the Triangulum Galaxy is the third largest in the Local Group of galaxies after the Andromeda Galaxy (M31), and our own Milky Way. Of course, to fully appreciate the Triangulum's stars, star clusters, and bright nebulae captured in this Hubble mosaic, you'll need to use a zoom tool., title=The Stars of the Triangulum Galaxy, url=https://apod.nasa.gov/apod/image/1902/heic1901aTriangulumS.jpg}
Like grains of sand on a cosmic beach, stars of the Triangulum Galaxy are resolved in this sharp mosaic from the Hubble Space Telescope's Advanced Camera for Surveys (ACS). The inner region of the galaxy spanning over 17,000 light-years is covered at extreme resolution, the second largest image ever released by Hubble. At its center is the bright, densely packed galactic core surrounded by a loose array of dark dust lanes mixed with the stars in the galactic plane. Also known as M33, the face-on spiral galaxy lies 3 million light-years away in the small northern constellation Triangulum. Over 50,000 light-years in diameter, the Triangulum Galaxy is the third largest in the Local Group of galaxies after the Andromeda Galaxy (M31), and our own Milky Way. Of course, to fully appreciate the Triangulum's stars, star clusters, and bright nebulae captured in this Hubble mosaic, you'll need to use a zoom tool.
341728 bytes written to /tmp/nasa.png

1.2.3 Vodafone SMS Send Example (HTTP Put)

This example uses HTTP Put to emulate a SOAP Call to the Vodafone SMS Web Service.

Copy
<script>
    var xmlcmd = `<?xml version="1.0" standalone="no"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <soapenv:Header>
                <vodh:VODHeader xmlns:vodh="http://www.vodafone.com/soap/header/">
                        <vodh:commandId>ServiceDelivery</vodh:commandId>
                        <vodh:authentication>
                                <vodh:username>XXXX</vodh:username>
                                <vodh:password>YYYY</vodh:password>
                        </vodh:authentication>
                        <vodh:service>
                                <vodh:serviceID>31900001</vodh:serviceID>
                                <vodh:serviceType>SMS</vodh:serviceType>
                        </vodh:service>
                </vodh:VODHeader>
        </soapenv:Header>
        <soapenv:Body>
                <vodb:VODBody xmlns:vodb="http://www.vodafone.com/soap/body/" version="1.0">
                        <vodb:contextID></vodb:contextID>
                        <vodb:destAddress>688191919</vodb:destAddress>
                        <vodb:subServiceId>100600124168</vodb:subServiceId>
                        <vodb:messageBody>texto de prueba</vodb:messageBody>
                        <vodb:bodyIsText>true</vodb:bodyIsText>
                        <vodb:deliveryReport>false</vodb:deliveryReport>
                        <vodb:priorityFlag>0</vodb:priorityFlag>
                        <vodb:dataCodingScheme>0</vodb:dataCodingScheme>
                        <vodb:sourceTON>0</vodb:sourceTON>
                        <vodb:destTON>1</vodb:destTON>
                        <vodb:sourceNPI>0</vodb:sourceNPI>
                        <vodb:destNPI>1</vodb:destNPI>
                        <vodb:esmClass>0</vodb:esmClass>
                        <vodb:protocolId>0</vodb:protocolId>
                </vodb:VODBody>
        </soapenv:Body>
</soapenv:Envelope>`;

    var result = new Ax.net.HttpClient()
        .post("http://serv.cdm.vodafone.es/soap/SOAPSMS")
        .ensureSuccess()
        .body(xmlcmd)
        .asString()
        .getBody();
    
    // Convert XML to JSON HashMap
    var json = new Ax.xml.XMLDocument(result).toJSON();
    console.log(result);
    console.log(json);
    // Access JSON using get
    console.log(json.get("soapenv:Envelope").get("soapenv:Body"));
    
</script>

1.2.4 Send invoice to Test AEAT SII Server

This example show how to emulate a raw SOAP by using HTTP Post sending an invoice SII notification to Spanish AEAT.

Copy
<script>
var xml = `
<T:TicketBai xmlns:T="urn:ticketbai:emision">
    <Cabecera>
        <IDVersionTBAI>1.2</IDVersionTBAI>
    </Cabecera>
    <Sujetos>
        <Emisor>
            <NIF>B62928809</NIF>
            <ApellidosNombreRazonSocial>Triathlon [GUI] S.L.</ApellidosNombreRazonSocial>
        </Emisor>
        <Destinatarios>
            <IDDestinatario>
                    <NIF>B03930393</NIF>
                <ApellidosNombreRazonSocial>ALBIR SPORT</ApellidosNombreRazonSocial>
                <CodigoPostal>48200</CodigoPostal>
                <Direccion>Polígono Montorreta 1st. ts3 crta.Elorrio</Direccion>
            </IDDestinatario>
        </Destinatarios>
            <VariosDestinatarios>N</VariosDestinatarios>
            <EmitidaPorTercerosODestinatario>N</EmitidaPorTercerosODestinatario>
    </Sujetos>
    <Factura>
        <CabeceraFactura>
                <SerieFactura>FV</SerieFactura>
            <NumFactura>602</NumFactura>
            <FechaExpedicionFactura>22-08-2021</FechaExpedicionFactura>
            <HoraExpedicionFactura>02:00:00</HoraExpedicionFactura>
        </CabeceraFactura>
        <DatosFactura>
                <FechaOperacion>22-08-2021</FechaOperacion>
            <DescripcionFactura>Varios</DescripcionFactura>
            <DetallesFactura>
                <IDDetalleFactura>
                    <DescripcionDetalle>Material auxiliar de deportes</DescripcionDetalle>
                    <Cantidad>40.00</Cantidad>
                    <ImporteUnitario>20.00</ImporteUnitario>
                    <Descuento>0.00</Descuento>
                    <ImporteTotal>800.00</ImporteTotal>
                </IDDetalleFactura>
            </DetallesFactura>
            <ImporteTotalFactura>15488.00</ImporteTotalFactura>
                <BaseImponibleACoste>0.00</BaseImponibleACoste>
            <Claves>
                <IDClave>
                    <ClaveRegimenIvaOpTrascendencia>01</ClaveRegimenIvaOpTrascendencia>
                </IDClave>
            </Claves>
        </DatosFactura>
        <TipoDesglose>
            <DesgloseTipoOperacion>
                 <Entrega>
                    <Sujeta>
                        <NoExenta>
                            <DetalleNoExenta>
                                <TipoNoExenta>S1</TipoNoExenta>
                                <DesgloseIVA>
                                    <DetalleIVA>
                                        <BaseImponible>38400.00</BaseImponible>
                                        <TipoImpositivo>21.00</TipoImpositivo>
                                        <CuotaImpuesto>8064.00</CuotaImpuesto>
                                        <TipoRecargoEquivalencia>0.00</TipoRecargoEquivalencia>
                                        <CuotaRecargoEquivalencia>0.00</CuotaRecargoEquivalencia>
                                    </DetalleIVA>
                                </DesgloseIVA>
                            </DetalleNoExenta>
                        </NoExenta>
                    </Sujeta>
                </Entrega>
            </DesgloseTipoOperacion>
        </TipoDesglose>
    </Factura>
    <HuellaTBAI>
        <EncadenamientoFacturaAnterior>
                <SerieFacturaAnterior>FV</SerieFacturaAnterior>
            <NumFacturaAnterior>602</NumFacturaAnterior>
            <FechaExpedicionFacturaAnterior>22-08-2021</FechaExpedicionFacturaAnterior>
            <SignatureValueFirmaFacturaAnterior>Bl71zArAimKe/XtZ9Z5W3fWeYCbOz0DQplt5fX4MYD6HDuVq/jC9AKuAHsuFtcpGUui3fsFJaN0LoUGAtPZud7DafnZKD5DQ3gnY</SignatureValueFirmaFacturaAnterior>
        </EncadenamientoFacturaAnterior>
        <Software>
            <LicenciaTBAI>TBAIGIPRE00000000270</LicenciaTBAI>
            <EntidadDesarrolladora>
                    <NIF>B62928809</NIF>
            </EntidadDesarrolladora>
            <Nombre>Axional ERP</Nombre>
            <Version>2021.1</Version>
        </Software>
    </HuellaTBAI>
</T:TicketBai>`;

const dbf = new Ax.xml.DocumentBuilderFactory();
    
// Firma XAdES. 
var signed_xml = new Ax.crypt.XAdES(dbf.parse(xml))
    .setSignaturePackaging(Ax.crypt.XAdES.SIGNATUREPACKAGING.ENVELOPED)
    .setDigestAlgorithm("SHA512")
    .sign(Ax.ext.user.getKeyStoreManager('DeisterTech'), Ax.ext.user.getKeyStorePassword('DeisterTech'));

// Envio a la agencia tributaria de Gipuzkoa.
const keyStore =;
var response = new Ax.net.HttpClient()
    .setSSLSocketFactory("SSL", new Ax.ks.KeyStore(Ax.ext.user.getKeyStoreManager('DeisterTech').getKeyStore()), Ax.ext.user.getKeyStorePassword('DeisterTech'))
    .post("https://tbai-prep.egoitza.gipuzkoa.eus/WAS/HACI/HTBRecepcionFacturasWEB/rest/recepcionFacturas/alta")
    .header("Content-Type", "application/xml;charset=utf-8")                
    .ensureSuccess()
    .body(objMessage.message_request)
    .asString()
    .getBody();
</script>

Calling context

Method header must be as: .header("Content-Type", "application/xml;charset=utf-8").

It´s wrong if it´s written as: .header("Content-Type", "application/xml") .header("charset", "UTF-8") .header("Accept", "application/xml")

1.2.5 List Files with DropBox REST API

This example show how to list files in DropBox Folder Using REST API.

Copy
var count_nfiles    = 0;
var dbox_result     = {};
var dbox_hasmore    = true;
var dbox_cursor     = null;

var iterations = 0;

while (dbox_hasmore == true) {
    if (iterations++ > 100)
        break;
    
    if (dbox_cursor === null) {
        var response = new Ax.net.HttpClient()
                .post("https://api.dropboxapi.com/2/files/list_folder")
                .header("Authorization", "Bearer n2zR5tiFAAAAAAAAAAUzAzUnQsR6oPE98FN7rjb2rYP")
                .header("Content-Type", "application/json")
                .body("{\"path\": \"/Aplicaciones/Sales Layer/Definitivo\",\"limit\": 200,\"recursive\": true,\"include_media_info\": false,\"include_deleted\": false,\"include_has_explicit_shared_members\": false,\"include_mounted_folders\": true,\"include_non_downloadable_files\": true}")
                .asJsonObject();
    } else {
        var response = new Ax.net.HttpClient()
                .post("https://api.dropboxapi.com/2/files/list_folder/continue")
                .header("Authorization", "Bearer n2zR5tiFAAAAAAAAAAUzAzUnQsR6oPE98FN7rjb2rY")
                .header("Content-Type", "application/json")
                .body("{\"cursor\": \"" + dbox_cursor + "\"}")
                .asJsonObject();
    }
    if (response.getStatusCode() == 200) {
        console.log(response);
        console.log(response.getBody());
        dbox_result  = response.getBody().toMap();
        dbox_cursor  = dbox_result.cursor;
        dbox_hasmore = dbox_result.has_more == undefined ? false : dbox_result.has_more;
        for (var file_obj of result.entries) {
            count_nfiles++;
            console.log(file_obj.name);
        }
        console.log("TOTAL NUMBER OF FILES: " + count_nfiles);
    } else {
        console.log("Error code " + response.getStatusCode() + "  body: " + response.getErrorBody());
        break;               
    }
}

1.2.6 HTTP SSL Connection to unknown server

Copy
<script>
    var result = new Ax.net.HttpClient()
        .setSSLTrustAllFactory()
        .get("https://restcountries.eu/rest/v2/name/united")
        .ensureSuccess()
        .asString()
        .getBody();
    
    console.log(result);
</script>
[{"name":"United States Minor Outlying Islands","topLevelDomain":[".us"],"alpha2Code":"UM","alpha3Code":"UMI","callingCodes":[""],"capital":"","altSpellings":["UM"],"region":"Americas","subregion":"Northern America","population":300,"latlng":[],"demonym":"American","area":null,"gini":null,"timezones":["UTC-11:00","UTC-10:00","UTC+12:00"],"borders":[],"nativeName":"United States Minor Outlying Islands","numericCode":"581","currencies":[{"code":"USD","name":"United States Dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"de":"Kleinere Inselbesitzungen der Vereinigten Staaten","es":"Islas Ultramarinas Menores de Estados Unidos","fr":"Îles mineures éloignées des États-Unis","ja":"合衆国領有小離島","it":"Isole minori esterne degli Stati Uniti d'America","br":"Ilhas Menores Distantes dos Estados Unidos","pt":"Ilhas Menores Distantes dos Estados Unidos","nl":"Kleine afgelegen eilanden van de Verenigde Staten","hr":"Mali udaljeni otoci SAD-a","fa":"جزایر کوچک حاشیه‌ای ایالات متحده آمریکا"},"flag":"https://restcountries.eu/data/umi.svg","regionalBlocs":[],"cioc":""},{"name":"Tanzania, United Republic of","topLevelDomain":[".tz"],"alpha2Code":"TZ","alpha3Code":"TZA","callingCodes":["255"],"capital":"Dodoma","altSpellings":["TZ","United Republic of Tanzania","Jamhuri ya Muungano wa Tanzania"],"region":"Africa","subregion":"Eastern Africa","population":55155000,"latlng":[-6.0,35.0],"demonym":"Tanzanian","area":945087.0,"gini":37.6,"timezones":["UTC+03:00"],"borders":["BDI","COD","KEN","MWI","MOZ","RWA","UGA","ZMB"],"nativeName":"Tanzania","numericCode":"834","currencies":[{"code":"TZS","name":"Tanzanian shilling","symbol":"Sh"}],"languages":[{"iso639_1":"sw","iso639_2":"swa","name":"Swahili","nativeName":"Kiswahili"},{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"de":"Tansania","es":"Tanzania","fr":"Tanzanie","ja":"タンザニア","it":"Tanzania","br":"Tanzânia","pt":"Tanzânia","nl":"Tanzania","hr":"Tanzanija","fa":"تانزانیا"},"flag":"https://restcountries.eu/data/tza.svg","regionalBlocs":[{"acronym":"AU","name":"African Union","otherAcronyms":[],"otherNames":["الاتحاد الأفريقي","Union africaine","União Africana","Unión Africana","Umoja wa Afrika"]}],"cioc":"TAN"},{"name":"United Arab Emirates","topLevelDomain":[".ae"],"alpha2Code":"AE","alpha3Code":"ARE","callingCodes":["971"],"capital":"Abu Dhabi","altSpellings":["AE","UAE"],"region":"Asia","subregion":"Western Asia","population":9856000,"latlng":[24.0,54.0],"demonym":"Emirati","area":83600.0,"gini":null,"timezones":["UTC+04"],"borders":["OMN","SAU"],"nativeName":"دولة الإمارات العربية المتحدة","numericCode":"784","currencies":[{"code":"AED","name":"United Arab Emirates dirham","symbol":"د.إ"}],"languages":[{"iso639_1":"ar","iso639_2":"ara","name":"Arabic","nativeName":"العربية"}],"translations":{"de":"Vereinigte Arabische Emirate","es":"Emiratos Árabes Unidos","fr":"Émirats arabes unis","ja":"アラブ首長国連邦","it":"Emirati Arabi Uniti","br":"Emirados árabes Unidos","pt":"Emirados árabes Unidos","nl":"Verenigde Arabische Emiraten","hr":"Ujedinjeni Arapski Emirati","fa":"امارات متحده عربی"},"flag":"https://restcountries.eu/data/are.svg","regionalBlocs":[{"acronym":"AL","name":"Arab League","otherAcronyms":[],"otherNames":["جامعة الدول العربية","Jāmiʻat ad-Duwal al-ʻArabīyah","League of Arab States"]}],"cioc":"UAE"},{"name":"United Kingdom of Great Britain and Northern Ireland","topLevelDomain":[".uk"],"alpha2Code":"GB","alpha3Code":"GBR","callingCodes":["44"],"capital":"London","altSpellings":["GB","UK","Great Britain"],"region":"Europe","subregion":"Northern Europe","population":65110000,"latlng":[54.0,-2.0],"demonym":"British","area":242900.0,"gini":34.0,"timezones":["UTC-08:00","UTC-05:00","UTC-04:00","UTC-03:00","UTC-02:00","UTC","UTC+01:00","UTC+02:00","UTC+06:00"],"borders":["IRL"],"nativeName":"United Kingdom","numericCode":"826","currencies":[{"code":"GBP","name":"British pound","symbol":"£"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"de":"Vereinigtes Königreich","es":"Reino Unido","fr":"Royaume-Uni","ja":"イギリス","it":"Regno Unito","br":"Reino Unido","pt":"Reino Unido","nl":"Verenigd Koninkrijk","hr":"Ujedinjeno Kraljevstvo","fa":"بریتانیای کبیر و ایرلند شمالی"},"flag":"https://restcountries.eu/data/gbr.svg","regionalBlocs":[{"acronym":"EU","name":"European Union","otherAcronyms":[],"otherNames":[]}],"cioc":"GBR"},{"name":"United States of America","topLevelDomain":[".us"],"alpha2Code":"US","alpha3Code":"USA","callingCodes":["1"],"capital":"Washington, D.C.","altSpellings":["US","USA","United States of America"],"region":"Americas","subregion":"Northern America","population":323947000,"latlng":[38.0,-97.0],"demonym":"American","area":9629091.0,"gini":48.0,"timezones":["UTC-12:00","UTC-11:00","UTC-10:00","UTC-09:00","UTC-08:00","UTC-07:00","UTC-06:00","UTC-05:00","UTC-04:00","UTC+10:00","UTC+12:00"],"borders":["CAN","MEX"],"nativeName":"United States","numericCode":"840","currencies":[{"code":"USD","name":"United States dollar","symbol":"$"}],"languages":[{"iso639_1":"en","iso639_2":"eng","name":"English","nativeName":"English"}],"translations":{"de":"Vereinigte Staaten von Amerika","es":"Estados Unidos","fr":"États-Unis","ja":"アメリカ合衆国","it":"Stati Uniti D'America","br":"Estados Unidos","pt":"Estados Unidos","nl":"Verenigde Staten","hr":"Sjedinjene Američke Države","fa":"ایالات متحده آمریکا"},"flag":"https://restcountries.eu/data/usa.svg","regionalBlocs":[{"acronym":"NAFTA","name":"North American Free Trade Agreement","otherAcronyms":[],"otherNames":["Tratado de Libre Comercio de América del Norte","Accord de Libre-échange Nord-Américain"]}],"cioc":"USA"},{"name":"Mexico","topLevelDomain":[".mx"],"alpha2Code":"MX","alpha3Code":"MEX","callingCodes":["52"],"capital":"Mexico City","altSpellings":["MX","Mexicanos","United Mexican States","Estados Unidos Mexicanos"],"region":"Americas","subregion":"Central America","population":122273473,"latlng":[23.0,-102.0],"demonym":"Mexican","area":1964375.0,"gini":47.0,"timezones":["UTC-08:00","UTC-07:00","UTC-06:00"],"borders":["BLZ","GTM","USA"],"nativeName":"México","numericCode":"484","currencies":[{"code":"MXN","name":"Mexican peso","symbol":"$"}],"languages":[{"iso639_1":"es","iso639_2":"spa","name":"Spanish","nativeName":"Español"}],"translations":{"de":"Mexiko","es":"México","fr":"Mexique","ja":"メキシコ","it":"Messico","br":"México","pt":"México","nl":"Mexico","hr":"Meksiko","fa":"مکزیک"},"flag":"https://restcountries.eu/data/mex.svg","regionalBlocs":[{"acronym":"PA","name":"Pacific Alliance","otherAcronyms":[],"otherNames":["Alianza del Pacífico"]},{"acronym":"NAFTA","name":"North American Free Trade Agreement","otherAcronyms":[],"otherNames":["Tratado de Libre Comercio de América del Norte","Accord de Libre-échange Nord-Américain"]}],"cioc":"MEX"}]

Calling setSSLTrustAllFactory is equivalent to create a TrustAllCerts KeyStore and pass it to setSSLSocketFactory

Copy
.setSSLTrustAllFactory()
// is equivalent to:
var kst = Ax.net.HttpClient.getTrustAllCerts();

.setSSLSocketFactory("SSL", null, null, kst)

1.2.7 HTTP SSL Post with Client Certificate Auth

HTTP Client allows to use a certificate keystore to client authentication in server.

Beaware only first certificate in keystore is used.

Copy
<script>
    // Open certificate from disktool storage
    var cert_bytes  = Ax.util.Base64.decode('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');
    
    // Also you can open direct file
    // var cert_bytes = new Ax.io.File('7USDIS113019541.p12');
    
    // Open Client Certificate Keystore from file
    var ksc = new Ax.ks.KeyStore('pkcs12').load(cert_bytes, 'TBrA7MqN');
    
    
    // Get Trust All Certificate from Servers 
    var kst = Ax.net.HttpClient.getTrustAllCerts();
    // Also, we can use a KeyStore to store trusted server certificates
    // var kst = new Ax.ks.KeyStore().load(new Ax.io.File('myCAStore.jks'), 'capass');

    var xmlmsg = `
        <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:urn="urn:wsdltypes.nmvs.eu:v3.0" xmlns:urn1="urn:types.nmvs.eu:v3.0">
           <soap:Header/>
           <soap:Body>
              <urn:SupportPingRequest>
                 <urn1:Input>Hello World!</urn1:Input>
              </urn:SupportPingRequest>
           </soap:Body>
        </soap:Envelope>
    `;

    // Create Http Client Connection
    // Define SSL Socket Parameters(SSLContext Instance as SSL, ksc =Certificate to Client authentication, Password of certificate, KeyStore for trusted server certificates)
    var result = new Ax.net.HttpClient()
        .setSSLSocketFactory("SSL", ksc, 'TBrA7MqN', kst)
        .post("https://ws-support-prod-es.nmvs.eu:8448/WS_SUPPORT_V1/SupportServiceV30")
        .body(xmlmsg)
        .ensureSuccess()
        .asString()
        .getBody();

    // Convert XML to JSON HashMap
    var json = new Ax.xml.XMLDocument(result).toJSON();
    // Access JSON using get
    console.log(json.get("soap:Envelope").get("soap:Body"));

</script>

1.2.8 Using Google Directions API

Copy
<script>

</script>

1.2.9 Reading Stock Quotes

The following example connects to quandl stock service and retrieves a CSV for FaceBook stock data. This could be acomplised by using only Ax.rs.Reader but in a more complex example you may need HTTP client control (for example, user authentication to access to URL).

Copy
<script>
    var result = new Ax.net.HttpClient()
        .get("https://www.quandl.com/api/v3/datasets/WIKI/FB/data.csv")
        .ensureSuccess()
        .asString()
        .getBody();
    // Read CSV string into ResultSet
    var rs = new Ax.rs.Reader().csv(options => {
        options.setString(result);
    });
    return rs;
</script>
+----------+-------+-------+-------+-------+-------------+-----------+-----+-------+-------+-------+-------+-------------+
|Date      |Open   |High   |Low    |Close  |Volume       |Ex-Dividend|Split|Adj. Op|Adj. Hi|Adj. Lo|Adj. Cl|Adj. Volume  |
|          |       |       |       |       |             |           |Ratio|en     |gh     |w      |ose    |             |
+----------+-------+-------+-------+-------+-------------+-----------+-----+-------+-------+-------+-------+-------------+
|2018-03-27|156.310|162.850|150.750|152.190| 76787884.000|      0.000|1.000|156.310|162.850|150.750|152.190| 76787884.000|
|2018-03-26|160.820|161.100|149.020|160.060|125438294.000|      0.000|1.000|160.820|161.100|149.020|160.060|125438294.000|
|2018-03-23|165.440|167.100|159.020|159.390| 52306891.000|      0.000|1.000|165.440|167.100|159.020|159.390| 52306891.000|
|2018-03-22|166.130|170.270|163.720|164.890| 73389988.000|      0.000|1.000|166.130|170.270|163.720|164.890| 73389988.000|
...

2 HttpRequest

The class HttpRequest is automatically declared as Ax.request and it's available from script context providing information about current HttpServletRequest that has stated the script execution.

You can access most HttpServletRequest methods from JavaScript

Copy
<script>

    console.log("    Method:" + Ax.request.getMethod());
    console.log("   Headers:" + Ax.request.getHeaderNames());
    console.log("Parameters:" + Ax.request.getParameterNames());
    console.log("Attributes:" + Ax.request.getAttributeNames());
    console.log("     Parts:" + Ax.request.getParts());
    console.log("       URI:" + Ax.request.getRequestURI());
    console.log(Ax.request.dump());

    switch(Ax.request.getHeader("content-type")) {
    	case "text/plain":
    		console.log("is text");
    		break;
    
    	case "text/xml":
    		console.log("is xml");
    		break;
    
    	default:
    		console.log("is not supported");
    		break;
    }
</script>
Method:GET
   Headers:[content-size, content-type]
Parameters:[]
Attributes:[]
     Parts:null
       URI:null
<request class='deister.axional.server.script.lib.httpservlet.StaticHttpServletRequest' from='js'>
                                 protocol : null
                               remoteAddr : 127.0.0.1
                               remoteHost : localhost
                                   scheme : http
                                   secure : false
                               serverName : null
                               serverPort : 0
                                   method : GET
                                 pathInfo : null
                           pathTranslated : null
                              queryString : null
                               remoteUser : null
                       requestedSessionId : null
             requestedSessionIdFromCookie : false
                requestedSessionIdFromURL : false
                  requestedSessionIdValid : false
                               requestURI : null
                              servletPath : null
                            userPrincipal : null
 <headers>
                             content-size : 92
                             content-type : text/xml
 </headers>
 <parameters 
 </parameters>
 <attributes
 </attributes>
</request>

2.1 Multipart Requests (aka File upload)

Multipart requests combine one or more sets of data into a single body, separated by boundaries. You typically use these requests for file uploads and for transferring data of several types in a single request. File uploads typically use the multipart/form-data media type.

To build a multipart form data entity, we use the class Ax.net.util.MultiPartEntityBuilder to add each 'part'. For instance:

Copy
let part1 = new Ax.io.File("/tmp/file1.pdf");
part1.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua");


let part2 = new Ax.io.File("/tmp/file2.pdf");
part2.write(`
Doubt thou the stars are fire;
Doubt that the sun doth move;
Doubt truth to be a liar;
But never doubt I love.
`);

// our multipart form data entity builder
let builder = new Ax.net.util.MultiPartEntityBuilder();
builder.addFieldPart("lorem_ipsum", part1, "text/plain");
builder.addFieldPart("hamlet", part2, "text/plain");

console.log(new Ax.lang.String(builder.build()))
var response = new Ax.net.HttpClient()
                            .post("http://localhost:8090/service/rest/junit_studio/test/v1/api/file_upload")
                            .header("Content-Type", "multipart/form-data;boundary=" + builder.getBoundary()) // IMPORTANT!!!
                            .header("Authorization", "Bearer " + Ax.ext.user.getJsonWebToken())
                            .body(builder.build()).
                            asString();

                           ;
console.log("getStatusCode",response.getStatusCode());    
console.log("post_message",response.getBody());      
console.log("headers",response.getHeaderFields());   
console.log("body",response.getBody());

3 HttpResponseBuilder

A class used to build Response instances that contain metadata instead of or in addition to an entity. Instance methods provide the ability to set metadata such as http status code, http headers, http response type and cache control

Copy
let data = { name :"John", familyName : "Doe"} 
return new Ax.net.HttpResponseBuilder()				
    .status(201)								
    .entity(data)					
    .header("X-test", "1234")				
    .type("application/json")								
    .cacheControl("public", 3600)
    .build();
Copy
// redirect
return new Ax.net.HttpResponseBuilder()				
     .seeOther("/api/junit_studio/v1/test/response/wrapper")
    .build();

Calling context

Ax.net.HttpResponseBuilder should only be called when the result of a JS Script execution is used as the response of an HTTP call. (E.g. In Js Scripts called by the REST API wrapper)

4 JSContainerRequestContext

Exposes a ContainerRequestContext to JS. A ContainerRequestContext is a mutable class that provides request-specific information for the filter, such as request URI, message headers, message entity or request-scoped properties. The exposed setters allow modification of the exposed request-specific information.

Calling context

JSContainerRequestContext is not exposed via Ax namespace. An instance of this class is passed as a parameter in REST api function filters.

Example

Add a header

Copy
function restApiFilterFunction(context) {
    // add a header
    context.getHeaders().add("x-myheader", "value");
    
    //
    context.abortWith(new Ax.net.HttpResponseBuilder()				
     .seeOther("/api/junit_studio/v1/test/response/wrapper").build() )
}
Example

Abort request and redirect

Copy
function restApiFilterFunction(context) {
    //
    context.abortWith(new Ax.net.HttpResponseBuilder()				
     .seeOther("/api/junit_studio/v1/test/response/wrapper").build() )
}

5 Testing HTTP

Some times we need to debug (or compare) a request send to an http server. A simple way is to setup a linux echo http server that can be used to log (echo) an http request.

You can use the following code to compile an http echo server in Java.

Copy

compile and run

$ javac server.java
$ java server
Copy

Echo server Java source code

import java.net.*;
import java.io.*;

public class server {
  public static void main(String[] argv) throws IOException {
    // Only local interface
    // String host = "127.0.0.1";
    // All server interfaces
    String host = "0.0.0.0";
    short port = 8080;

    if(argv.length >= 2)
      host = argv[1];

    if(argv.length >= 1)
      port = Short.parseShort(argv[0]);

    ServerSocket server = null;

    try {
      server = new ServerSocket(port, 0, InetAddress.getByName(host));

      System.err.println("Server listening on " + host + ":" + port + "\n");
      int read;
      byte[] buffer = new byte[8192];

      while(true) {
        Socket client = server.accept();
        System.out.println("Connection accepted from " + client.getRemoteSocketAddress());
        PrintWriter out = new PrintWriter(client.getOutputStream(), true);
        InputStream in = client.getInputStream();

        while((read = in.read(buffer)) > 0) {
          System.out.write(buffer, 0, read);
        }

        System.out.println("");

        out.write("HTTP/1.1 200 OK");
        out.close();
        in.close();
        client.close();
      }
    }
    finally {
      System.out.println("Closing");
      if(server != null)
        server.close();
    }
  }
}