Exposing a RESTful service on the OSB

In this recipe, we will show how to REST-enable an existing service. We will reuse the CRM mock service used in Chapter 1, Creating a Basic OSB Service, but instead of exposing it as a SOAP-based web service as shown in Chapter 1, Creating a Basic OSB Service; we will expose it as a RESTful web service.

For that we first implement a business service that wraps the SOAP-based web service of the CRM system (a soapUI mock service). Then we create a proxy service using the HTTP transport and conditionally executing an action based on the HTTP method passed by the consumer:

Exposing a RESTful service on the OSB

Getting ready

Import the SoapUI project CustomerServiceCRM-soapui-project.xml from the location chapter-5getting-readyexposing-restful-servicesoapui into your SoapUI. Start the mock service CustomerServiceSOAP MockService.

Import the base OSB project containing the right folder structure and the necessary XQuery transformations into Eclipse from chapter-5getting-readyexposing-restful-service.

How to do it...

We will start with the business service, which will wrap the SOAP-based web service of the CRM system (the soapUI mock service).

In Eclipse OEPE, perform the following steps:

  1. In the business folder of the exposing-restful-service project, create a new business service and name it CustomerServiceCRM.
  2. On the General tab select WSDL Web Service for the Service Type and click Browse.
  3. Navigate to CustomerServiceCRM.wsdl from the wsdl folder and select the CustomerManagementSOAP (port).
  4. Click OK and confirm the pop-up window with Yes.
  5. Navigate to the Transport tab and change the Endpoint URI to http://localhost:8088/mockCustomerServiceSOAP.

    Now, we can create the proxy service that will expose the RESTful interface. In Eclipse OEPE, perform the following steps:

  6. In the proxy folder create a new proxy service and name it CustomerService.
  7. On the General tab select Messaging Service for the Service Type option.
  8. Navigate to the Messaging tab and select XML for both Request Message Type and Response Message Type.
  9. Navigate to the Transport tab and select http for the Protocol.
  10. Change the Endpoint URI to /exposing-restful-service/CustomerService.
  11. Navigate to the Message Flow tab.
  12. Drag a Conditional Branch node from the Design palette and drop it below CustomerService.
  13. On the Properties of the Conditional Branch node, navigate to the Flow tab and enter MethodBranch into the Name Field
  14. Navigate to the Conditional Branch tab and click on <XPath> link.
  15. Enter ./ctx:transport/ctx:request/http:http-method/text() into the Expression field and click OK.
  16. Enter inbound into the In Variable field.
  17. On the Conditional Branch node, add three new branches by clicking the Add Branch three times:
    How to do it...
  18. Click on the first branch, branch1 and navigate to the Properties tab.
  19. Enter GET into the Label field.
  20. Leave = selected for the Operator drop-down listbox.
  21. Enter 'GET' (don't forget the quotes) into the Value field
  22. Repeat the last three steps for the other three branches and use PUT, DELETE, and POST for the Label and the Value fields:
    How to do it...

    We have now created the basic structure of our RESTful service with the four HTTP methods representing the CRUD operations. Let's now implement each of the four operations. We start with the GET method, which will invoke the RetrieveCustomerByCriteria operation on the business service.

  23. Drag a Route node and drop it on the GET branch and change the name to RetrieveCustomerRoute.
  24. Right-click on the Route node and select Insert Into | Communication | Routing.
  25. On the Properties of the Routing node, click on Browse and select the business service CustomerServiceCRM.
  26. Select RetrieveCustomerByCriteria for the Invoking drop-down listbox.
  27. Right-click on the Request Action and select Insert Into | Message Processing | Replace.
  28. Enter body into the In Variable and click on <Expression>.
  29. Navigate to the XQuery Resources tab, click on Browse, select the HttpGetToSoap.xq resource from the transformation folder and click OK.
  30. Enter $inbound/ctx:transport/ctx:request/http:query-string/text() into the Binding field of the queryString variable and click OK.
  31. Select the Replace node contents option.
  32. Copy the Replace action and paste it into the Response Action of the same Routing action.
  33. On the Properties, click on the Expression link to replace it by the correct XQuery.
  34. On the XQuery Resources, click on Browse, select the SoapToHttpGet.xq resource from the transformation folder and click OK.
  35. Enter $body/cus:RetrieveCustomerByCriteriaResponse into the Binding field of the retrieveCustomerByCriteriaResponse1 variable.
  36. Add a new namespace and enter cus into the prefix and http://www.crm.org/CustomerService/ into the URI field.
  37. Click OK twice.
  38. Add a Transport Header action after the Replace action in the Response Action.
  39. Select Inbound Response for the Direction drop-down listbox.
  40. Click Add Header.
  41. Select Defined | http | Content-type for the Name part.
  42. Enter 'text/xml; charset=utf-8' into the Set Header to field of the Action part.
  43. Click Add Header.
  44. Select Other in the Name part and enter Content-Length into the field to the right of it.
  45. Enter string-length($body) into the Set Header to field of the Action part.
  46. Click Add Header.
  47. Select Defined | http | Date for the Name part.
  48. Enter fn-bea:dateTime-to-string-with-format("E, dd MMM yyyy hh:mm:ss",fn:current-dateTime()) into the Set Header to field of the Action part.
  49. Click Add Header.
  50. Select Other in the Name part and enter Content-Encoding into the field to the right of it.
  51. Enter 'UTF-8' into the Set Header to field of the Action part.
  52. Click Add Header.
  53. Select Other in the Name part and enter Allow into the field to the right of it.
  54. Enter 'GET, POST, PUT, DELETE' into the Set Header to field of the Action part:
    How to do it...

    We have completed the implementation of the GET operation and the flow should look as shown in the following screenshot:

    How to do it...

    Next let's implement the PUT method, which will invoke the UpdateExistingCustomer operation on the business service.

  55. Copy the RetrieveCustomerRoute node and paste it into the PUT flow.
  56. Change the name to UpdateExistingCustomerRoute.
  57. On the Properties tab of the Routing action, select the UpdateExistingCustomer operation for the Invoking drop-down listbox.
  58. On the Properties tab of the Replace action in the Request Action of the UpdateExistingCustomerRoute, click on the Expression link.
  59. On the XQuery Resources, click on Browse, select the HttpPutToSoap.xq resource from the transformation folder and click OK.
  60. Enter $body/cus1:Customer into the Binding field of the customer1 variable.
  61. Add a new namespace and enter cus1 into the prefix and http://www.somecorp.com/customer into the URI field.
  62. Click OK.
  63. On the Properties of the Replace action in the Response Action flow of UpdateExistingCustomerRoute, click on the Expression link.
  64. Enter the following XML fragment into the Expression field:
    <status>
        <code>OK</code>
        <message>record updated</message>
    </status>

    Next, let's implement the POST method, which will invoke the CreateNewCustomer operation on the business service.

  65. Copy the RetrieveCustomerRoute node and paste it into the POST flow.
  66. Change the name to CreateNewCustomerRoute.
  67. On the Properties tab of the Routing action, select the CreateNewCustomer operation for the Invoking drop-down listbox.
  68. Insert an Assign action into the Request Action flow, right before the Replace action.
    How to do it...
  69. On the Properties tab of the Assign action, click on <Expression> and enter $body into the Expression field.
  70. Enter origRequest into the Variable field.
  71. On the Properties tab of the Replace action in the Request Action of the CreateNewCustomerRoute, click on the Expression link.
  72. On the XQuery Resources, click on Browse, select the HttpPostToSoap.xq resource from the transformation folder and click OK.
  73. Enter $body/cus1:Customer into the Binding field of the customer1 variable.
  74. Add a new namespace and enter cus1 into the prefix and http://www.somecorp.com/customer into the URI field.
  75. Click OK.
  76. On the Properties of the Replace action in the Response Action flow of CreateNewCustomerRoute, click on the Expression link.
  77. On the XQuery Resources, click on Browse, select the SoapToHttpPost.xq resource from the transformation folder and click OK.
  78. Enter $body/cus:CreateNewCustomerResponse into the Binding field of the createNewCustomerResponse1 variable.
  79. Enter $origRequest/cus1:Customer into the Binding field of the customer1 variable and click OK.

    Last but not least, let's implement the DELETE method, which will invoke the DeleteExistingCustomer operation on the business service.

  80. Copy the UpdateExistingCustomerRoute node and paste it into the DELETE flow.
  81. Change the name to DeleteExistingCustomerRoute.
  82. On the Properties tab of the Routing action, select the DeleteExistingCustomer operation for the Invoking drop-down listbox.
  83. On the Properties tab of the Replace action in the Request Action of DeleteExistingCustomerRoute, click on the Expression link.
  84. On the XQuery Resources, click on Browse, select the HttpDeleteToSoap.xq resource from the transformation folder and click OK.
  85. Enter $inbound/ctx:transport/ctx:request/http:query-parameters/http:parameter/@value[$inbound/ctx:transport/ctx:request/http:query-parameters/http:parameter/@name='id'] into the Binding field of the id variable and click OK.
  86. On the Properties of the Replace action in the Response Action of DeleteExistingCustomerRoute, click on the Expression link.
  87. Change message element content to record deleted and click OK.

    Now we have implemented all required HTTP methods for the CRUD operations. But there are two other HTTP methods (OPTIONS and HEADER), which we won't need and which will trigger the default branch. Therefore, we will raise an error to communicate that these methods are not supported.

  88. Add a Pipeline Pair node to the default branch and name it NotSupportedMethodPipeline.
  89. Add a Stage to the Pipeline Pair node and name it RaiseErrorStage.
  90. Insert a Raise Error action into the Stage.
  91. On the Properties of the Raise Error action, enter NOT_SUPPORT_HTTP_METHOD into the Code field and Unsupported HTTP Method into the Message field.
  92. Deploy the project to the OSB server.

The completed message flow definition is shown in the following screenshot:

How to do it...

Now let's test our RESTful service. There are multiple ways for testing a RESTful service.

For testing the GET method, we can use a Web browser and enter the following URL: http://localhost:7001/exposing-restful-service/CustomerService?id=100:

How to do it...

For testing the other implemented HTTP methods, we can use the Service Bus console.

To test the PUT method, perform the following steps in Service Bus console:

  1. In the Project Explorer, navigate to exposing-restful-service | proxy and click on the Launch Test Console icon for the CustomerService proxy service.
  2. Expand the Transport section.
  3. Enter PUT into the http-method field.
  4. Enter the following XML fragment into the Payload field.
    <cus1:Customer xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cus="http://www.crm.org/CustomerService/" xmlns:cus1="http://www.somecorp.com/customer">
        <cus1:ID>1</cus1:ID>
        <cus1:FirstName>Peter</cus1:FirstName>
        <cus1:LastName>Sample</cus1:LastName>
        <cus1:EmailAddress>[email protected]</cus1:EmailAddress>
        <cus1:Addresses>
            <cus1:Address>
                <cus1:Street>Somestreet</cus1:Street>
                <cus1:PostalCode>98999</cus1:PostalCode>
                <cus1:City>Somewhere</cus1:City>
            </cus1:Address>
        </cus1:Addresses>
        <cus1:Rating>A</cus1:Rating>
        <cus1:Gender>M</cus1:Gender>
    </cus1:Customer> 
    
    How to do it...
  5. Click Execute.
  6. The Response Document shows the record updated message and in soapUI a new request has been logged on the mock service.
    How to do it...

Testing the other HTTP methods is similar to the PUT, just replace the HTTP method and pass another message. To test the DELETE and GET, enter the value of the query string (that is, id=100) into the query-string field.

How it works...

In this recipe, we created a REST interface for an existing SOAP service. We have mapped each of the available HTTP methods to one of the CRUD operations of the customer service.

The HTTP methods we implemented are PUT, POST, DELETE, and GET.

Working with the HTTP methods is similar to working with a WSDL with multiple operations. But we can't use an operational branch for handling the different methods; we have to use a conditional branch. The conditional branch decides which HTTP method has been passed and triggers the right behavior.

For each method we have to transform the message we get through the RESTful interface to the message the backend SOAP-based web service expects. For that, we have used the Replace action together with XQuery scripts.

For the GET and DELETE method, all the necessary values are passed in the query string, that is, in the URL of the request. For example, to read the customer with id=100, we have defined and used the following URL:

http://localhost:7001/exposing-restful-service/CustomerService?id=100

The request URL will end up in the $inbound variable, the part with the information about the request is shown here:

<con:transport>
    <con:uri>/exposing-restful-service/CustomerService</con:uri>
    <con:request>
    <tran:headers>
        <http:Content-Type>text/plain; charset=utf-8</http:Content-Type>
    </tran:headers>
    <tran:encoding>utf-8</tran:encoding>
    <http:query-string>id=100</http:query-string>
    <http:query-parameters>
        <http:parameter name="id" value="100"/>
    </http:query-parameters>
    <http:http-method>GET</http:http-method>
    </con:request>
</con:transport>

We can see that the there is a separate element http:query-string only holding the value of the query string. Additionally there is a http:query-parameters collection with one element for each parameter.

In the implementation of the GET method, we have used the http:query-string element to pass the value of the query-string to the XQuery script by using the following XPath expression:

$inbound/ctx:transport/ctx:request/http:query-string/text()

The XQuery then uses the following FLOWER expression to create the criteria structure to be passed to the SOAP web service of the CRM system.

<criterias>
    {
    for $nameValue in fn:tokenize($queryString,"&amp;")
    return 
        <criteria>
            <criteriaField>{ fn:substring-before($nameValue,'=') }
            </criteriaField>
            <criteriaValue>{ fn:substring-after($nameValue,'=') }</criteriaValue>
        </criteria>
    }
</criterias>

The implementation of the DELETE method is using the http:query-parameters collection to retrieve the value of the id parameter using the following expression:

$inbound/ctx:transport/ctx:request/http:query-parameters/http:parameter/@value[$inbound/ctx:transport/ctx:request/http:query-parameters/http:parameter/@name='id']

For the PUT and POST method, the message is passed as an XML fragment, which will end up in the $body variable in the message flow. The following screenshot shows the content of the $body for the PUT request when logged through a Log action.

How it works...

There's more...

In the previous recipe, we used the query string part of the URL to pass the values to the GET and DELETE method. Another way of passing these values is as so-called clean URLs, which are purely structural URLs that do not contain a query string but instead define the path of the resource through the URL. So instead of using the following URL for the GET:

http://localhost:7001/exposing-restful-service/CustomerService?id=100

we could use a clean URL, such as this one:

http://localhost:7001/exposing-restful-service/CustomerService/id/100

Even if the request URL is longer, the existing proxy listening on Endpoint-URI /exposing-restful-service/CustomerService will still be triggered. The interesting part of the $inbound variable in this case would hold the following values:

<con:transport>
    <con:uri>/exposing-restful-service/CustomerService</con:uri>
    <con:request>
        <tran:headers>
            <http:Content-Type>text/plain; charset=utf-8</http:Content-Type>
        </tran:headers>
        <tran:encoding>utf-8</tran:encoding>
        <http:relative-URI>id/100</relative-UR>
        <http:http-method>GET</http:http-method>
    </con:request>
</con:transport>

We can see that the part of the path after the Endpoint-URI of the proxy can be found in the http:relative-URI element. Using a tokenize function, it's easy to access the different values, for example, for the value of the id we could use:

fn:tokenize($inbound/ctx:transport/ctx:request/http:relative-URI/text(),'/')[2]

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset