equals: "..."
equalsIgnoreCase: "..."
startsWith: "..."
endsWith: "..."
isLike: "...regEx..."
contains: "..."
exists: true
exists: false
Request Matching
Overview
API Simulator inspects the requests it receives by applying matching rules. Multi-key matching by URI (route), URI path pattern, any URI element, HTTP Method, HTTP Header fields, and values from the body provide for extensive request matching.
Matching Operations
Various operations are supported to check for matching text:
To negate an operation (other than exists
), use not
:
not equals: "..."
not equalsIgnoreCase: "..."
not startsWith: "..."
not endsWith: "..."
not isLike: "..."
not contains: "..."
Matchers
Matchers are responsible for applying matching rules. A simlet configuration defines the matchers as a list where each element in the list represents some matching criteria. For example:
matchers:
- method: POST
- where: UriPath
equals: "/rest/v1/products/product"
- header: content-type
equals: "application/json"
- where: body
element: "//product/upc"
exists: true
The default simlet should have no matchers. For that, don’t define matchers at all or define them as an empty list:
matchers: []
Method Matching
Matching requests by the HTTP Method (verb) can be specified in several ways. Use the one you like:
- method: POST
- where: method
equals: POST
- where: method
equals: "POST"
- where: method
equals: 'POST'
# A quite contrived example
- where: method
not startsWith: "G"
URI Matching
Here are examples of matching elements of the URI:
# Match a value in the whole URI
- where: Uri
contains: "api/places/json?"
# The whole URI is in encoded form just as it comes in the request, whereas
# the parts (path, query string parameters, etc) are decoded.
# Thus, the matching expression for a URI must use encoded values when applicable.
# The expression below matches URI that contains "?type=restaurant&bar" value
- where: Uri
contains: "?type=restaurant%26bar"
- where: UriPath
equals: "/api/places/json"
- where: UriPath
isLike: "/admin/.*"
- where: UriPathPattern
matches: "/api/{collection}/{format}"
- where: UriScheme
equals: "https"
- where: UriUserInfo
startsWith: "admin:"
- where: UriUserInfo
endsWith: ":passW0rd"
- where: UriHost
equalsIgnoreCase: "example.com"
- where: UriPort
equals: "8090"
- where: UriQueryParameter
named: "radius"
equals: "5"
# Multi-value query string parameter
- where: UriQueryParameter
named: "types"
equals: "food"
- where: UriQueryParameter
named: "types"
equals: "cafe"
- where: UriQueryParameter
named: "checked"
exists: true
- where: UriQueryParameter
named: "blah"
exists: false
- where: UriFragment
equals: "ref1"
Header Matching
header: Content-Type
contains: "/json"
- where: header
named: Content-Type
equals: "application/xml"
# Header name matching is case insensitive
- header: authorization
startsWith: "Bearer "
# Matching a custom header is no different
- header: X-Csrf-Token
exists: true
Cookie Matching
HTTP requests may contain cookies in an HTTP Header field called Cookie
. A single header field can have multiple cookies separated by semi-colon ;
.
API Simulator supports matching individual cookies so you don’t have to extract them yourself from the Cookie
header field.
matchers:
# Matching a cookie value using cookie matcher
- where: cookie
named: lang
equals: "en-US"
# Matching a cookie value using parameter from a cookie
- where: parameter
named: SessionIDParm
endsWith: "0259"
SessionIDParm:
is: parameter
from: cookie
named: "SessionID"
Body Matching
Currently there are two flavors of body matching methods: one that treats the body as unstructured content, and one that matches elements in structured text (JSON, XML).
Body as Unstructured Content
# Any one of the text matching operations can be applied
# Notice in this example the use of single quotes to surround the value
# that contains double quotes (presumably as part of JSON payload)
- where: body
contains: '"salePrice": "22.98"'
XML without Namespaces
Assuming the following XML body:
<ValidateAddress>
<ValidateAddressInput>
<Address>
<Line1>123 Main Street</Line1>
<City>Anycity</City>
<StateProvinceCode>ZZ</StateProvinceCode>
<PostalCode>12345</PostalCode>
<CountryCode>USA</CountryCode>
</Address>
</ValidateAddressInput>
</ValidateAddress>
…these are examples of matching elements in the XML body:
- where: body
element: "ValidateAddress/ValidateAddressInput/Address/Line1"
equals: "123 Main Street"
- where: body
element: "//Address/PostalCode"
equals: "12345"
XML with Namespaces
Often time XML uses namespaces. Assuming the following XML body:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ws="http://ws.v1_0.avs.example.com/">
<s:Header />
<s:Body>
<ws:ValidateAddress>
<ValidateAddressInput>
<Address>
<Line1>123 Main Street</Line1>
<City>Anycity</City>
<StateProvinceCode>ZZ</StateProvinceCode>
<PostalCode>12345</PostalCode>
<CountryCode>USA</CountryCode>
</Address>
</ValidateAddressInput>
</ws:ValidateAddress>
</s:Body>
</s:Envelope>
…these are examples of matching elements in the XML body when namespaces are in play: :
- where: body
element: "s:Envelope/s:Body/ws:ValidateAddress/ValidateAddressInput/Address/Line1"
namespaces:
s: "http://schemas.xmlsoap.org/soap/envelope/"
ws: "http://ws.v1_0.avs.example.com/"
equals: "123 Main Street"
# The namespace prefixes in the path (ns1, ns2) do not have to match the
# prefixes in the request (s, ws) as longs as they refer to the same URI
- where: body
element: "ns1:Envelope/ns1:Body/ns2:ValidateAddress/ValidateAddressInput/Address/PostalCode"
namespaces:
ns1: "http://schemas.xmlsoap.org/soap/envelope/"
ns2: "http://ws.v1_0.avs.example.com/"
equals: "123 Main Street"
JSON Elements
In the absence of a standard for JSON similar to XPath, API Simulator uses an unique extension to the JSON parsing that, internally, safely converts JSON documents to XML documents. Among the things it deals with are field names with spaces, for example. Elements from the original JSON document can then be matched using the same powerful XPath like for XML.
Need not to worry - for the majority of cases there is little to learn beyond how to navigate the structure of the JSON document.
Please see json.org for the complete JSON syntax. Here are some of the definitions to help understand the conversion to XML:
object
{}
{ members }
members
pair
pair , members
pair
string : value
array
[]
[ elements ]
elements
value
value , elements
value
string
number
object
array
true
false
null
...see http://json.org/ for the complete JSON syntax.
These are few important conversion rules for converting JSON to XML:
-
We represent
{
- the start of an unnamed object - with XML tag named<object>
and the end -}
- with</object>
. -
Simple elements of an array are stored between XML tags
<value>
and</value>
. -
JSON field names with spaces and other characters invalid in an XML tag name become attribute called 'name' of a
<field>
tag. For example,<field name="country code">US</field>
The following example should help understand how it works. Assuming this is the JSON document:
{
"product":
{
"id":"1234",
"name":"The Jumpers",
"category":
[
"Shoes",
"Kids"
],
"subCategory":"Basketball",
"color":"white"
}
}
…this is the XML representation of the JSON document:
<object>
<product>
<id>1234</id>
<category>
<value>Shoes</value>
<value>Kids</value>
</category>
<color>white</color>
<subCategory>Basketball</subCategory>
<name>The Jumpers</name>
</product>
</object>
…and these are examples of matching elements in the JSON body:
- where: body
element: "object/product/id"
equals: "1234"
- where: body
element: "/object/product/id"
equals: "1234"
- where: body
element: "//product/id"
equals: "1234"
# The first array element
- where: body
element: "object/product/category/value"
equalsIgnoreCase: "shoes"
# Again, the first array element
- where: body
element: "object/product/category/value[1]"
equalsIgnoreCase: "shoes"
# The second array element
- where: body
element: "object/product/category/value[2]"
equalsIgnoreCase: "kids"
# Any of the array elements' value is equal to 'Kids' (case-sensitive matching)
- where: body
element: "object/product/category[value='Kids']"
exists: true
- where: body
element: "object/product/color"
exists: true
- where: body
element: "object/product/size"
exists: false
Matching Rank
Sometimes, simlet matching has to be applied in a certain order. This is where matching rank comes into play. Here is an example:
Simlet 1 matches all GET
requests to the admin area:
# The default rank is 0
#rank: 0
matchers:
- method: GET
- where: UriPath
isLike: "/admin/.*"
Simlet 2 matches GET
requests to the /admin/logout
URL in the admin area:
rank: 100
matchers:
- method: GET
- where: UriPath
equals: "/admin/logout"
Because Simlet 2’s rank is 100, which is higher than the Simlet 1’s (default) rank of 0, Simlet 2 will be selected for GET
requests to the /admin/logout
URL.
The rule is that API Simulator tries to match simlets with higher matching ranks before simlets with lower ranks; simlets with the same matching rank are evaluated in no particular order.
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!