Request Object

Overview

Defining and using individual parameters for what is in a request is a good and clean way to configure simlets.

A less verbose way can be to use an object API Simulator makes available in Simula templates - _request. The _request object offers read-only access to the various parts of the HTTP request and can be used in a template’s placeholders and scriptlets.

Notice that using _request isn’t required. It does give you more power if you are willing to apply some light scripting.

Example

Below is an example simlet to help with understanding how to use _request and its methods in placeholders and scriptlets. The simlet will return a hierarchical structure to describe the parts of a specific request:

request:
- method: GET
- uriPath: /v1/places

response:
  from: template
  template: Simula
  body: `
# This is not JSON but just formatting to show hierarchical information
{
  request: {
    method: ${ _request.method }
    httpVersion: ${ _request.httpVersion }
    uri: {
      value: ${ _request.uri.value }
      scheme: ${ _request.uri.scheme }
      userInfo: ${ _request.uri.userInfo }
      host: ${ _request.uri.host }
      port: ${ _request.uri.port }
      path: {
        value: ${ _request.uri.path.value }
        segments: {
          exist: ${ _request.uri.path.segments.exist }
          count: ${ _request.uri.path.segments.count }
          # _request.pathSegments is shortcut for _request.uri.path.segments
          values: [
            ${ _request.pathSegments.get(0) }
            ${ _request.pathSegments[1] }
          ]
        }
      }
      query: {
        value: ${ _request.uri.query.value }
        params: {
          count: ${ _request.uri.query.params.count }
          values: [
            {
              name: postalcode
              exists: ${ _request.uri.query.params.contain('postalcode') }
              count: ${ _request.uri.query.params.count('postalcode') }
              values: [
                ${ _request.uri.query.params.get('postalcode', 0) }
              ]
            }
            {
              # HTTP URI query string parameter names are case sensitive. Thus, for a
              # parameter 'postalcode' this will yield exists as false and count as 0
              name: PostalCode
              exists: ${ _request.uri.query.params.contain('PostalCode') }
              count: ${ _request.uri.query.params.count('PostalCode') }
              values: []
            }
            {
              # _request.queryParams is shortcut for _request.uri.query.params
              name: types
              exists: ${ _request.queryParams.contain('types') }
              count: ${ _request.queryParams.count('types') }
              values: [
                ${ _request.queryParams.first('types') }
                ${ _request.queryParams.get('types', 1) }
              ]
            }
            {
              name: checked
              exists: ${ _request.uri.query.params.contain('checked') }
              count: ${ _request.uri.query.params.count('checked') }
              values: [
                ${ _request.uri.query.params.first('checked') }
              ]
            }
            {
              name: blah
              exists: ${ _request.uri.query.params.contain('blah') }
              count: ${ _request.uri.query.params.count('blah') }
              values: []
            }
          ]
        }
      }
      fragment: ${ _request.uri.fragment }
    }
    headers: {
      count: ${ _request.headers.count }
      values: [
        {
          name: Accept
          exists: ${ _request.headers.contain('ACCEPT') }
          count: ${ _request.headers.count('accept') }
          values: [
            ${ _request.headers.get('AcCePt', 0) }
          ]
        }
        {
          name: Cookie
          exists: ${ _request.headers.contain('Cookie') }
          count: ${ _request.headers.count('Cookie') }
          values: [
            ${ _request.headers.get('Cookie', 0) }
          ]
        }
        {
          name: Origin
          exists: ${ _request.headers.contain('Origin') }
          count: ${ _request.headers.count('Origin') }
          values: [${ _request.headers.get('Origin', 0) ?: '' }]
        }
      ]
    }
    cookies: {
      exist: ${ _request.cookies.exist }
      count: ${ _request.cookies.count }
      values: [
        {
          name: lang
          exists: ${ _request.cookies.contain('lang') }
          value: ${ _request.cookies.get('lang') }
        }
        {
          name: SESSION
          exists: ${ _request.cookies.contain('SESSION') }
          value: <% q = (_request.cookies.contain('SESSION') ? '"' : '')
                    write q + _request.cookies.get('SESSION') + q %>
        }
      ]
    }
    body: <% write (_request.bodyText ?: null) %>
  }
}
`

Say we started API Simulator on localhost port 6090 for a simulation that contains the simlet above, and then ran the following curl command:

curl --proxy localhost:6090 "http://admin:passW0rd@localhost:6090/v1/places?postalcode=10004&types=food&types=cafe&checked#ref" -H "Accept: application/json" -H "Cookie: lang=en_us"

