Enterprise Architecture & Integration, SOA, ESB, Web Services & Cloud Integration

Enterprise Architecture & Integration, SOA, ESB, Web Services & Cloud Integration

Tuesday 24 November 2015

Read HTTP Header and Pass as CORS headers in WSO2 ESB

Are you facing CORS headers issue? If you want to understand it better, it has been defined well in https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS and https://en.wikipedia.org/wiki/Cross-origin_resource_sharing. You can also follow http://enable-cors.org/.

As you read, you would understand that you may have to pass the following response headers
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers


It is straightforward to add the first two headers in the response path (i.e., Out Sequence). For third header i.e., Access-Control-Allow-Headers, you may have to use the value that has come in the request header "Access-Control-Request-Headers". If so, you can achieve this in two steps:

Step 1:
Get the value for header "Access-Control-Request-Headers" and store the value in synapse config scope. It means that the value will be available both in and out sequence.

Snippet that need to be added in "In Sequence":
<property name="Access-Control-Request-Headers" expression="get-property('transport', 'Access-Control-Request-Headers')"/>

Step 2:
Get the value from synapse config, and set it as response header

Snippet that need to be added in "Out Sequence":
<property name="Access-Control-Allow-Headers"
     expression="$ctx:Access-Control-Request-Headers"
     scope="transport"
     type="STRING"/>


Sunday 15 November 2015

Expose existing SOAP Web Services as REST API using WSO2 ESB

Problem statement

Your enterprise might have invested a lot in exposing software assets as SOAP Web Services to integrate with internal and external applications. If a new "modern" consumer who can (or wants to) consume only REST API, would you rewrite the entire thing to support REST clients?

WSO2 ESB (for example, version 4.8.1 which I have used) supports RESTful integration using API. We can leverage this feature to expose SOAP web services through REST API without changes to SOAP services. So, same time you would be able to support existing SOAP clients and new REST clients.

There are many blogs / web sites available on detailed explanation on REST style. You can follow this https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm from Roy Fielding. Another blog on REST can be found here in http://rest.elkstein.org/2008/02/what-is-rest.html

Steps involved

The following steps are required to achieve what we said.
  1. Define REST API
  2. Transform request REST data into SOAP data
  3. Call the back end application / URL
  4. Transform response SOAP data into REST data (for example JSON)
  5. Respond with JSON response

Sample use case - Student Exam Result Service


Usecase overview

We will implement a simple use case - student exam results application. The organisation has the SOAP service already running in the premise that will take student id as input parameter and responds with marks for four subjects.

Sample request SOAP message:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stud="http://www.example.org/student/">
   <soapenv:Header/>
   <soapenv:Body>
      <stud:getMarks>
         <student>John Smith</student>
      </stud:getMarks>
   </soapenv:Body>
</soapenv:Envelope>

Sample response message:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stud="http://www.example.org/student/">
   <soapenv:Header/>
   <soapenv:Body>
      <stud:getMarksResponse>
         <marks>
            <Mathematics>95</Mathematics>
            <Science>97</Science>
            <Lang-I>92</Lang-I>
            <Lang-II>94</Lang-II>
         </marks>
      </stud:getMarksResponse>
   </soapenv:Body>
</soapenv:Envelope>

WSDL:
<wsdl:definitions name="Student" targetNamespace="http://www.example.org/student/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.example.org/student/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <wsdl:types>
      <xsd:schema targetNamespace="http://www.example.org/student/">
         <xsd:element name="getMarks">
            <xsd:complexType>
               <xsd:sequence>
                  <xsd:element name="student" type="xsd:string"/>
               </xsd:sequence>
            </xsd:complexType>
         </xsd:element>
         <xsd:element name="getMarksResponse">
            <xsd:complexType>
               <xsd:sequence>
                  <xsd:element name="marks" type="tns:Marks"/>
               </xsd:sequence>
            </xsd:complexType>
         </xsd:element>
         <xsd:complexType name="Marks">
            <xsd:sequence>
               <xsd:element name="Mathematics" type="xsd:int"/>
               <xsd:element name="Science" type="xsd:int"/>
               <xsd:element name="Lang-I" type="xsd:int"/>
               <xsd:element name="Lang-II" type="xsd:int"/>
            </xsd:sequence>
         </xsd:complexType>
      </xsd:schema>
   </wsdl:types>
   <wsdl:message name="getMarksRequest">
      <wsdl:part element="tns:getMarks" name="parameters"/>
   </wsdl:message>
   <wsdl:message name="getMarksResponse">
      <wsdl:part element="tns:getMarksResponse" name="parameters"/>
   </wsdl:message>
   <wsdl:portType name="Student">
      <wsdl:operation name="getMarks">
         <wsdl:input message="tns:getMarksRequest"/>
         <wsdl:output message="tns:getMarksResponse"/>
      </wsdl:operation>
   </wsdl:portType>
   <wsdl:binding name="StudentSOAP" type="tns:Student">
      <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsdl:operation name="getMarks">
         <soap:operation soapAction="http://www.example.org/student/getMarks"/>
         <wsdl:input>
            <soap:body use="literal"/>
         </wsdl:input>
         <wsdl:output>
            <soap:body use="literal"/>
         </wsdl:output>
      </wsdl:operation>
   </wsdl:binding>
   <wsdl:service name="Student">
      <wsdl:port binding="tns:StudentSOAP" name="StudentSOAP">
         <soap:address location="http://www.example.org/"/>
      </wsdl:port>
   </wsdl:service>
</wsdl:definitions>

Now, lets do some hands on in WSO2 ESB.

1. Define REST API

In WSO2 ESB Management Console, goto Main --> Service Bus --> API and create a new API. The steps are very clearly mentioned in the WSO2 documentation site https://docs.wso2.com/display/ESB480/Getting+Started+with+REST+APIs how to create API. 


The API definition will look like this.

<api xmlns="http://ws.apache.org/ns/synapse"
     name="StudentAPI"
     context="/exam-results">
   <resource methods="GET" uri-template="/student/{student}" >

<inSequence> </inSequence>
<outSequence> </outSequence>
</resources>
</api>

This will provide a REST GET url like this http://localhost:8280/exam-results/student/1002 where 1002 is the student id for which we need the marks scored in four subjects. The URL may directly be accessed from a browser for instance.

2. Transform request REST data into SOAP data

Use Payload Mediator to transform REST data into SOAP data

         <payloadFactory media-type="xml">
            <format>
               <stud:getMarks xmlns:stud="http://www.example.org/student/">
                  <student>$1</student>
               </stud:getMarks>
            </format>
            <args>
               <arg evaluator="xml" expression="get-property('uri.var.student')"/>
            </args>
         </payloadFactory>

3. Call the back end application / URL

Set Action property and configure the SOAP end point URL

         <header name="Action" value="http://www.example.org/student/getMarks"/>
     <send>
            <endpoint>
<address uri="http://localhost:8088/services/Student" format="soap11"/>
            </endpoint>
         </send>

4. Transform response SOAP data into REST data (for example JSON)

Now, you will get the response in SOAP format. Use Payload Mediator to transform into REST data i.e., JSON

         <payloadFactory media-type="json">
            <format>{"Language_1": "$1", "Language_2": "$2", "Maths": "$3", "Science": "$4"}</format>
            <args>
               <arg xmlns:stud="http://www.example.org/student/"
                    evaluator="xml"
                    expression="$body/stud:getMarksResponse/marks/Lang-I"/>
               <arg xmlns:stud="http://www.example.org/student/"
                    evaluator="xml"
                    expression="$body/stud:getMarksResponse/marks/Lang-II"/>
               <arg xmlns:stud="http://www.example.org/student/"
                    evaluator="xml"
                    expression="$body/stud:getMarksResponse/marks/Mathematics"/>
               <arg xmlns:stud="http://www.example.org/student/"
                    evaluator="xml"
                    expression="$body/stud:getMarksResponse/marks/Science"/>
            </args>
         </payloadFactory>

5. Respond with JSON response


Change the response type to JSON and send the response to REST client

         <property name="messageType" value="application/json" scope="axis2"/>
         <send/>









When you execute, the output will look like
{
  "Language_1": "92",
  "Language_2": "94",
  "Maths": "95",
  "Science": "97"
}

See below the complete API definition

<?xml version="1.0" encoding="UTF-8"?>
<api xmlns="http://ws.apache.org/ns/synapse"
     name="StudentAPI"
     context="/exam-results">
   <resource methods="GET" uri-template="/student/{student}">
      <inSequence>
         <log level="custom">
            <property name="student_name_##########"
                      expression="get-property('uri.var.student')"/>
         </log>
         <payloadFactory media-type="xml">
            <format>
               <stud:getMarks xmlns:stud="http://www.example.org/student/">
                  <student>$1</student>
               </stud:getMarks>
            </format>
            <args>
               <arg evaluator="xml" expression="get-property('uri.var.student')"/>
            </args>
         </payloadFactory>
         <header name="Action" value="http://www.example.org/student/getMarks"/>
         <property name="Accept-Encoding" scope="transport" action="remove"/>
         <log level="custom">
            <property name="#body" expression="$body/*"/>
         </log>
         <send>
            <endpoint>
               <address uri="http://localhost:8088/services/Student" format="soap11"/>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <log level="custom">
            <property name="#response-body-from-Backend" expression="$body/*"/>
         </log>
         <payloadFactory media-type="json">
            <format>{"Language_1": "$1", "Language_2": "$2", "Maths": "$3", "Science": "$4"}</format>
            <args>
               <arg xmlns:stud="http://www.example.org/student/"
                    evaluator="xml"
                    expression="$body/stud:getMarksResponse/marks/Lang-I"/>
               <arg xmlns:stud="http://www.example.org/student/"
                    evaluator="xml"
                    expression="$body/stud:getMarksResponse/marks/Lang-II"/>
               <arg xmlns:stud="http://www.example.org/student/"
                    evaluator="xml"
                    expression="$body/stud:getMarksResponse/marks/Mathematics"/>
               <arg xmlns:stud="http://www.example.org/student/"
                    evaluator="xml"
                    expression="$body/stud:getMarksResponse/marks/Science"/>
            </args>
         </payloadFactory>
         <property name="messageType" value="application/json" scope="axis2"/>
         <log level="custom">
            <property name="#body" expression="$body/*"/>
         </log>
         <send/>
      </outSequence>
   </resource>
</api>

Hope this is useful to you. Please let me know if any further help.