Skip to content

How HTTP Requests and Responses are Mapped to Gloop Services

Introduction

As you may have read, endpoints invoke services based on certain configured conditions and when they do invoke a service, TORO Integrate tries to inject as much useful data as it can about the endpoint and what triggered the service, itself, so that this data may be used during the execution of the service. TORO Integrate will map data to the service (in the form of method arguments) if its method parameters have types and names that match any of the entries in the list of supported or injectable endpoint service arguments1.

The same can be said for how TORO Integrate maps HTTP request related data to the inputs of a Gloop Service. The only difference is that the names of the parameters will change, to coincide with things such as:

As a result we think it's important for you to understand how the mapping works, in order to help you write cleaner code, and to allow TORO Integrate to do all the heavy lifting for you!

Gloop Object Converters

Gloop has the ability to convert objects from one type to another for you. These converters are also used by Gloop when mapping request data to the input properties of a Gloop Service.

Request Data

First, we'll show you how TORO Integrate handles the request data, which gets mapped before your service is invoked.

How Request Cookies are Mapped

The first thing TORO Integrate does is check all cookies in the request and see if there's an input property with either the same name as the cookie's name or if there's a property whose name is the same as the cookie's name, but prefixed with $cookie_. In both cases, Gloop will try to map matching cookies to their respective input properties. This means that if you want your Gloop Service to get a cookie's value from the request called JSESSIONID, you simply add an input property called either JSESSIONID or $cookie_JSESSIONID. If a matching input was found, the value of the cookie will be passed in.

Next, if the input has a Gloop Model property called $cookies, it will perform the same mapping as above. However, instead of mapping to the service's input properties, it will instead map them to the properties of the $cookies input model. This allows you to have a single, dedicated input model that will only contain cookie data.

How Request Attributes are Mapped

The next thing TORO Integrate does is check all attributes in the request and see if there's an input property with either the same name as any of the attributes' names or if there's a property whose name is the same as any of the attributes' names, but prefixed with $attribute_. In both cases, Gloop will try to map matching attributes to their respective input properties. This means that if you want your Gloop Service to get the value of an attribute called myRequestAttribute from the request, you simply add an input property called either myRequestAttribute or $attribute_myRequestAttribute.

Next, if the input has a Gloop Model property called $requestAttributes, it will perform the same mapping as above. However, instead of mapping to the service's input properties, it will instead map them to the properties of the $requestAttributes input model. This allows you to have a single, dedicated input model that will only contain attribute data.

How Request Headers are Mapped

The next thing TORO Integrate does is check all headers in the request, and see if there's an input property with either the same name as any of the headers' names or if there's a property whose name is the same as any of the headers' names, but prefixed with $header_. In both cases, Gloop will try to map matching headers to their respective input properties. Note that this check is case-insensitive. This means that if you want your Gloop service to get the value of a header called Accept from the request, you simply add an input property to your service called either Accept, ACCEPT, AcCePt, accept, or $header_Accept, $header_ACCEPT, etc.

Next, if the input has a Gloop Model property called $requestHeaders, it will perform the same mapping as above. However, instead of mapping to the service's input properties, it will instead map them to the properties of the $requestHeaders input model. This allows you to have a single, dedicated input model that will only contain header data.

More accurate header mapping

You are also allowed to have Date and Number input properties mapped to request headers. If Gloop finds a Date input property, it will call getDateHeader. If it finds a Number input property, getIntHeader will be called to map the header instead. For all other object types, getHeader will be called instead.

How Session Data is Mapped

The next thing TORO Integrate does is check all session attributes in the request and see if there's an input property with either the same name as any of the session attributes' names or if there's a property whose name is the same as any of the session attributes' names, but prefixed with $sessionAtt_. In both cases, Gloop will try to map matching session attributes to their respective input properties. This means that if you want your Gloop Service to get the value of a session attribute called shoppingCart from the request, you simply add an input property called either shoppingCart or $sessionAtt_shoppingCart.

Next, if the input has a Gloop Model property called $session, it will perform the same mapping as above. This allows you to have a single, dedicated input model that will only contain session data.

After iterating over the session attributes, TORO Integrate will look for an input property called $sessionId which, if found, will have the session's ID mapped to it. Finally, it will check for a property called $sessionCreationTime, which will have the session's creation time mapped to it, if it's found.

HTTP session creation

TORO Integrate will not create a session when a Gloop Service is invoked. If you want to associate a session with the request, you will need to add a $request property, and call getSession() manually.

How URI Variables are Mapped

