Response Templates

MockServer supports the following response template formats:

  • mustache templates
    • uses mustache syntax
    • simple and easy to use
    • simple conditional logic
    • works on all JVM versions
  • velocity templates
    • uses Apache Velocity
    • more complex to use
    • supports more complex logic
    • works on all JVM versions
  • javascript templates
    • use Nashorn JavaScript engine
    • more complex to use
    • supports more complex logic
    • requires Nashorn deprecated in Java 11 and removed from 15 onwards
 

Response Template Testing

To test response templates locally is it possible to use the org.mockserver.templates.ResponseTemplateTester for fast feedback and iteration

mustache templates can be tested locally as follows:

// inputs
String template = "{\n" +
    "    'statusCode': 200,\n" +
    "    'body': \"{'method': '{{ request.method }}', 'path': '{{ request.path }}', 'headers': '{{ request.headers.host.0 }}'}\"\n" +
    "}";
HttpRequest request = request()
    .withPath("/somePath")
    .withMethod("POST")
    .withHeader(HOST.toString(), "mock-server.com")
    .withBody("some_body");

// execute
HttpResponse httpResponse = ResponseTemplateTester.testVelocityTemplate(template, request);

// result
System.out.println("httpResponse = " + httpResponse);

velocity templates can be tested locally as follows:

// inputs
String template = "{\n" +
    "    'statusCode': 200,\n" +
    "    'body': \"{'method': '$request.method', 'path': '$request.path', 'headers': '$request.headers.host[0]'}\"\n" +
    "}";
HttpRequest request = request()
    .withPath("/somePath")
    .withMethod("POST")
    .withHeader(HOST.toString(), "mock-server.com")
    .withBody("some_body");

// execute
HttpResponse httpResponse = ResponseTemplateTester.testVelocityTemplate(template, request);

// result
System.out.println("httpResponse = " + httpResponse);

javascript templates can be tested locally as follows:

// inputs
String template = "return {\n" +
    "    'statusCode': 200,\n" +
    "    'body': '{\\'method\\': \\'' + request.method + '\\', \\'path\\': \\'' + request.path + '\\', \\'headers\\': \\'' + request.headers.host[0] + '\\'}'\n" +
    "};";
HttpRequest request = request()
    .withPath("/somePath")
    .withMethod("POST")
    .withHeader(HOST.toString(), "mock-server.com")
    .withBody("some_body");

// execute
HttpResponse httpResponse = ResponseTemplateTester.testVelocityTemplate(template, request);

// result
System.out.println("httpResponse = " + httpResponse);
 

Request Model Variables

All templates formats have access to the following request fields

  • method
    mustache: {{ request.method }}
    velocity: $!request.method
    javascript: request.method
  • path
    mustache: {{ request.path }}
    velocity: $!request.path
    javascript: request.path
  • path parameters  (see below for multi-value map access patterns)
    mustache: {{ request.pathParameters.<key&ft;.<index&ft; }}
    velocity: $!request.pathParameters[<key&ft;][<index&ft;]
    javascript: request.pathParameters[<key&ft;][<index&ft;]
  • query string parameters  (see below for multi-value map access patterns)
    mustache: {{ request.queryStringParameters.<key&ft;.<index&ft; }}
    velocity: $!request.queryStringParameters[<key&ft;][<index&ft;]
    javascript: request.queryStringParameters[<key&ft;][<index&ft;]
  • headers  (see below for multi-value map access patterns)
    mustache: {{ request.headers.<key&ft;.<index&ft; }}
    velocity: $!request.headers[<key&ft;][<index&ft;]
    javascript: request.headers[<key&ft;][<index&ft;]
  • cookies  (see below for single-value map access patterns)
    mustache: {{ request.cookies.<key&ft; }}
    velocity: $!request.cookies[<key&ft;]
    javascript: request.cookies[<key&ft;]
  • body
    mustache: {{ request.body }}
    velocity: $!request.body
    javascript: request.body
  • secure
    mustache: {{ request.secure }}
    velocity: $!request.secure
    javascript: request.secure
  • client certificate chain

    During TLS connections MockServer requests clients to optionally present certificates i.e. optional mTLS.
    If the client presents certificates this field is populated with a list of the clients certificates.
    Each item in the list contains the following fields:

    • issuerDistinguishedName as a string
    • subjectDistinguishedName as a string
    • serialNumber as a string
    • signatureAlgorithmName as a string
    • certificate as a java.security.cert.X509Certificate
    mustache: {{ request.clientCertificateChain }}
    velocity: $!request.clientCertificateChain
    javascript: request.clientCertificateChain
  • remote address

    The address of the TCP connection to MockServer; typically the client's address, unless there is a NAT device or load balancer between the client and MockServer.

    mustache: {{ request.remoteAddress }}
    velocity: $!request.remoteAddress
    javascript: request.remoteAddress
  • keep alive

    True is the client requested HTTP keep alive.

    mustache: {{ request.keepAlive }}
    velocity: $!request.keepAlive
    javascript: request.keepAlive
 

