Groovy and JavaScript scripts and SpEL expressions in Membrane API Gateway get the current exchange and its messages as ready-to-use built-in variables. The names are the same in all three languages, but the way to access a value differs. Functions like user() or jsonPath() are listed in Built-in Functions for Groovy, JavaScript and SpEL.
| Language | Used in |
|---|---|
| Groovy | groovy, template |
| JavaScript | javascript |
| SpEL | ${...} in setHeader, setProperty, if, log, ... |
The request POST /shop/tea?limit=10 with the header X-Customer: 42, the cookie session=abc, the body {"name":"Oolong"} and the API path /shop/{category} gives:
| Variable | Groovy | JavaScript | SpEL | Value |
|---|---|---|---|---|
header | header['X-Customer'] | header.get('X-Customer') | ${header['X-Customer']} | 42 |
cookie | cookie.session | cookie.get('session') | ${cookie.session} | abc |
param | param.limit[0] | params.get('limit')[0] | ${param.limit[0]} | 10 |
pathParam | pathParam.category | pathParam.get('category') | ${pathParam.category} | tea |
json | json.name | json.name | ${json.name} | Oolong |
body | body.toString() | body.toString() | ${body} | {"name":"Oolong"} |
property | property.tenant | property.get('tenant') | ${property.tenant} | value of the property |
flow | flow | flow | ${flow} | REQUEST |
method | method | method | ${method} | POST |
path | path | path | ${path} | /shop/tea?limit=10 |
exchange | exchange.request.uri | exchange.getRequest().getUri() | ${exchange.request.uri} | /shop/tea?limit=10 |
In the response flow:
| Variable | Groovy | JavaScript | SpEL | Value |
|---|---|---|---|---|
statusCode | statusCode | statusCode | ${statusCode} | 200 |
header | header['Content-Type'] | header.get('Content-Type') | ${header['Content-Type']} | application/json |
header.get('X-Customer'), property.put('tenant', 'demo'). Index or dot access like header['X-Customer'] returns undefined. Only json is a JavaScript object and is read with json.name.-: ${header['X-Customer']}. ${header.X-Customer} is read as a subtraction and fails.[10]. Use ${param.limit[0]} for the value.message, header, cookie, body and json refer to the message of the current flow: the request in the request flow, the response in the response flow.
| Variable | Groovy, JavaScript | SpEL |
|---|---|---|
param / params | request flow only | both flows |
response | response flow only | when a response exists |
statusCode | response flow only | when a response exists |
Put a plugin under request: or response: to run it in one flow only. A plugin directly in flow: runs in both.
| Variable | Groovy | JS | SpEL | Description |
|---|---|---|---|---|
flow | ✓ | ✓ | ✓ | REQUEST, RESPONSE or ABORT |
method | ✓ | ✓ | ✓ | HTTP method of the request |
path | ✓ | ✓ | ✓ | Request URI including the query string |
header / headers | ✓ | ✓ | ✓ | Headers of the current message |
cookie / cookies | ✓ | ✓ | ✓ | Cookies of the current message, read-only |
param / params | ✓ | ✓ | ✓ | Query parameters, each a list of values |
pathParam | ✓ | ✓ | ✓ | Path parameters from a path: uri: /shop/{category} template, read-only |
json | ✓ | ✓ | ✓ | Parsed JSON body of the current message |
body | ✓ | ✓ | ✓ | Body of the current message, converts to a string |
property / properties | ✓ | ✓ | ✓ | Exchange properties, shared by all plugins of the exchange |
props | ✓ | ✓ | Alias for property | |
message | ✓ | ✓ | ✓ | Current message: the request or the response |
request | ✓ | ✓ | ✓ | The request, in both flows |
response | ✓ | ✓ | ✓ | The response |
statusCode | ✓ | ✓ | ✓ | Status code of the response |
exchange | ✓ | ✓ | ✓ | The whole exchange: request, response, properties |
exc | ✓ | ✓ | Alias for exchange | |
gateway | ✓ | ${gateway.version}: Membrane version | ||
CONTINUE, RETURN, ABORT | ✓ | ✓ | Return values that steer the flow, see Return Values | |
spring, registry | ✓ | ✓ | Access to beans defined in the configuration |
In Groovy and JavaScript, request and response are the full Request and Response objects. In SpEL, they only offer header, body and version: ${request.header['X-Customer']}.
// Groovy
def customer = header['X-Customer'] // null if missing
header.put('X-Gateway', 'Membrane') // replaces existing values
header.remove('X-Internal')
// Groovy
def customer = header['X-Customer'] // null if missing
header.put('X-Gateway', 'Membrane') // replaces existing values
header.remove('X-Internal')
// JavaScript
var customer = header.get('X-Customer');
header.put('X-Gateway', 'Membrane');
// JavaScript
var customer = header.get('X-Customer');
header.put('X-Gateway', 'Membrane');
Header names are case-insensitive. A header that occurs more than once returns its values joined with ,: two X-Multi headers with a and b give "a, b".
The map is parsed from the Cookie header. Changing it does not change the message. To set a cookie, write the header: header.put('Set-Cookie', 'session=abc; HttpOnly').
// curl "localhost:2000?limit=10&tag=a&tag=b"
def limit = param.limit?.getAt(0) // "10", or null if missing
def tags = param.tag // ["a", "b"]// curl "localhost:2000?limit=10&tag=a&tag=b" def limit = param.limit?.getAt(0) // "10", or null if missing def tags = param.tag // ["a", "b"]
Each parameter maps to a list, because a parameter can occur more than once.
json only exists when the body of the current message is valid JSON with a JSON content type. Otherwise a script fails with No such property: json. Check first if the body may be something else:
if (binding.hasVariable('json')) { ... }
if (binding.hasVariable('json')) { ... }
- if:
test: isJSON()
flow:
- setHeader:
name: X-Name
value: ${json.name}
- if:
test: isJSON()
flow:
- setHeader:
name: X-Name
value: ${json.name}
property.customer = json.customer // Groovy: setproperty.customer = json.customer // Groovy: set
property.put('customer', json.customer); // JavaScript: set
property.put('customer', json.customer); // JavaScript: set
- setHeader:
name: X-Customer
value: ${property.customer} # SpEL: read
- setHeader:
name: X-Customer
value: ${property.customer} # SpEL: read
Properties live as long as the exchange. Use them to pass values between plugins, e.g. from a script in the request flow to one in the response flow.
In template, use property instead of properties: there, properties returns the properties of the template object itself.
Only for Groovy and JavaScript scripts. The value of the last expression decides what happens next:
| Return value | Effect |
|---|---|
CONTINUE | The flow continues with the next plugin |
RETURN | The flow turns around; a response must already exist |
ABORT | The flow is aborted |
Response | Becomes the response, the flow returns |
Request | Replaces the request |
Map, JavaScript object | Becomes the JSON body of the current message |
String, byte[] | Becomes the body of the current message |
null | The flow continues unchanged |
End a script with CONTINUE when it only reads or changes values. Otherwise the last expression may be used as the new body by accident. For example, header.put(...) returns the previous header value, a String.
header.put('X-Checked', 'true')
CONTINUE
header.put('X-Checked', 'true')
CONTINUE
For more examples, see the transformation tutorials and the scripting overview.