TORO Integrate will next check for any URI variable tokens. It does this by reusing the URI Patterns functionality from Spring Web MVC. Similar to the example in the Spring documentation, if a Gloop Service was being executed from a URI such as /owners/123/pets/456, and the Gloop Service had a REST URL path or Gloop REST API path of /owners/{ownerId}/pets/{petId}, then TORO Integrate will populate the input property called ownerId with 123 and the property petId with 456.

How Request-Specific Properties are Mapped

Next, TORO Integrate will try and map some simple request-specific properties. The table below shows the input properties that it checks for and which method of HttpServletRequest will be used to populate it:

Input Property Name Request Method
$path HttpServletRequest#getRequestURI()
$method HttpServletRequest#getMethod()
$queryString HttpServletRequest#getQueryString()
$contentType HttpServletRequest#getContentType()

How HTTP Request Parameters are Mapped

TORO Integrate will then check for any request parameters, regardless of how they are passed in (e.g. part of the query string, POST-ed). The name of the parameter will be used to try and find a matching input property.

For input properties that are models, TORO Integrate will use its customised Spring Content Negotiation Manager to determine the format (content-type) of the parameters. The Content Negotiation Manager in TORO Integrate doesn't use file extensions to detect the content type. Instead, it uses a parameter called dataFormat to detect whether to use formats such as XML or JSON. If the dataFormat parameter doesn't exist in the request, then the Accept header of the request is used. Finally, if no Accept header exists (or is too broad), it will fall back to the format specified in the default.format instance property.

URL-encoded requests

If the request Content-Type is application/x-www-form-urlencoded and the service has a Gloop Model Body Parameter, Integrate will map the request parameters to the properties of the model. This does not work for nested properties (in other words, the model can't have any model properties).

How Multipart Requests are Mapped

If the request is detected as a multipart request, then the parameters are mapped as they normally would be, and any files in the request will be mapped to their respective input properties. The input properties for multipart files can either be a Gloop Model that references io.toro.integrate.http.MultipartFile, or any object type supported by the built-in ContentToStreamConverter.

How Request Bodies are Mapped

If the request isn't a multipart request and isn't application/x-www-form-urlencoded, then the body of the request (if there is one) will be mapped to the service. If the service was invoked from a REST URL path or a Gloop REST API path with a set Body Parameter, then TORO Integrate will try and populate the Body Parameter input for you. The list below shows the supported types for the Body Parameter:

Gloop Model request bodies

If the Body Parameter input is a Gloop Model, the same logic that's used when detecting the content type of request parameters) will be used when parsing the body. Also, if the Body property is a Gloop Model, TORO Integrate will map the root element name of the request body (if it's XML or JSON) to an input property of the service called $rootElementName, if it exists. This is useful if the body could have different names.

If Tracker is enabled for the HTTP request, TORO Integrate will map the internalId request parameter to an input property called $internalId, if it exists. This, then, will be set as the ID of the Tracker document of the service invocation.

How HTTP Request and Response Objects are Mapped

TORO Integrate will then check for input properties called $request, and $response. These are the properties that TORO Integrate will map the original HttpServletRequest and HttpServletResponse to. These properties are handy if you want to perform low level manipulation of the request and response (such as creating sessions, adding cookies, setting response codes, and others more).

Order of request mapping

You may have noticed that if a request parameter and cookie had the same name, the request parameter mapping will overwrite the cookie value because parameters are processed after cookies. This is deliberate and it's why we included prefixes such as $cookie_, $attribute and $sessionAtt. In fact, request parameters that begin with $cookie_, $attribute_, or $sessionAtt_ are ignored.

Response Data

Once the request data has been mapped into your service and the service has been invoked, TORO Integrate will then try to map the output of your service to the HTTP response.

Exceptions in Gloop Services

If your Gloop Service throws an uncaught exception, Integrate will wrap it with an APIException. These exceptions are annotated to allow TORO Integrate to respond with error messages in a common format such as:

  • JSON
    1
    2
    3
    4
    5
    {
      "result" : "ERROR",
      "apiErrorCode" : -1,
      "message" : "Unhandled Error: null"
    }
    
  • XML
    1
    2
    3
    4
    5
    <APIException>
      <result>ERROR</result>
      <apiErrorCode>-1</apiErrorCode>
      <message>Unhandled Error: null</message>
    </APIException>
    

There are Gloop Model-compatible versions of these Java classes at io.toro.integrate.api, in the core/models directory (located in Coder Navigator).

Rendering a Response with a JSP Page

