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) %>
}
}
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:
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 a quick email to [feedback at APISimulator.com] to let us know what you think.
Happy API Simulating!