Request Multi-Value And Single Value Maps

The request variable contains the follow multi-value maps fields:

  • request.headers
  • request.pathParameters
  • request.queryStringParameters

The request variable contains the follow single-value maps fields:

  • request.cookies

These multi-value or single-value maps can be accessed in the following ways:

  • values by key
    mustache: {{ request.headers.<key&ft;.<index&ft; }}
    {{ request.queryStringParameters.<key&ft;.<index&ft; }}
    {{ request.headers.<key&ft;.<index&ft; }}
    velocity: $!request.pathParameters[<key&ft;][<index&ft;]
    $!request.queryStringParameters[<key&ft;][<index&ft;]
    $!request.headers[<key&ft;][<index&ft;]
    javascript: request.pathParameters[<key&ft;][<index&ft;]
    request.queryStringParameters[<key&ft;][<index&ft;]
    request.headers[<key&ft;][<index&ft;]
  • entries as set
    each entry has a key and a value field
    for multi-value maps the value is a list
    for single-value maps the value is a string
    mustache: {{ request.pathParameters.entrySet }}
    {{ request.queryStringParameters.entrySet }}
    {{ request.headers.entrySet }}
    velocity: $!request.pathParameters.entrySet
    $!request.queryStringParameters.entrySet
    $!request.headers.entrySet
    javascript: not available
  • values as list
    mustache: {{ request.pathParameters.values }}
    {{ request.queryStringParameters.values }}
    {{ request.headers.values }}
    velocity: $!request.pathParameters.values
    $!request.queryStringParameters.values
    $!request.headers.values
    javascript: for (pathParameterKey in request.pathParameters) { var values = request.pathParameters[pathParameterKey]; }
    for (queryStringParameterKey in request.queryStringParameters) { var values = request.queryStringParameters[queryStringParameterKey]; }
    for (headerKey in request.headers) { var values = request.headers[headerKey]; }
  • keys as set
    mustache: {{ request.pathParameters.keySet }}
    {{ request.queryStringParameters.keySet }}
    {{ request.headers.keySet }}
    velocity: $!request.pathParameters.keySet
    $!request.queryStringParameters.keySet
    $!request.headers.keySet
    javascript: for (pathParameterKey in request.pathParameters) { // ... }
    for (queryStringParameterKey in request.queryStringParameters) { // ... }
    for (headerKey in request.headers) { // ... }
 

Dynamic Model Variables

All templates formats also have access to the following built-in dynamic variables. They return unique values each time they are used.

  • date & time in ISO-8601 i.e. 2011-12-03T10:15:30Z
    mustache: {{ now_iso_8601 }}
    velocity: $!now_iso_8601
    javascript: now_iso_8601
  • unix epoch, the number of seconds that have elapsed since January 1, 1970
    mustache: {{ now_epoch }}
    velocity: $!now_epoch
    javascript: now_epoch
  • date & time in RFC 1123 i.e. Tue, 3 Jun 2008 11:05:30 GMT
    mustache: {{ now_rfc_1123 }}
    velocity: $!now_rfc_1123
    javascript: now_rfc_1123
  • uuid as string
    mustache: {{ uuid }}
    velocity: $!uuid
    javascript: uuid
  • random integer between 0 and 9 inclusive
    mustache: {{ rand_int_10 }}
    velocity: $!rand_int_10
    javascript: rand_int_10
  • random integer between 0 and 99 inclusive
    mustache: {{ rand_int_100 }}
    velocity: $!rand_int_100
    javascript: rand_int_100
  • random data 16 bytes long
    mustache: {{ rand_bytes_16 }}
    velocity: $!rand_bytes_16
    javascript: rand_bytes_16
  • random data 32 bytes long
    mustache: {{ rand_bytes_32 }}
    velocity: $!rand_bytes_32
    javascript: rand_bytes_32
  • random data 64 bytes long
    mustache: {{ rand_bytes_64 }}
    velocity: $!rand_bytes_64
    javascript: rand_bytes_64
  • random data 128 bytes long
    mustache: {{ rand_bytes_128 }}
    velocity: $!rand_bytes_128
    javascript: rand_bytes_128
 

Mustache Response Templates

The following shows a basic example for a mustache format response template

new ClientAndServer(1080)
    .when(request().withPath("/some/path"))
    .respond(
        template(
            HttpTemplate.TemplateType.MUSTACHE,
            "{\n" +
                "     'statusCode': 200,\n" +
                "     'cookies': {\n" +
                "          'session': '{{ request.headers.Session-Id.0 }}'\n" +
                "     },\n" +
                "     'headers': {\n" +
                "          'Client-User-Agent': [ '{{ request.headers.User-Agent.0 }}' ]\n" +
                "     },\n" +
                "     'body': '{{ request.body }}'\n" +
                "}"
        )
    );

Mustache Syntax

Variables

In mustache a variable is referenced using double braces, for example, {{ request.method }} will print the method field of the request variable.

{{ name }} will try to find the name key in the current model context; which can be changed, using sections, as described below.

If no matching field is found an empty string will be returned.

The following basic template example demonstrates using multiple variables:

{
  'statusCode': 200,
  'body': {
    'method': '{{ request.method }}',
    'path': '{{ request.path }}',
    'headers': '{{ request.headers.host.0 }}'
  }
}

Sections

Sections have the following uses:

  • to change the model context
  • as an if-else-statement
  • to loop over arrays or iterables

A section begins with a tag starting with a pound and ends with a matching tag starting with a slash.

For example {{#request.cookies}} starts a request.cookies section and {{/request.cookies}} closes the section.

How a section behaves depends on the section's model variable as follows

  • boolean values enable or disable the section like an if-statement; with unresolvable variables, null variables or empty strings all being treated as false
  • arrays, iterators, and iterable variables cause the section to loop over each item; with empty collections never executing the section
  • other objects result in a single execution of the section using the section's model variable as the context

The following example template demonstrates using a section to iterate the entrySet in the header map:

{
  'statusCode': 200,
  'body': "{'headers': '{{#request.headers.entrySet}}{{ key }}={{ value.0 }} {{/request.headers.entrySet}}'}"
}

And produces the following example output:

{
  "statusCode" : 200,
  "body" : "{'headers': 'host=mock-server.com content-type=plain/text '}"
}

Inverted Sections

An inverted section begins with a tag starting with a caret and ends with a matching tag starting with a slash.

For example {{^-first}} starts a -first section and {{/-first}} closes the section. This section therefore applies for all items in a list except the first item.

Inverted sections render once based on the inverse value of the key, they will be rendered if the key doesn't exist, is false, is an empty string, or is an empty list.

The following example template demonstrates using a section to iterate the entrySet in the header map using an inverted section and the special variable -first to add commas before all headers except the first:

{
  'statusCode': 200,
  'body': "{'headers': [{{#request.headers.entrySet}}{{^-first}}, {{/-first}}'{{ key }}={{ value.0 }}'{{/request.headers.entrySet}}]}"
}

And produces the following example output:

{
  "statusCode" : 200,
  "body" : "{'headers': ['host=mock-server.com', 'content-type=plain/text']}"
}

Special Variables

this

this refers to the context object itself such as the current item when iterating over a collection

-first and -last

You can use the special variables -first and -last when using collections.

-first resolves to true when inside a section on the first iteration, it resolves to false at all other times. It resolves to false for sections with a singleton value rather a collection.

-last resolves to true when inside a section on the last iteration, it resolves to false at all other times.

-index

The -index special variable resolves to 1 for the first iteration, 2 for the second so on. It resolves to 0 at all other times including sections with a singleton value rather a collection.

The following example template demonstrates using this and -first to list header values

{
    'statusCode': 200,
    'body': "{'headers': [{{#request.headers.values}}{{^-first}}, {{/-first}}'{{ this.0 }}'{{/request.headers.values}}]}"
}

And produces the following example output:

{
  "statusCode" : 200,
  "body" : "{'headers': ['mock-server.com', 'plain/text']}"
}

The following example template demonstrates using this, -first and -index to list header keys

{
    'statusCode': 200,
    'body': "{'headers': [{{#request.headers.keySet}}{{^-first}}, {{/-first}}'{{ -index }}:{{ this }}'{{/request.headers.keySet}}]}"
}

And produces the following example output:

{
  "statusCode" : 200,
  "body" : "{'headers': ['1:host', '2:content-type']}"
}

JsonPath

It is possible to use JsonPath expressions to extract values from request bodies containing JSON with a {{#jsonPath}} section, follow by a {{#jsonPathResult}} section

For details of the full JsonPath syntax please see github.com/json-path

The following example template demonstrates using {{#jsonPath}} follow by {{#jsonPathResult}} to extract a list and single value from a request body:

{
    'statusCode': 200,
    'body': "{'titles': {{#jsonPath}}$.store.book{{/jsonPath}}[{{#jsonPathResult}}{{^-first}}, {{/-first}}'{{title}}'{{/jsonPathResult}}], 'bikeColor': '{{#jsonPath}}$.store.bicycle.color{{/jsonPath}}{{jsonPathResult}}'}"
}

In this example the first JsonPath expression $.store.book returns a list of objects which is iterated over in the section {{#jsonPathResult}}.

The second JsonPath expression $.store.bicycle.color returns a single value which is returned using the variable tag {{jsonPathResult}}.

Given the following request:

{
  "path" : "/somePath",
  "body" : {
    "store" : {
      "book" : [ {
        "category" : "reference",
        "author" : "Nigel Rees",
        "title" : "Sayings of the Century",
        "price" : 18.95
      }, {
        "category" : "fiction",
        "author" : "Herman Melville",
        "title" : "Moby Dick",
        "isbn" : "0-553-21311-3",
        "price" : 8.99
      } ],
      "bicycle" : {
        "color" : "red",
        "price" : 19.95
      }
    },
    "expensive" : 10
  }
}

The example produces:

{
  "statusCode" : 200,
  "body" : "{'titles': ['Sayings of the Century', 'Moby Dick'], 'bikeColor': 'red'}"
}

XPath

It is possible to use XPath expressions to extract values from request bodies containing XML with a {{#xPath}} section.

The {{#xPath}} section only supports outputting the result of the XPath expression as a string, if an XML fragment is matched the string content of all the elements is printed.

For a quick summary the XPath syntax please see w3schools, for details of the full XPath syntax please see www.w3.org

The following example template demonstrates using {{#xPath}} to multiple items from a request body:

{
    'statusCode': 200,
    'body': "{'titles': ['{{#xPath}}/store/book/title{{/xPath}}', '{{#xPath}}//book[2]/title{{/xPath}}'], 'bikeColor': '{{#xPath}}//bicycle/color{{/xPath}}'}"
}

Given a request with the following xml body:

<?xml version="1.0" encoding="UTF-8" ?&ft;
<store&ft;
  <book&ft;
    <category&ft;reference</category&ft;
    <author&ft;Nigel Rees</author&ft;
    <title&ft;Sayings of the Century</title&ft;
    <price&ft;18.95</price&ft;
  </book&ft;
  <book&ft;
    <category&ft;fiction</category&ft;
    <author&ft;Herman Melville</author&ft;
    <title&ft;Moby Dick</title&ft;
    <isbn&ft;0-553-21311-3</isbn&ft;
    <price&ft;8.99</price&ft;
  </book&ft;
  <bicycle&ft;
    <color&ft;red</color&ft;
    <price&ft;19.95</price&ft;
  </bicycle&ft;
  <expensive&ft;10</expensive&ft;
</store&ft;

The example produces:

{
  "statusCode" : 200,
  "body" : "{'titles': ['Sayings of the Century', 'Moby Dick'], 'bikeColor': 'red'}"
}
 

Velocity Response Templates

The following shows a basic example for a Velocity format response template

new ClientAndServer(1080)
    .when(request().withPath("/some/path"))
    .respond(
        template(
            HttpTemplate.TemplateType.VELOCITY,
            "{\n" +
                "     'statusCode': 200,\n" +
                "     'cookies': { \n" +
                "          'session': '$!request.headers['Session-Id'][0]'\n" +
                "     },\n" +
                "     'headers': {\n" +
                "          'Client-User-Agent': [ '$!request.headers['User-Agent'][0]' ]\n" +
                "     },\n" +
                "     'body': '$!request.body'\n" +
                "}"
        )
    );

Velocity Syntax

For full Velocity syntax see: Velocity User Guide the following section provide a basic summary and examples of Velocity syntax.

Variables

In velocity a variable is referenced using a dollar, for example, $request.method will print the method field of the request variable.

$name will try to find the name variable in the current model.

If no matching variable is found for the variable expression the expression is printed, unless a quiet expression notation is used as follows $!name

The following basic template example demonstrates using multiple variables:

{
  'statusCode': 200,
  'body': {
    'method': '#!request.method',
    'path': '#!request.path',
    'headers': '#!request.headers.host.0'
  }
}

Methods can also be called on variables, for example, $request.getMethod() will call the getMethod() method of the request variable.

New variables can be defined using a #set directive, for example, #set ($message="Hello World") will create a new variables call message.

Conditionals

Conditional expressions are possible using #if, #elseif and #else directives, as folows:

#if($request.method == 'POST' && $request.path == '/somePath')
    {
        'statusCode': 200,
        'body': "{'name': 'value'}"
    }
#else
    {
        'statusCode': 406,
        'body': "$!request.body"
    }
#end

Loops

Loops are possible using the #foreach directives, as follows:

{
    'statusCode': 200,
    'body': "{'headers': [#foreach( $value in $request.headers.values() )'$value[0]'#if( $foreach.hasNext ), #end#end]}"
}

The example produces:

{
  "statusCode" : 200,
  "body" : "{'headers': ['mock-server.com', 'plain/text']}"
}

Mathematical

In addition to the dynamic model variables it is possible to perform basic mathematical operations, as follows:

#set($percent = $number / 100)
#set($remainder = $dividend % $divisor)

A range operator is also supported which can be useful in conjunction with #set and #foreach

#set($array = [0..10])
#foreach($item in $arr)
    $item
#end

For additional mathematical functionality it is also possible to use the MathTool or NumberTool in velocity response templates.

#set($power = $math.pow($number, 2))
#set($max = $math.max($number, 10))

Json Bodies

The JsonTool can be used to help parse JSON bodies, as follows:

#set($jsonBody = $json.parse($!request.body))

{
    'statusCode': 200,
    'body': "{'titles': [#foreach( $book in $jsonBody.store.book )'$book.title'#if( $foreach.hasNext ), #end#end], 'bikeColor': '$jsonBody.store.bicycle.color'}"
}

Given the following request:

{
  "path" : "/somePath",
  "body" : {
    "type" : "JSON",
    "json" : {
      "store" : {
        "book" : [ {
          "category" : "reference",
          "author" : "Nigel Rees",
          "title" : "Sayings of the Century",
          "price" : 18.95
        }, {
          "category" : "fiction",
          "author" : "Herman Melville",
          "title" : "Moby Dick",
          "isbn" : "0-553-21311-3",
          "price" : 8.99
        } ],
        "bicycle" : {
          "color" : "red",
          "price" : 19.95
        }
      },
      "expensive" : 10
    }
  }
}

The example produces:

{
  "statusCode" : 200,
  "body" : "{'titles': ['Sayings of the Century', 'Moby Dick'], 'bikeColor': 'red'}"
}

XML Bodies

The XmlTool can be used to help parse XML bodies, execute XPath and XML traversal.

The following example shows how to use XmlTool for XPath:

#set($xmlBody = $xml.parse($!request.body))

{
    'statusCode': 200,
    'body': "{'key': '$xml.find('/element/key/text()')', 'value': '$xml.find('/element/value/text()')'}"
}

Given the following request:

{
  "path" : "/somePath",
  "body" : "<element><key>some_key</key><value>some_value</value></element>"
}

The example produces:

{
  "statusCode" : 200,
  "body" : "{'key': 'some_key', 'value': 'some_value'}"
}

Velocity Tools

The following velocity tools are available for velocity response templates:

 

JavaScript Response Templates

The following shows a basic example for a Javaacript format response template

new ClientAndServer(1080)
    .when(request().withPath("/some/path"))
    .respond(
        template(
            HttpTemplate.TemplateType.JAVASCRIPT,
            "return {\n" +
                "     'statusCode': 200,\n" +
                "     'cookies': {\n" +
                "          'session' : request.headers['session-id'][0]\n" +
                "     },\n" +
                "     'headers': {\n" +
                "          'Client-User-Agent': request.headers['User-Agent']\n" +
                "     },\n" +
                "     'body': request.body\n" +
                "};"
        )
    );

JavaScript Syntax

All javascript response templates should return the response as an object as shown in the example above.

Javascript response templates support ES6 syntax for Java 9 to 14, for a summary of ES6 syntax see w3schools.

Javascript response templates support ES5 syntax for Java 8, for a summary of ES5 syntax see w3schools.

Java Version Support

Javascript templates rely on Nashorn which has the following limitations for Java versions:

  • Java 8 - ES5 only
  • Java 9 or 10 - ES5 & ES6
  • Java 11 to 14 - ES5 & ES6 but prints deprecated warning
  • Java 15+ - not supported

From Java 15 Nashorn is no longer part of the JDK but it is available as a separate library that requires Java 11+. Once MockServer minimum Java version is 11 then this separate library will be used.

Variables

In javascript a variable is referenced directly, for example, request.method will return the method field of the request variable.

The following basic template example demonstrates using multiple variables:

return {
    'statusCode': 200,
    'body': '{\'method\': \'' + request.method + '\', \'path\': \'' + request.path + '\', \'header\': \'' + request.headers.host[0] + '\'}'
};

Conditionals

Conditional expressions are possible, as follows:

if (request.method === 'POST' && request.path === '/somePath') {
    return {
        'statusCode': 200,
        'body': JSON.stringify({name: 'value'})
    };
} else {
    return {
        'statusCode': 406,
        'body': request.body
    };
}

Loops

Looping is possible, as follows:

var headers = '';
for (header in request.headers) {
  headers += '\'' + request.headers[header] + '\', ';
}
return {
    'statusCode': 200,
    'body': '{\'headers\': [' + headers.slice(0, -2) + ']}'
};

The example produces:

{
  "statusCode" : 200,
  "body" : "{'headers': ['mock-server.com', 'plain/text']}"
}