If you wish to render the output of your service with a JSP page, simply add an output property called $gloopView. If this property exists, it will return a Spring ModelAndView object back to Spring Web MVC that contains the name of the view and the output from the Gloop Service as the model. The service output will be wrapped in a custom class that implements the Map interface allowing the resulting JSP page to use JSTL to access the properties from the output of the service. Since the response handling from this point on is delegated back to Spring, the use of the redirect: prefix is also supported.

JSP pages for Integrate must be in the web directory of the corresponding Integrate Package that the top-level service resides in. The $gloopView value doesn't need the web directory included in it, nor does it need the file extension. Therefore if you wanted your Gloop Service to use the JSP page web/myOutput.jsp, then the $gloopView value would be myOutput. Similarly, if your Gloop Service was to use the JSP page web/my/views/here/view1.jsp then the corresponding value for $gloopView would be my/views/here/view1.

Rendering the Output using Binary Data

If the output of your Gloop Service is binary data (such as having a service that renders an image or PDF, or prompts the user to download a file), then the output of the service should only contain a single model property that references the io.toro.integrate.gloop.http.MultipartFile model. The name of the model isn't important. If the contentType of the model is compatible with application/octet-stream, then TORO Integrate will set the Content-Disposition response headers for you, which normally trigger web browsers to prompt the user for downloading a file (and also includes the filename in the header); otherwise the contentType from the model will be set as the response content type. If the size property is set, it, too, will be passed to the response. Once all the headers are taken care of, the inputStream of the service output io.toro.integrate.gloop.http.MultipartFile model is copied to the response output stream.

Other Response Types

If the output of the service doesn't tell TORO Integrate to use binary data or a JSP page, then the output of the service itself is sent to the response. TORO Integrate uses the detected content type to determine whether to write the response out as XML, JSON, or YAML. However, TORO Integrate behaves slightly differently if there's only one output parameter in your Gloop Service. If your Gloop Service has one output parameter, then only that will get serialised.

Sample Outputs

In this section, we'll show you how TORO Integrate behaves with certain output types.

Single String Parameter

Below is the output of a Gloop Service called TestOutput that returns a single String property called name, whose value is John Smith.

Single Output Parameter

JSON
1
"John Smith"
XML
1
2
<?xml version="1.0"?>
<name>John Smith</name>
YAML
1
name: John Smith

More Than One Parameter

Below is the output of a Gloop Service called TestOutput that returns two properties called name and age, with the values of John Smith and 25, respectively.

Double Output Parameter

JSON
1
2
3
4
{
  "name": "John Smith",
  "age": 25
}
XML
1
2
3
4
5
<?xml version="1.0"?>
<TestOutputOutput>
    <name>John Smith</name>
    <age>25</age>
</TestOutputOutput>
YAML
1
2
3
TestOutputOutput: 
  name: John Smith
  age: 25

Notice how the output has been wrapped in all three formats. For XML and YAML, the parent node is called the name of the service, suffixed with Output.

Single Gloop Model Parameter

Below is the output for a Gloop Service called TestOutput that returns one model property called person, which has two properties called name and age, with a value of John Smith and 25.

Single Model Output Parameter

JSON
1
2
3
4
{
    "name": "John Smith",
    "age": 25
}
XML
1
2
3
4
5
<?xml version="1.0"?>
<person>
    <name>John Smith</name>
    <age>25</age>
</person>
YAML
1
2
3
person: 
  name: John Smith
  age: 25

Notice how the output has been wrapped in all three formats again. Also, the XML and YAML outputs are now wrapped with a nicer name (person instead of TestOutputOutput).

Complex Output

Below is the output for a Gloop Service called TestOutput that returns one model property called person, which has two properties called name and age, with a value of John Smith and 25. It also has two other output parameters called reponseMessage with a value of OK and responseTime with a value of 50.

Complex Output Parameter

JSON
1
2
3
4
5
6
7
8
{
  "person": {
    "name": "John Smith",
    "age": 25
  },
  "responseMessage": "OK",
  "responseTime": 50
}
XML
1
2
3
4
5
6
7
8
9
<?xml version="1.0"?>
<TestOutputOutput>
    <person>
        <name>John Smith</name>
        <age>25</age>
    </person>
    <responseMessage>OK</responseMessage>
    <responseTime>50</responseTime>
</TestOutputOutput>
YAML
1
2
3
4
5
6
TestOutputOutput: 
  person: 
    name: John Smith
    age: 25
  responseMessage: OK
  responseTime: 50

Notice how the output has been wrapped in all three formats again. But since the service has more than one output parameter, the XML and YAML responses have gone back to using TestOutputOutput as the name or the root node.


  1. Endpoint types vary in which service arguments they support. To learn which parameters are supported by a type, please visit the respective endpoint type's documentation page.