Skip to main content

WSO2 API Manager- Extended Mediation Capabilities on APIs -Part1

After a while,thought to write a blog-post about how we can use extended mediation capabilities with the published APIs from WSO2 APIManager.

Requirement- A back-end  endpoint with returning xml content need to wrap with an API from WSO2 APIManager to give additional security,throttling and monitoring capabilities for it.

For this blog-post,as the back-end endpoint,I have used the sample JAX-RS based web-app which can be found from here,deployed in WSO2 AS 5.2.1.You can try downloading WSO2 AS 5.2.1 and try deploying this web-app as instructed in here.I have started AS with port offset 2.Thus the deployed jax-rs web-app url is http://localhost:9765/Order-1.0/
This jax-rs web app supports following HTTP verbs with the url-patterns;

POST  /submitOrder    Input & Output content-type : text/xml

Input Payload:


<Order>
<customerName>Jack</customerName>
<quantity>5</quantity>
<creditCardNumber>233</creditCardNumber>
<delivered>false</delivered>
</Order>

GET    /orderStatus     Output content-type : text/xml
GET    /cancelOrder    Output content-type : text/xml



Then download the latest WSO2 AM 1.7.0 binary pack from here. With AM 1.7.0 we have done a major re-design the APIPublisher UI,in which allowing users to get the experience of designing APIs addition to implement APIs and manage APIs as previous AM publisher  versions are only focus on implement and manage APIs.
Start AM server,log in to APIPublisher app and create an API with below details;For more information,refer the quick start guide,
In Design API view,enter below parameters.

  • Name -order
  • Context -order
  • Version-v1

  Under Resources section,define following three API resources.

  •   URL-Pattern - submitOrder
              HTTP Verb - POST
             
  •   URL-Pattern -cancelOrder/{id}
              HTTP Verb- GET

  •  URL-Pattern - orderStatus/{id}
             HTTP Verb- GET

  •  URL-Pattern - confirmCancelOrder/*
             HTTP Verb- GET

a.png

   4) Then save the design API view content.Then click on ‘Implement’ button.

   5) Enter  above deployed JAXRS web app url [http://localhost:9765/Order-1.0/] as the production endpoint value with setting endpoint type as ‘HTTP Endpoint’.

 6) Then save the details and next click on ‘manage’ button.
 7) Select ‘Tier Availability’ as ‘Unlimited’


8) Set the Authentication Type for all API resources as ‘Application &amp; Application User’

9) Click ‘save & publish’ option in it.

Once created the above API and publish the API,you'll see a xml configuration named as admin--Order_v1.xml has been created at {AM}/repository/deployment/server/synapse-configs/default/api location.

<?xml version="1.0" encoding="UTF-8"?><api xmlns="http://ws.apache.org/ns/synapse" name="admin--Order" context="/Order" version="1" version-type="url">
    <resource methods="POST" url-mapping="/submitOrder">
        <inSequence>
           <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                   <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://10.100.1.85:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence>
            <send/>
        </outSequence>
    </resource>
    <resource methods="GET" uri-template="/cancelOrder/{id}">
        <inSequence>
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                 
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence>
            <send/>
        </outSequence>
    </resource>
    <resource methods="GET" uri-template="/orderStatus/{id}">
        <inSequence>
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                   <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://10.100.1.85:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence>
            <send/>
        </outSequence>
    </resource>
    <handlers>      
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler"/>        
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.throttling.APIThrottleHandler">
            <property name="id" value="A"/>
            <property name="policyKey" value="gov:/apimgt/applicationdata/tiers.xml"/>
        </handler>      
        <handler class="org.wso2.carbon.apimgt.usage.publisher.APIMgtGoogleAnalyticsTrackingHandler">
            <property name="configKey" value="gov:/apimgt/statistics/ga-config.xml"/>
        </handler>
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.ext.APIManagerExtensionHandler"/>
    </handlers>
</api>

Once publish the API,browse APIStore and create a subscription for this API and generate a application token from APIstore. 

Now let's try to invoke /submitOrder method of API. 

A sample curl request would be as ;

curl -d @payload.xml -H "Authorization:Bearer xxxxx" -H "Content-Type:text/xml" http://localhost:8280/Order/1/submitOrder 

payload.xml content -

<Order> <customerName>Jack</customerName> <quantity>5</quantity> <creditCardNumber>233</creditCardNumber> <delivered>false</delivered> </Order>

You'll observe a response similar to below.

<Order> <creditCardNumber>233</creditCardNumber> <customerName>Jack</customerName> <date>06/24/2014 08:43:52</date> <delivered>false</delivered> <orderId>a4c1315d-8a07-4e80-85b1-3795ab47db7a</orderId> <quantity>5</quantity> </Order>

New Requirement 1

Now,let's say you want to make the above Order API as a json/REST API.Thus the input and output has to be in json format.For this,you have to change the Order API xml content.Since AM 1.7.0 doesn't provide mediation UI capabilities,you can try directly editing the deployed api xml file located at {AM}/repository/deployment/server/synapse-configs/default/api.

Replace json message formatter and message builder


Replace below message-formatter and builder to axis2.xml of {AM}/repository/conf/axis2/ location and restart AM server.


Message Formatter

<messageFormatter contentType="application/json" class="org.apache.synapse.commons.json.JsonFormatter"/>


Message Builder

<messageBuilder contentType="application/json"
                       class="org.apache.synapse.commons.json.JsonBuilder"/>

To set response to be json format in /submitOrder resource of Order API.
Set messageType and content-type as ‘application/json’ in out-sequence of /submitOrder resource.

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

To accept json formatted inputs for /submitOrder API resource
To pass the payload as json from client and then convert that payload from json to xml in APIManager side,we have added below payload factory inside ‘/submitOrder’ API resource.

<payloadFactory media-type="xml"> <format> <Order> <customerName>$1</customerName> <quantity>$2</quantity> <creditCardNumber>$3</creditCardNumber> <delivered>$4</delivered> </Order> </format> <args> <arg expression="$.Order.customerName" evaluator="json"></arg> <arg expression="$.Order.quantity" evaluator="json"></arg> <arg expression="$.Order.creditCardNumber" evaluator="json"></arg> <arg expression="$.Order.delivered" evaluator="json"></arg> </args> </payloadFactory>

The changed order API is as below.
<?xml version="1.0" encoding="UTF-8"?><api xmlns="http://ws.apache.org/ns/synapse" name="admin--Order" context="/Order" version="1" version-type="url">
    <resource methods="POST" url-mapping="/submitOrder">
        <inSequence> 
<payloadFactory media-type="xml">
           <format>
           <Order>
  <customerName>$1</customerName>
  <quantity>$2</quantity>
  <creditCardNumber>$3</creditCardNumber>
  <delivered>$4</delivered>
</Order>

           </format>
           <args>           
            <arg expression="$.Order.customerName" evaluator="json"></arg>
            <arg expression="$.Order.quantity" evaluator="json"></arg>
            <arg expression="$.Order.creditCardNumber" evaluator="json"></arg>
<arg expression="$.Order.delivered" evaluator="json"></arg>
           </args>
           </payloadFactory>
 
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                   <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://localhost:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence>
 <property name="messageType" value="application/json" scope="axis2"/>
 <property name="ContentType" value="application/json" scope="axis2"/>
            <send/>
        </outSequence>
    </resource>
    <resource methods="GET" uri-template="/cancelOrder/{id}">
        <inSequence>
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                 <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://localhost:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>  
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence> 
            <send/>
        </outSequence>
    </resource>
    <resource methods="GET" uri-template="/orderStatus/{id}">
        <inSequence>
            <property name="POST_TO_URI" value="true" scope="axis2"/>
            <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
                <then>
                   <send>
                        <endpoint name="admin--TTT_APIproductionEndpoint_0" >
                            <address uri="https://localhost:9463/services/orderSvc/" format="soap11">
                                <timeout>
                                    <duration>30000</duration>
                                    <responseAction>fault</responseAction>
                                </timeout>
                                <suspendOnFailure>
                                    <errorCodes>-1</errorCodes>
                                    <initialDuration>0</initialDuration>
                                    <progressionFactor>1.0</progressionFactor>
                                    <maximumDuration>0</maximumDuration>
                                </suspendOnFailure>
                                <markForSuspension>
                                    <errorCodes>-1</errorCodes>
                                </markForSuspension>
                            </address>
                        </endpoint>
                    </send>
                </then>
                <else>
                    <sequence key="_sandbox_key_error_"/>
                </else>
            </filter>
        </inSequence>
        <outSequence> 
            <send/>
        </outSequence>
    </resource>
    <handlers>        
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler"/>      
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.throttling.APIThrottleHandler">
            <property name="id" value="A"/>
            <property name="policyKey" value="gov:/apimgt/applicationdata/tiers.xml"/>
        </handler>        
        <handler class="org.wso2.carbon.apimgt.usage.publisher.APIMgtGoogleAnalyticsTrackingHandler">
            <property name="configKey" value="gov:/apimgt/statistics/ga-config.xml"/>
        </handler>
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.ext.APIManagerExtensionHandler"/>
    </handlers>
</api>

Now let's try to invoke /submitOrder method of API. A sample curl request would be as ; 
curl -d @payload.json "Authorization:Bearer xxxxx" -H "Content-Type:application/json"  http://localhost:8280/Order/1/submitOrder

payload.json content -
{"Order":{"customerName:"PP_88","quantity":"8" ,".creditCardNumber":"1234","delivered":"true"}}
Response would be as below.
{"Order":{"orderId:"a4c1315d-8a07-4e80-85b1-3795ab47db7a","date:"06/24/2014 08:43:52","customerName:"PP_88","quantity":"8" ,".creditCardNumber":"1234","delivered":"true"}}

Comments

Popular posts from this blog

Convert an InputStream to XML

For that we can use DocumentBuilder class in java. By using the method parse(InputStream) ; A new DOM Document object will return. InputStream input; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder parser = factory.newDocumentBuilder(); Document dc= parser.parse(input); In the above code segment,by using the created Document object,the corresponding XML file for the inputStream can be accessed. References: http://www.w3schools.com/dom/dom_intro.asp http:// download.oracle.com/javase/1.4.2/docs/api/javax/xml/parsers/DocumentBuilder.html

CORS support from WSO2 API Manager 2.0.0

Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources  on a web page to be requested from another domain outside the domain from which the first restricted resource was served. For example, an HTML page of a web application served from http://domain-a.com makes an <img src >  request for a different domain as 'domain-b.com' to get an image via an API request.  For security reasons, browsers restrict cross-origin HTTP requests initiated from within scripts as in above example and only allows to make HTTP requests to its own domain. To avoid this limitation modern browsers have been used CORS standard to allow cross domain requests. Modern browsers use CORS in an API container - such as  XMLHttpRequest  or Fetch - to mitigate risks of cross-origin HTTP requests.Thing to  note is it's not only sufficient that the browsers handle client side of cross-origin sharing,but also the servers from which these resources getting need to handl

Passing end-user details from client to real backend endpoint via JWT token

In real-world business system,WSO2 API Manager useful on exposing company APIs, in a secured and controlled manner with the features provided by APIManager as; OAuth support [To secure API invocations] Throttling support [To control API invocations] Monitoring support [To track API usage] More technically what happening is when a user sends a particular API request,it will goes to WSO2 APIManager node and from there,the request will route to the real implemented back-end endpoint of the particular API and get back the response and returned it to the API invoked user. There can be a use-case,that this back-end endpoint may expect the details of API invoked user as to pass those details to some internal company usage  as; Additional authentication/authorization Track usage data from an internal system. So how to support above requirement from WSO2 AM. There comes the use of JSON Web Token[JWT] implementation done inside WSO2 AM. JWT is a means of representing claims to be