Notice that without the --proxy localhost:6090 option curl will "move" in the actual HTTP request the domain and port to a Host header, and base64-encoded user info (admin:passW0rd) to an Authorization header. Also, the fragment - ref - isn’t sent by curl but if present, the _request object will capture it.

And, yes - we are using API Simulator as an HTTP proxy…​to itself!

In case you are wondering what the (_request.bodyText ?: null) expression is: in this particular case it will return _request.bodyText when the value is not null or an empty string or it will return null otherwise. For more info, see the so-called Elvis operator.

Here is the output:

# This is not JSON but just formatting to show hierarchical information
{
  request: {
    method: GET
    httpVersion: HTTP/1.1
    uri: {
      value: http://admin:passW0rd@localhost:6090/v1/places?postalcode=10004&types=food&types=cafe&checked
      scheme: http
      userInfo: admin:passW0rd
      host: localhost
      port: 6090
      path: {
        value: /v1/places
        segments: {
          exist: true
          count: 2
          # _request.pathSegments is shortcut for _request.uri.path.segments
          values: [
            v1
            places
          ]
        }
      }
      query: {
        value: postalcode=10004&types=food&types=cafe&checked
        params: {
          count: 3
          values: [
            {
              name: postalcode
              exists: true
              count: 1
              values: [
                10004
              ]
            }
            {
              # HTTP URI query string parameter names are case sensitive. Thus, for a
              # parameter 'postalcode' this will yield exists as false and count as 0
              name: PostalCode
              exists: false
              count: 0
              values: []
            }
            {
              # _request.queryParams is shortcut for _request.uri.query.params
              name: types
              exists: true
              count: 2
              values: [
                food
                cafe
              ]
            }
            {
              name: checked
              exists: true
              count: 1
              values: [

              ]
            }
            {
              name: blah
              exists: false
              count: 0
              values: []
            }
          ]
        }
      }
      fragment: null
    }
    headers: {
      count: 7
      values: [
        {
          name: Accept
          exists: true
          count: 1
          values: [
            application/json
          ]
        }
        {
          name: Cookie
          exists: true
          count: 1
          values: [
            lang=en_us`
          ]
        }
        {
          name: Origin
          exists: false
          count: 0
          values: []
        }
      ]
    }
    cookies: {
      exist: true
      count: 1
      values: [
        {
          name: lang
          exists: true
          value: en_us`
        }
        {
          name: SESSION
          exists: false
          value: null
        }
      ]
    }
    body: null
  }
}

Compare the values output for the checked query string parameter and the Origin header - in both cases, it is an empty list but notice how new lines in the use of placeholders affect the output.

Where did 7 headers come from when we added only two - Accept and Cookie? if we added the -v switch to the curl command, we would see something like this:

> Host: localhost:6090
> Authorization: Basic YWRtaW46cGFzc1cwcmQ=
> User-Agent: curl/x.yz.0
> Proxy-Connection: Keep-Alive
> Accept: application/json
> Cookie: lang=en_us

Accounting for Content-Length: 0 for the GET request, and we have 7 headers.

Request Object Reference

The built-in _request object has the following methods. Some of the methods may return other objects, which have their own methods that may return other objects and so on, to provide access down to the individual components of an HTTP request.

String method
The HTTP method (aka verb). For example, GET, POST, etc.

String httpVersion
The HTTP version (e.g. 1.1, 1.0).

<URI> uri
Gives non-null object that provides access to the URI components.

<HTTP Headers> headers
Gives non-null object that provides access to the collection of HTTP headers.

<HTTP Cookies> cookies
Gives non-null object that provides access to the collection of HTTP cookies.

String bodyText
Gives the body of the request as a String using the default UTF-8 character set. The value returned would be null if the request has no body.

String bodyText(String charsetName)
Returns the body of the request as a String using the given character set. The value returned would be null if the request has no body.

byte[] bodyBytes
The body of the request as an array of bytes. The value returned would be null if the request has no body.

URI

String value
Gives the raw, unparsed value for the URI.

String scheme
The URI scheme component - http or https for HTTP requests.

String userInfo
Gives the decoded user-information component of the URI, or null if the user information is undefined.

String host
The URI host component.

int port
The URI port number.

<URI Path> path
Gives non-null object that provides access to the URI path components.

<URI Query> query
Gives non-null object that provides access to the URI Query String.

String fragment
The value of the fragment - the part after #. It is null when the URI doesn’t have fragment part.

URI Path

String value
Gives the raw value of the whole path. It won’t be null if it was derived from a valid URI.

<URI Path Segments> segments
Gives non-null object that provides access to the URI path segments.

URI Path Segments

An object that provides access to the segments of the URI path component. Implemented as List Elements.

URI Query

String value
Gives the raw value of the whole query string. It will be null when a URI doesn’t have query string component.

<URI Query Parameters> params
Gives non-null object that provides access to the URI query string parameters.

URI Query Parameters

An object that provides access to the individual URI query string parameters. Practically, a collection of case-sensitive Named Values.

HTTP Headers

An object that provides access to HTTP headers. Practically, a collection of case-insensitive Named Values.

HTTP Cookies

A "convenience" object that provides access to HTTP cookies found in "Cookie" HTTP Header.

int count
The number of cookies (>= 0).

boolean exist
Gives true if there are any cookies, or false otherwise.

boolean contain(String name)
Returns true if a cookie with the given name exists, or false otherwise. The name of the cookie to check is case-insensitive.

String get(String name)
Returns the value for the cookie with the given name or null if such cookie doesn’t exist. The name of the cookie to get is case-insensitive.

<Set Elements> names
Gives a non-null set of the cookie names in lower case.

Named Values

Named Values is a collection of elements keyed by their name and for each name having a list of values accessible by position (random access, not sequential).

<Random Access Values> get(String name)
Returns a non-null object to access by position the values for the given name. There will be no values if element with that name doesn’t exist.

String get(String name, int index)
Returns the value for the name at the given index, or null if index is out of boundaries (no exception is thrown!) or when there are no values. The index is 0-based.

String first(String name)
Returns the first value (at index 0) for the specified name, or null if element with the name doesn’t exist or it has no values.

boolean exist
Gives true if there are any names, or false otherwise.

boolean contain(String name)
Returns true if element with the given name exists, possibly with no values, or false otherwise.

boolean contain(String name, int index)
Returns true if there is value (even if it is null) for the name at the given index, or false otherwise. The index is 0-based.

int count
Gives the total number of all names.

int count(String name)
Returns the number of values for the name. 0 if the name doesn’t exist.

<Set Elements> names
Gives non-null, unmodifiable set of all names. It could be an empty set.

Random Access Values

Values accessible by their 0-based position (random access, not sequential).

String get(int index)
Returns the value at the given 0-based index, or null if index is out of boundaries (no exception is thrown!) or when there are no values.

boolean exist
Gives true if there are any values, or false otherwise.

boolean contain(String value)
Returns true if the given value is one of the values, or false otherwise.

int count
Gives the number of values.

<Iterator> iterator()
Returns an iterator over the values in no particular order. It makes it possible to use the values in "foreach" statements. Attempts to modify the values via the iterator (e.g. remove a value) will result in UnsupportedOperationException.

List Elements

int count
Gives the number of elements in the list.

boolean exist
Gives true if list elements exist. That’s it this list contains any elements.

boolean contain(String element)
Returns true if the list contains the specified element, or false otherwise.

<Iterator> iterator()
Returns an iterator over the elements in no particular order. It makes it possible to use the list elements in "foreach" statements. Attempts to modify the set via the iterator (e.g. remove an element) will result in UnsupportedOperationException.

String get(int index)
Returns the element at the specified 0-based position in this list. It throws IndexOutOfBoundsException if the index is out of range.

Set Elements

Set Elements is a collection of unique elements - there are no duplicates.

int count
Gives the number of elements.

boolean exist
Gives true if set elements exist. That’s it this set contains any elements.

boolean contain(String element)
Returns true if this set contains the specified element, or false otherwise.

<Iterator> iterator()
Returns an iterator over the elements in no particular order. It makes it possible to use the set elements in "foreach" statements. Attempts to modify the set via the iterator (e.g. remove an element) will result in UnsupportedOperationException.

Iterator

An object that provides sequential access to all elements in a collection.

boolean hasNext()
Returns true if the iteration has more elements. That is - if calling the next method would return an element rather than throwing an exception.

Object next()
Returns the next element. Throws NoSuchElementException if there are no more elements to keep on iterating.

void remove()
Removes from the underlying collection the last element returned by this iterator. Throws UnsupportedOperationException for read-only iterators.


We would love to hear your feedback! Shoot us an email to [feedback at APISimulator.com] about anything that is on your mind.

Happy API Simulating!