With Web services, the native service endpoint that is sent by CentraSite to Mediator inside a virtual service definition is a static element. Once the virtual service is successfully deployed to Mediator, the real endpoint is returned to CentraSite as part of the response message during deployment. At run time, when a SOAP request is received, that request is POSTed to the endpoint that is statically defined in the virtual service definition.
With REST services or XML services, however the endpoint is flexible. The REST services or XML services describe data as resources. The resources are accessible via logical endpoints that have application meaning to users. For example, a collection of textbooks might be defined as a resource with the following URL:
http://{host}:{port}/books
A specific book with an identifier of 1234 would be accessible with the following URL:
http://{host}:{port}/books/1234
Due to this difference and others, you should keep the following topics in mind when you configure a REST or XML virtual service:
When you configure the processing steps of a virtual REST or XML service, you specify the native service name, an endpoint, and the HTTP method type(s) that are included in the message (POST, GET, PUT, DELETE). From this information, CentraSite will generate a virtual service definition that includes service and operation elements, as well as an endpoint and binding element pair for each HTTP method specified.
CentraSite will automatically generate an operation name to be included when the virtual service definition deployment message is sent to Mediator. This means that if you create a virtual service called VS1, and you specify a native endpoint, then the endpoint exposed by Mediator for calling the virtual service will be /ws/VS1/Invoke.
For example, assume the following endpoints are deployed.
Native service endpoint: | http://localhost:8080/services/mtc/member |
Virtual service endpoint: | http://localhost:5555/ws/VS1/Invoke |
Assume that the example virtual service is deployed with two HTTP method bindings: GET and POST. Both of these bindings have operation elements that include the same HTTP location attribute: member. To better illustrate the functionality, the examples below show a series of sample requests from a consumer including the requests' HTTP method and Content-Type. (At run time, REST message detection is dependent upon a consumer using the correct Content-Type when a request is sent.) Each example shows the expected endpoint that Mediator will send after it has rewritten the endpoint prior to native service invocation.
For a GET, assume that:
The request Content-Type is: application-x-www-form-urlencoded
The endpoint received by Mediator is: http://localhost:5555/ws/VS1/Invoke
The native service endpoint sent by Mediator is: http://localhost:8080/services/mtc/member
The application function is: The native service returns a collection of members with summary information.
For a GET, assume that:
The request Content-Type is: application-x-www-form-urlencoded
The endpoint received by Mediator is: http://localhost:5555/ws/VS1/Invoke/1234
The native service endpoint sent by Mediator is: http://localhost:8080/services/mtc/member/1234
The application function is: The native service returns summary data for a member with this key.
For a GET, assume that:
The request Content-Type is: application-x-www-form-urlencoded
The endpoint received by Mediator is: http://localhost:5555/ws/VS1/Invoke/1234?detail=true
The native service endpoint sent by Mediator is: http://localhost:8080/services/mtc/member/1234?detail=true
The application function is: Query parameters remain intact. Returns a response message with more member details.
For a POST, assume that:
The request Content-Type is: application/xml or application/json
The endpoint received by Mediator is: http://localhost:5555/ws/VS1/Invoke/1234
The native service endpoint sent by Mediator is: http://localhost:8080/services/mtc/member/1234
The application function is: The request message provides the contents needed to create the member resource.
For a GET, assume that:
The request Content-Type is: application-x-www-form-urlencoded
The endpoint received by Mediator is: http://localhost:5555/ws/VS1/Invoke/joe
The native service endpoint sent by Mediator is: http://localhost:8080/services/mtc/member/joe
The application function is: Fetches the member defined with the login joe. (Mediator contains no metadata in its service deployment to differentiate between the "login" vs. "key" GET requests.)
For a GET, assume that:
The request Content-Type is: application-x-www-form-urlencoded
The endpoint received by Mediator is: http://localhost:5555/ws/VS1/Invoke?type=login&value=joe
The native service endpoint sent by Mediator is: http://localhost:8080/services/mtc/member?type=login&value=joe
The application function is: The native service might also support a static endpoint with constraints defined in query parameters. Mediator also supports this approach.
When you configure the Entry Protocol step of a virtual REST or XML service, it is important to specify all the HTTP methods that are supported for the service. For example, if the virtual service is deployed to Mediator and you selected only the GET method in the virtual service's Entry Step, then Mediator will only permit GET invocations. In this case, a POST request will be rejected with a return of statusCode 405 even if the native service happens to support POSTs.
It is important that the client's requests contain an HTTP Content-Type header. At run time, Mediator determines which message builder to use based on the message’s HTTP method and its Content-Type. (The absence of the soapAction header will indicate to Mediator that the message is an XML message.)
The valid HTTP method/Content-Type combinations are as follows:
This method... | Can be included in a message of this Content-Type... |
---|---|
POST | application/xml application/json application-x-www-form-urlencoded multipart/form-data or text/xml |
PUT | application/xml application/json application-x-www-form-urlencoded multipart/form-data or text/xml |
GET | application-x-www-form-urlencoded |
DELETE | application-x-www-form-urlencoded |
Notes:
When configuring the routing step of a REST or XML virtual service, you specify whether to route the requests to the native service with the same HTTP method that is contained in the requests (GET, POST, PUT, DELETE), or whether to route the requests with a different HTTP method.
Typically you want to pass each request to the native service with the same HTTP method that is contained in the request. For example, if a request contains a GET method, you allow the GET method to be passed to the native service. However, there might be rare cases in which you want to change the HTTP method of a request to different HTTP method. For example, you might want to:
Expose an XML service as a REST service.
In this case, the service you create would be a Virtual XML service that exposes the HTTP methods GET, POST, PUT and DELETE, but the routing method would always be POST.
Expose a REST service whose virtual REST service only exposes the POST method.
To change the HTTP method of a REST or XML request
On the REST or XML virtual service's Routing Protocols tab, set the value of the HTTP Method field either statically (by explicitly setting the value to GET, POST, PUT, or DELETE) or dynamically (by setting the value to Use Context Variable).
In order to use the Use Context Variable option to set the field dynamically, you must write a webMethods IS service that sets a value of GET, POST, PUT or DELETE for a predefined context variable named ROUTING_METHOD. You need to invoke this service in the virtual service's Request Processing step. For details, see Changing HTTP Methods in Requests Dynamically using a Context Variable.
Caution:
Use this feature carefully, since changing HTTP methods to
certain other HTTP methods could result in unintended results or errors.
For example, changing an inbound GET request to a DELETE request would be a serious mistake if the deletion was not intended and the native REST service actually deleted a resource when invoked with a DELETE method. Additionally, an incoming POST or PUT request cannot be translated into a GET or DELETE if the request has nested elements. For more information, see The Implications of Changing HTTP Methods.
This section discusses the following topics:
Note the following.
When changing this incoming HTTP method... | To... | Note that... |
---|---|---|
GET | POST |
|
GET | PUT | Identical to GET-to-POST, except that Mediator changes the request's HTTP method from GET to PUT. |
GET | DELETE | No comment. |
POST | GET |
|
POST | DELETE | Identical to POST-to-GET, except that Mediator changes the request's HTTP method from POST to DELETE. |
POST | PUT | The Content-Type of the changed request is sent as application/xml or application/json, and the charset is UTF-8. |
PUT | GET | Identical to POST-to-GET, except that Mediator changes the request's HTTP method from PUT to GET. |
PUT | POST | The Content-Type of the changed request is sent as application/xml or application/json, and the charset is UTF-8. |
PUT | DELETE | Identical to POST-to-DELETE, except that Mediator changes the request's HTTP method from PUT to DELETE. |
DELETE | GET | No comment. |
DELETE | POST | Identical to GET-to-POST, except that Mediator changes the request's HTTP method from DELETE to POST. |
DELETE | PUT | Identical to GET-to-PUT, except that Mediator changes the request's HTTP method from DELETE to PUT. |
GET, POST, PUT or DELETE | Use Context Variable | See Changing HTTP Methods in Requests Dynamically using a Context Variable. |
GET or DELETE | POST or PUT | Note that the query parameters will be picked off the URL and stored as top-level elements when the message is sent to the native service. The query parameters are ignored on the endpoint URL and lost when we POST to the native provider (i.e. don't change the protocol method). |
Alternatively, instead of changing an HTTP method explicitly (statically) to PUT, POST, GET or DELETE, you can change the HTTP method to the value of a predefined context variable (ROUTING_METHOD) that dynamically resolves to a different HTTP method (PUT, POST, GET or DELETE, as appropriate).
To change the HTTP method dynamically, you create a webMethods IS service and invoke it in the virtual service's Request Processing step. This webMethods IS service should reference the predefined context variable ROUTING_METHOD (see The Predefined Context Variables). To set the value of ROUTING_METHOD, use the setContextVariableValue method, which is defined in the following class:
com/softwareag/mediator/api/MediatorRuntimeFacade.java
For example:
public static final void updateHttpMethod(IData pipeline) throws ServiceException { String mcKey = “Message Context”; Object obj = IDataUtil.get(pipeline.getCursor(), mcKey); if (obj!=null && obj instanceof org.apache.axis2.context.MessageContext) { MessageContext msgCtx = (MessageContext) obj; QName varName = new QName(MediatorContextVariableType.ROUTING_METHOD.getName()); MediatorRuntimeFacade.setContextVariableValue(varName, "PUT", msgCtx ); } }
As stated in the above table, depending on the structure of the native service, the native service might not be expecting the same payload structure that is being sent. In this case, you would need to transform the request message into the format required by the native service before Mediator sends the requests to the native service. To do this, you invoke an XSLT file during the Request Processing step.
Assume that:
The native service name is "authors".
The virtual REST service or virtual XML service for "authors" is named "vs-authors" and is made available in Mediator at this endpoint: http://localhost:5555/ws/vs-authors/Invoke. The targetNamespace of the virtual REST service or virtual XML service is "http://example.com/authors".
Following is a sample XSLT transformation file for the GET-to-POST or GET-to-PUT scenario.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://example.com/authors" version="1.0"> <xsl:output method="xml" omit-xml-declaration="no" standalone="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="//ns:invoke/node()"> <xsl:element name="{local-name(.)}"> <xsl:value-of select="."/> </xsl:element> </xsl:template> <xsl:template match="//ns:invoke"> <xsl:element name="authors"> <xsl:apply-templates/> </xsl:element> </xsl:template> </xsl:stylesheet>
Mediator can accept a REST service request that specifies the Content-Type "application/json" (or "application/json/badgerfish") and the HTTP methods PUT, GET, DELETE and POST.
Assuming that the native service supports both JSON and the HTTP method(s) specified in the request, Mediator can determine the correct service, operation and output format (JSON) to return to the consuming application. There are different ways in which a native service provider can be prompted to return response content. It will vary with the provider. For example, some providers may rely on the Accept transport header to specify the format the consumer wants. Others may use an element in the request or a query parameter on the URL.
However, suppose for example that the native service does not support the HTTP method specified in the request (e.g., POST). As a workaround, you can configure the virtual service so that it "bridges" this difference between the consumer request and the native service. In this case, you can configure the virtual service so that it takes the POST and bridges it into an HTTP GET query, and then returns the service to the consumer in the expected JSON format. To implement this, you set the following predefined context variables in a user-defined webMethods IS service that you can invoke in the virtual service’s Request Processing step and Response Processing step:
MESSAGE_TYPE
: A Content-Type defined in
axis2.xml for a message formatter. This value must be a key in the axis2
message formatters list, since it is used to control message serialization.
(The valid choices are defined as attributes of the <messageFormatters/>
group in the Integration Server's axis2.xml configuration.)
BUILDER_TYPE
: A Content-Type defined in
axis2.xml for a message builder. This value must be a key in the axis2 message
builders list since it is used to control building of native service response
messages. (The valid choices are defined as attributes of the
<messageBuilders/> group in the Integration Server's axis2.xml
configuration.)
This and other "bridging" scenarios are discussed in more detail below.
How Mediator Determines Which Builder and Formatter Classes to Use (and How You Can Override Them)
JSON Example 2: POST/JSON Request, JSON Response (where POST is not supported)
Characteristics of the Mapped and Badgerfish JSON Conventions
Mediator makes these determinations at run time as follows. This table also summarizes how you can override the default determinations.
Run-time Step | Description |
---|---|
Mediator receives the request from the client |
It is important that the client's PUT or POST requests contain the HTTP header Content-Type because the Content-Type header determines the message builder Mediator uses to parse the input stream. (GET or DELETE request do not require a Content-Type header.) |
Mediator sends the request to the service provider |
Mediator uses a message formatter to serialize the request, and then sends the serialized request to the native service provider. Mediator determines the message formatter to use as follows:
|
Mediator receives a response from the service provider |
When the provider returns a response to Mediator, a message builder parses the response stream into an Axiom message to be stored in the message context. Mediator determines which message builder to use as follows:
|
Mediator sends a response to the client |
Mediator serializes the response and sends it to the client. By default, Mediator uses the formatter that was used to
serialize the request sent to the provider. (If the formatter was
You can override the Content-Type that is sent to the client by setting the MESSAGE_TYPE context variable in a webMethods IS service, and invoking this service in the virtual service’s Response Processing step. |
Following are some of the possible scenarios in which JSON type services can be requested. Many scenarios require that you "bridge" the differences between consumer requests and the native service (i.e., differing HTTP methods and Content-Types). Three of the scenarios are discussed in more detail following the table.
Consumer Sends | Mediator Sends Request to Provider | Mediator Receives Response from Provider | Mediator Sends Response to Consumer | Requirement for Bridging? |
---|---|---|---|---|
GET | GET | JSON | JSON | Request Processing step bridging (see JSON Example 1: GET Request, JSON Response) |
POST/JSON | POST/JSON | JSON | JSON | No bridging needed |
GET | GET | XML | JSON | Response Processing step bridging |
POST/JSON | GET | JSON | JSON | Request Processing step bridging |
POST/JSON | GET | XML | JSON | Request Processing step bridging and Response Processing step bridging (see JSON Example 2: POST/JSON Request, JSON Response (where POST is not supported)) |
POST/JSON | XSLT/GET * | JSON | JSON | Request Processing step bridging |
POST/JSON | XSLT/POST/XML * | XML | XML | Request Processing step bridging |
POST/JSON | POST/JSON | JSON/XSLT * | JSON | Response Processing step bridging |
GET | GET | JSON | XML | Request Processing step bridging and Response Processing step bridging (see JSON Example 3: GET Request, XML Response) |
POST/XML | POST/JSON | JSON | JSON | Request Processing step bridging |
* The XSLT references indicate where you can perform an XSLT message transformation at either the Request Processing or Response Processing step.
In the table above, the required Content-Type settings are not shown, but assume the following:
HTTP Method/Request Content | Required Axis2 Content Type |
---|---|
GET or DELETE | application/x-www-form-urlencoded |
POST or PUT/XML | application/xml |
POST or PUT/Mapped JSON | application/json |
POST or PUT/Badgerfish | application/json/badgerfish |
In this example, a consumer sends a GET request to get a native JSON
service. Mediator will send the response to the consumer in the requested JSON
format (as indicated by the "output=json"
parameter in
the query).
The request looks like this:
http://localhost:5555/ws/YahooVS/search?query=wsdl20&output=json
and because this is a GET request, the Content-Type defaults to
application/x-www-form-urlencoded
.
Note:
For GET or DELETE requests for REST services, it is not necessary
to specify the Content-Type in the request; Mediator will default to
application/x-www-form-urlencoded
for GET or DELETE
requests.
The run-time processing will be as follows:
Consumer Sends | Mediator Sends Request to Provider | Mediator Receives Response from Provider | Mediator Sends Response to Consumer | Requirement for Bridging |
---|---|---|---|---|
GET | GET | JSON | JSON | Request Processing step bridging |
Since the request is a GET (i.e., of Content-Type
application/x-www-form-urlencoded
), but Mediator expects
to receive a JSON stream from the provider, you must send the BUILDER_TYPE
application/json
to the native provider. To do this,
write and invoke a webMethods IS service in the virtual service’s Request
Processing step. The IS service should include the following predefined context
variable set to this value:
Context Variable | Value |
---|---|
BUILDER_TYPE | application/json |
In this example, a consumer sends a POST request (of Content-Type
application/json
) to a native service, but the native
service does not support POST.
The request's Content-Type is application/json
and its output
parameter is set to
xml
. The reason for this is explained below.
The run-time processing will be as follows:
Consumer Sends | Mediator Sends Request to Provider | Mediator Receives Response from Provider | Mediator Sends Response to Consumer | Requirement for Bridging |
---|---|---|---|---|
POST/JSON | GET | XML | JSON |
|
Configure the virtual service as follows:
In the virtual service's Routing Protocols tab, set the value of the HTTP Method field to the value GET. Doing this instructs Mediator to change the POST to an HTTP GET before Mediator sends it to the native service (which is necessary because the native service does not support POST).
Write and invoke a webMethods IS service in the virtual service’s
Request Processing step. The IS service should include the following
predefined context variables set to the values shown below. The request's
"query": "wsdl20"
and "output":
"xml"
parameters are transformed into query parameters on the URL
before the native service is invoked. Thus, although the consumer is sending a
JSON request, the native service is instructed to return an XML response to
Mediator.
Context Variable | Value |
---|---|
MESSAGE_TYPE | application/x-www-form-urlencoded |
BUILDER_TYPE | application/xml |
Write and invoke a webMethods IS service in the virtual service’s Response Processing step. The IS service should include the following predefined context variable set to the value shown below. Doing this instructs Mediator to bridge the XML response from the native service into JSON format, to be returned to the consumer.
Context Variable | Value |
---|---|
MESSAGE_TYPE | application/json |
In this example, a consumer sends a GET request to get a native
service. Mediator will send the response to the consumer in the requested XML
format (as indicated by the "output=xml"
parameter in
the query).
The request looks like this:
http://localhost:5555/ws/YahooVS/search?query=wsdl20&output=xml
and because this is a GET request, the Content-Type defaults to
application/x-www-form-urlencoded
.
The run-time processing will be as follows:
Consumer Sends | Mediator Sends Request to Provider | Mediator Receives Response from Provider | Mediator Sends Response to Consumer | Requirement for Bridging |
---|---|---|---|---|
GET | GET | JSON | XML |
|
Since the request is a GET (i.e., of Content-Type
application/x-www-form-urlencoded
), but Mediator expects
to receive a JSON stream from the provider, you must instruct Mediator to send
the BUILDER_TYPE application/json
to the native
provider. To do this, write and invoke a webMethods IS service in the virtual
service’s Request Processing step. The IS service should include the
following predefined context variable set to this value.
Context Variable | Value |
---|---|
BUILDER_TYPE | application/json |
Since the provider will return a JSON stream to Mediator, but the
consumer expects to receive the service in XML output format, you must set the
MESSAGE_TYPE to application/xml
. To do this, write and
invoke a webMethods IS service in the virtual service’s Response
Processing step. The IS service should include the following predefined context
variable set to this value:
Context Variable | Value |
---|---|
MESSAGE_TYPE | application/xml |
The open source library that Axis2 uses to support JSON is called Jettison. The Jettison library supports two formats of JSON: Mapped JSON and Badgerfish. Both are syntactically correct from a JSON perspective.
Note:
An important difference between the two is that the Mapped
convention returns a service fault if a virtual service is configured for
application/json (Mapped convention) and it encounters a message that has
namespaces, while the Badgerfish convention attempts to avoid losing any
meaning encoded in XML by preserving namespace declarations. The Axis2 JSON
library MessageFormatter will complain if Mediator attempts to transform an XML
response that contains namespace declarations. So, either make sure that the
requests do not include namespaces, or else set the MESSAGE_TYPE to
application/json/badgerfish
instead of setting it to
application/json
.
Other characteristics include the following.
An element with no characters or child elements is represented by:
{ "element" : "" }
No namespaces declarations are ever written.
Note:
The Badgerfish convention does allow namespaces. If a client sends a request that contains XML
namespaces, you need to bridge to the Badgerfish convention. To do this,
in the virtual service's Routing Protocol's step, set the parameter HTTP Headers to Customize and specify the Name as "Content-Type" and the Value as "application/json/badgerfish". Doing this will override the existing Content-Type that will be sent to the native provider.
An element with multiple child elements of the same name is represented by an array.
Simple case:
<price>10.00</price> { "acme.price" : { "10.00" }
Array case:
<root><child>test</child><child>test</child></root> { "root" : { "child" : [ "test", "test" ] } }
The XML attributes for a message are prefixed with @ when a message is serialized (same as Badgerfish).
This convention is used to provide the means to translate between XML and JSON without losing any data (i.e., namespaces).
Element names become object properties.
Text content of elements goes in the $ property of an object.
Nested elements become nested properties.
Multiple elements at the same level become array elements.
Attributes go in properties whose names begin with @.
Active namespaces for an element go in the element's @xmlns property.
The default namespace URI goes in @xmlns.$.
Other namespaces go in other properties of @xmlns.
Elements with namespace prefixes become object properties, too.
Simple example:
<price xmlns="http://acme.com">10.00</price> { "price": { "@xmlns": { "$": "http://acme.com" }, "$1": "10.00" } }
A more complex example:
<alice xmlns="http://some-namespace" xmlns:charlie="http://some-other-namespace"> <bob>david</bob> <charlie:edgar>frank</charlie:edgar> </alice> { "alice" : { "bob" : { "$" : "david" , "@xmlns" : {"charlie" : "http:\/\/some-other-namespace" , "$" : "http:\/\/some-namespace"} } , "charlie:edgar" : { "$" : "frank" , "@xmlns" : {"charlie":"http:\/\/some-other-namespace", "$" : "http:\/\/some-namespace"} }, "@xmlns" : { "charlie" : "http:\/\/some-other-namespace", "$" : "http:\/\/some-namespace"} } }
With REST virtual services, when working with requests and responses of the Content-Type application/json, the message content
can contain one or more root nodes. For example, a message might have the two root nodes {“firstName": “John”, “lastName”:
“Smith”}
. Note the following points for messages with multiple root nodes.
The XSLT provided in the Request/Response Processing step should have the XPath start with "//", for example "//firstName". This is because during the processing of the JSON content, Mediator will wrap the given content with system-defined elements.
webMethods IS services that you invoke in the Request/Response Processing step will contain the JSON content in the variable
called JSONRESTContentString
. You can update the content in the IS service and put the updated content into the pipeline's input variable UpdatedJSONRESTContentString
, which will be sent to the native service.