REST and HTTP templates Pub Share

We use templates to parse HTTP requests and replies, in/out over HTTP. Let's take a look.

Note: read REST Microservices++ first. Templates are not really neccessary and a just a prototype at this point.

Incoming vs outgoing

The first thing to understand is that a particular message can be either incoming (i.e. receiving your HTTP request) or an outgoing (i.e. sending an HTTP request to a 3rd party service, but currently, it cannot be both.

If you need to simulate a message for both, like a proxy, then use an in message mapped to an out message and then each can have a different formatting template:

$msg my.api (id,transactiontype,authtype,account,exp,amt,systemcode)
$msg my.api.out (id,transactiontype,authtype,account,exp,amt,systemcode)

$when my.api (id,transactiontype,authtype,account,exp,amt,systemcode) 
=> my.api.out (id,transactiontype,authtype,account,exp,amt,systemcode)

This establishes a simple route / rule to map the "in" to the "out" and it looks like the message is both in/out.

So, each message can have one request and one response template. Depending on the message being used as in/out, there are thus four usages of templates (some templates are used to format a message and some are used to parse a message:

  • incoming, request - used to parse an incoming message request into its attributes
  • incoming, response - used to format an outgoing response from a message
  • outgoing, request - used to format an outgoing request for a message
  • outgoing, response - used to parse an incoming message response into its attributes

We attempted to use similar templates for all of these.

HTTP response properties

One way to control the response properties is to set them at the root level:

$when diesel.rest (path ~path "/v1/ems/:env/device/:deviceType/:deviceId", verb == "GET")
=> (dieselRoot["diesel.http.response.contentType"] = "application/json")
=> (dieselRoot["diesel.http.response.status"] = 512)
=> (payload = "meh, is this csv or error?")

This can also be achieved from anywhere inside the flow with the diesel.flow.return message:

$when diesel.rest (path ~path "/v1/ems/:env/device/:deviceType/:deviceId", verb == "GET")
=> diesel.flow.return(
  diesel.http.response.contentType = "application/json", 
  diesel.http.response.status      = 512
  payload = "meh, is this csv or error?")

Request templates

You can define either a pattern or specific xml/json templates for requests.

Pattern template - for incoming requests only

Here's a sample of a pattern template. Note that it looks like an XML, but in fact it is a pattern - note the (...)? optional blocks:

{{template my.api:request}}
POST ${URL}
content-type: application/xml

<TRANSACTION>
<ID>${id}</ID>
<TRANSACTIONTYPE>${transactiontype}</TRANSACTIONTYPE>
(<AUTHTYPE>${authtype}</AUTHTYPE>)?
<ACCOUNT>${account}</ACCOUNT>
<EXP>${exp}</EXP>
(<AMT>${amt}</AMT>)?
(<SYSTEMCODE>${ccsystemcode}</SYSTEMCODE>)?
<ENCODING>UTF-8</ENCODING>
</TRANSACTION>
{{/template}}

This is used to match the incoming message as regex and extract the respective parameters. This is very well suited to parsing plain text.

Note that if your xml would have the tags in a different order, they won't match anymore! Use the XML template below for that!

Also, any optional tags, need to be put inside an optional non-named capture group, as AMT above.

XML template - parsing by example

Add the content type for the template, to turn it into an XML matcher. Note that the template does not look much different, except the request matcher will not match it as text, but will parse the XML tags and match them one by one.

This is parsing by example - you mark the values you're interested in and the parser figures out how to extract them.

Note the content-type="application/xml" on the template definition, that's the important one, this indicates that the template itself should be interpreted as an XML template.

{{template my.api:request:content-type="application/xml"}}
POST ${URL}
content-type: application/xml

<TRANSACTION>
<ID>${id}</ID>
<TRANSACTIONTYPE>${transactiontype}</TRANSACTIONTYPE>
<AUTHTYPE>${authtype}</AUTHTYPE>
<ACCOUNT>${account}</ACCOUNT>
<EXP>${exp}</EXP>
<AMT>${amt}</AMT>
<SYSTEMCODE>${ccsystemcode}</SYSTEMCODE>
<ENCODING>UTF-8</ENCODING>
</TRANSACTION>
{{/template}}

Currently, only plain nodes are supported - they can be embedded nodes in a structure, but no attributes yet.

XML template - Extract parameters

There is another method to parse incoming XML (and JSON) documents, using parameter sourcing lists, like so:

{{template my.api:request:content-type="application/xml",ID=TRANSACTION.ID, AMT=TRANSACTION.AMT}}
POST ${URL}
content-type: application/xml

no body
{{/template}}

In this case, the template needs no body (since it's used for parsing) and the parameter list is evaluated, to extract values. Note that you must still respect the HTTP template format, so it can be parsed (start with a verb, url, followed by two empty lines).

JSON

JSON parsing is similar to xml, just use the content-type:application/json instead. You have both the parse by example and extract with expression options as well.

This is an example of an outgoing template which will post a form and parse the result:

$msg microsoft.getAccessToken : (accessToken)

{{template microsoft.getAccessToken:request accessToken=access_token}}
POST https://login.microsoftonline.com/${ACCESS_ID}/oauth2/token
Content-type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&resource=${RESOURCE}
{{/template}}

This will use the ${ACCESS} and ${CLIENT_ID} etc parameters to format the outgoing request and then will parse the incoming request (will automatically detect that it's a JSON document) and extract the accessToken from it.


Was this useful?    

By: Razie | 2017-08-24 .. 2021-07-29 | Tags: academy , reference


Viewed 914 times ( | History | Print ) this page.

You need to log in to post a comment!

© Copyright DieselApps, 2012-2024, all rights reserved.