This site contains the documentation that is relevant to older WSO2 product versions and offerings.
For the latest WSO2 documentation, visit https://wso2.com/documentation/.

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 10 Next »

This page describes how to configure REST APIs for the following use cases:

Setting query parameters on outgoing messages

REST clients use query parameters to provide inputs for the relevant operation. These query parameters may be required to carry out the back-end operations either in a REST service or a proxy service. Let’s take a sample request and see how these parameters can be set in the outgoing message.

curl -v -H "Content-Type: application/xml" -d "<Customer><id>123</id><name>John</name></Customer>" http://localhost:8282/customerservice/customers?param1=value1&m2=value2

In this request, there are two query parameters (customer name and ID) that must be set in the outgoing message from the ESB. We can configure the API to set those parameters as follows:

<definitions>

<api name="StockQuoteAPI" context="/stockquote">

 <resource uri-template="/view/{symbol}" methods="GET">

    <inSequence>

       <payloadFactory>

       <format>

       <m0:getQuote xmlns:m0="http://services.samples">

               <m0:request>

                     <m0:symbol>$1</m0:symbol>

                 <m0:customerName>$2</m0:customerName>

                 <m0:customerId>$3</m0:customerId>

               </m0:request>

           </m0:getQuote>

       </format>

       <args>

           <arg expression="get-property('uri.var.symbol')"/>

           <arg expression="get-property('query.param.param1')"/>

           <arg expression="get-property('query.param.param2')"/>

       </args>

    </payloadFactory>

    <send>

       <endpoint>

       <address uri=" http://localhost:9000/services/SimpleStockQuoteService " format="soap11"/>

       </endpoint>

    </send>

    </inSequence>

    <outSequence>

    <send/>

    </outSequence>

 </resource>

</api>

</definitions>

The query parameter values can be accessed through the "get-property" function by specifying the parameter number as highlighted in the above request.  

Content negotiation

Content negotiation is a mechanism by which a client and a server communicate with each other and decide on a content format to use for data transfer. In our samples so far, we used XML (application/xml) as the primary means of data transfer. However we could use other content formats such as plain text and JSON to achieve the same result.

Real-world client applications usually have preferred content types. For example:

  • A web browser usually prefers HTML.

  • A Java-based desktop application usually prefers plain-old XML (POX).

  • A mobile application usually prefers JSON.

To maintain interoperability, the server-side applications should be prepared to serve content using any of these formats. Using content negotiation, the client can indicate its content type preferences to the server, and the server can serve the requests using a content type preferred by the client.

The HTTP specification provides the basic elements to build a powerful content negotiation framework. The HTTP client can indicate its content type preferences by sending the Accept header along with the requests. First, we need to retrieve the value of the Accept header sent by the client. We do this in the in-sequence as follows:

<property name="TEST_CLIENT_ACCEPT" expression="$trp:Accept"/>

Now in the out-sequence we can run a few comparisons to see which content type to use for sending the response:

<switch source="$ctx:TEST_CLIENT_ACCEPT">
        <case regex=".*atom.*">
                <send/>
        </case>
        <case regex=".*text/html.*">
                 <send/>
        </case>
        <case regex=".*json.*">
                 <send/>
        </case>
        <case regex=".*application/xml.*">
                 <send/>
        </case>
        <default>
                 <send/>
        </default>
</switch>

The above configuration results in the following priority order of content types.

  1. Atom (Also used as default)

  2. HTML

  3. JSON

  4. POX

To try this out, invoke the following Curl commands and see how the response format changes according to the value of the Accept header.

curl -v http://localhost:8281/orders
curl -v -H "Accept: application/xml" http://localhost:8281/orders
curl -v -H "Accept: application/json" http://localhost:8281/orders
curl -v -H "Accept: text/html" http://localhost:8281/orders

Transforming the content type

This section describes how you can transform the content type of a message using the REST API. In this scenario, the REST API exposes a REST back-end service that accepts and returns XML and JSON messages for HTTP methods as follows:

  • GET - response is in JSON format

  • POST - accepts XML request and returns response in JSON format

  • PUT - accepts JSON request and returns response in JSON format

  • DELETE - empty request body should be sent

Transformation in HTTP GET:


Transformation in HTTP POST:

Transformation in HTTP PUT:

Setting up the back end

For the REST back-end service, we will use a modified version of the JAX-RS Advanced sample, which is available in the WSO2 Application Server. The sample is modified to avoid complications in content types when integrating with WSO2 ESB. The only difference is that the 'serviceURL' of the sample used in this use case will be 'http://localhost:9763/StarbucksService/services/Starbucks_Outlet_Service'.

When using Application Server 5.1.0 or later, be sure to add the CXF classloading environment to the <AS_HOME>/repository/conf/tomcat/webapp-classloading.xml file as in the following configuration (see Webapp Classloading for details on configuring class loading in Application Server):

<Classloading xmlns="http://wso2.org/projects/as/classloading">
    <ParentFirst>false</ParentFirst>
    <Environments>CXF,Carbon</Environments>
</Classloading>

Since we are going to run both WSO2 ESB and the WSO2 Application Server on the same machine, offset the ports of the application server by changing the offset value to 1 in <AS_HOME>/repository/conf/carbon.xml.

To deploy the REST back-end, download the StarbucksService.war from https://svn.wso2.org/repos/wso2/people/charitham/REST-API/ StarbucksService.war) and deploy it in Application Server.

Configuring the REST API

Create a REST API using the following configuration:

<api name="StarbucksService" context="/Starbucks_Service">
    <resource methods="POST" url-mapping="/orders/add">
        <inSequence>
            <property name="REST_URL_POSTFIX" scope="axis2" action="remove"/>
            <send>
                <endpoint>
                    <address uri="http://localhost:9764/StarbucksService/services/Starbucks_Outlet_Service/orders/"/>
                </endpoint>
            </send>
        </inSequence>
        <outSequence>
            <log level="full"/>
            <property name="messageType" value="application/xml" scope="axis2"/>
            <send/>
        </outSequence>
    </resource>
    <resource methods="PUT" url-mapping="/orders/edit">
        <inSequence>
            <log level="full"/>
            <property name="REST_URL_POSTFIX" scope="axis2" action="remove"/>
            <property name="messageType" value="application/json" scope="axis2"/>
            <property name="ContentType" value="application/json" scope="axis2"/>
            <send>
                <endpoint>
                    <address uri="http://localhost:9764/StarbucksService/services/Starbucks_Outlet_Service/orders/"
                             format="rest"/>
                </endpoint>
            </send>
        </inSequence>
        <outSequence>
            <log level="full"/>
            <property name="messageType" value="application/xml" scope="axis2"/>
            <send/>
        </outSequence>
    </resource>
    <resource methods="DELETE GET" uri-template="/orders/{id}">
        <inSequence>
            <property name="REST_URL_POSTFIX"
                      expression="fn:concat('/orders/',get-property('uri.var.id'))"
                      scope="axis2"/>
            <send>
                <endpoint>
                    <address uri="http://localhost:9764/StarbucksService/services/Starbucks_Outlet_Service/"/>
                </endpoint>
            </send>
        </inSequence>
        <outSequence>
            <log level="full"/>
            <property name="messageType" value="application/xml" scope="axis2"/>
            <send/>
        </outSequence>
    </resource>
</api>

Executing the sample

The context of the REST API is ‘/Starbucks_Service’. For every HTTP method, a url-mapping or uri-template is defined, and the URL to call the methods differ with the defined mapping or template.

Following is the CURL command to send a GET request to the REST API:

curl -v -X GET http://localhost:8280/Starbucks_Service/orders/123

The response from the back end to the ESB will be:

{"Order":{"additions":"Milk","drinkName":"Vanilla Flavored Coffee","locked":false,"orderId":123}}

The ESB transforms this response to XML and send it back as:

<Order>
    <additions>Milk</additions>
    <drinkName>Vanilla Flavored Coffee</drinkName>
    <locked>false</locked>
    <orderId>123</orderId>
</Order>

Following is the cURL command to send an HTTP POST request to the API:

curl -v -H "Content-Type: application/xml" -X POST -d @placeOrder.xml http://localhost:8280/Starbucks_Service/orders/add

where placeOrder.xml has the following content on the order:

<Order>
    <drinkName>Mocha Flavored Coffee</drinkName>
    <additions>Caramel</additions>
</Order>

This XML request will be sent to the back end, which will send back a JSON response to the ESB:

{"Order":{"additions":"Caramel","drinkName":"Mocha Flavored Coffee","locked":false,"orderId":"d088a289-1be3-453b-ab37-7609828d2197"}}

The ESB converts this response to XML and sends it back to the client:

<Order>    <additions>Caramel</additions>
    <drinkName>Mocha Flavored Coffee</drinkName>
    <locked>false</locked>
    <orderId>d088a289-1be3-453b-ab37-7609828d2197</orderId>
</Order>

Following is the cURL command for sending an HTTP PUT request:

curl -v -H "Content-Type: application/xml" -X PUT -d @editOrder.xml http://localhost:8280/Starbucks_Service/orders/edit

where editOrder.xml has the following syntax:

<Order>
    <orderId>d088a289-1be3-453b-ab37-7609828d2197</orderId>
    <additions>Chocolate Chip Cookies</additions>
</Order>

The ESB will convert this request to JSON and send it to the back end. The response will be in JSON format:

{"Order":{"additions":"Chocolate Chip Cookies","drinkName":"Mocha Flavored Coffee","locked":false,"orderId":"d088a289-1be3-453b-ab37-7609828d2197"}}

The ESB converts this response to XML and sends it back to the client:

<Order>    <additions>Chocolate Chip Cookies</additions>
    <drinkName>Mocha Flavored Coffee</drinkName>
    <locked>false</locked>
    <orderId>d088a289-1be3-453b-ab37-7609828d2197</orderId>
</Order>

Following is the cURL command for sending an HTTP DELETE request:

curl -v -X DELETE http://localhost:8280/Starbucks_Service/orders/d088a289-1be3-453b-ab37-7609828d2197  

This request will be sent to the back end, and the order with the specified ID will be deleted.

Enabling REST to SOAP

In this scenario, you expose a SOAP service over REST using an ESB REST API.  


 
 

Setting up the back end

For the SOAP back-end service, we are using the StockQuote Service that is shipped with ESB. Configure and start the back-end service as described in Starting Sample Back-End Services. We will use cURL as the REST client to invoke the ESB REST API.

Configuring the REST API

Following is a sample API configuration that we can used to implement this scenario.

<api name="StockQuoteAPI" context="/stockquote">
   <resource uri-template="/view/{symbol}" methods="GET">
      <inSequence>
         <payloadFactory>
        <format>
        <m0:getQuote xmlns:m0="http://services.samples">
                <m0:request>
                   <m0:symbol>$1</m0:symbol>
                </m0:request>
             </m0:getQuote>
        </format>
        <args>
        <arg expression="get-property('uri.var.symbol')"/>
        </args>
     </payloadFactory>
     <header name="Action" value="urn:getQuote"/>
     <send>
        <endpoint>
        <address uri="http://localhost:9000/services/SimpleStockQuoteService" format="soap11"/>
        </endpoint>
     </send>
      </inSequence>
      <outSequence>
     <send/>
      </outSequence>
   </resource>
   <resource url-mapping="/order/*" methods="POST">
      <inSequence>
        <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/>
    <property name="OUT_ONLY" value="true"/>
    <header name="Action" value="urn:placeOrder"/>
    <send>
            <endpoint>
                <address uri="http://localhost:9000/services/SimpleStockQuoteService" format="soap11"/>
            </endpoint>
         </send>
      </inSequence>      
   </resource>
</api>

Executing the sample

In this API configuration we have defined two resources. One is for the HTTP method GET and the other one is for POST. 

In the first resource, we have defined the uri-template as /view/{symbol} so that request will be dispatched to this resource when you invoke the API using the following URI:

http://127.0.0.1:8280/stockquote/view/IBM  

Following is the cURL command to send this URI:

curl -v http://127.0.0.1:8280/stockquote/view/IBM

The context of this API is stockquote.

The SOAP payload required for the SOAP back-end service is constructed using the payload factory mediator defined in the inSequence. The value for the <m0:symbol> element is extracted using the following expression:

get-property('uri.var.symbol')

Here ‘symbol’ refers to the variable we defined in the uri-template (/view/{symbol}). Therefore, for the above invocation, the 'uri.var.symbol' property will resolve to the value ‘IBM’.

After constructing the SOAP payload, the request will be sent to the SOAP back-end service from the <send> mediator, which has an address endpoint defined inline with the format="soap11" attribute in the address element. The response received from the back-end soap service will be sent to the client in plain old XML (POX) format. 

In the second resource, we have defined the URL mapping as "/order/*". Since this has POST as the HTTP method, the client has to send a payload to invoke this. Following is a sample cURL command to invoke this:

curl -v -d @placeorder.xml -H "Content-type: application/xml" http://127.0.0.1:8280/stockquote/order/

Save the following sample place order request as placeorder.xml in your local file system and execute the command. This payload is used to invoke a SOAP service.  

<placeOrder xmlns="http://services.samples">
  <order>
     <price>50</price>
     <quantity>10</quantity>
     <symbol>IBM</symbol>
  </order>
</placeOrder>

You will see something similar to following line printed in the sample axis2 server (back-end server) console.

Tue Mar 19 09:30:33 IST 2013 samples.services.SimpleStockQuoteService  :: Accepted order #1 for : 10 stocks of IBM at $ 50.0

This SOAP service invocation is an OUT_ONLY invocation, so the ESB is not expecting any response back from the SOAP service. Since we have set the FORCE_SC_ACCEPTED property value to true, the ESB returns a 202 response back to the client. 

Setting HTTP status codes

A REST service typically sends HTTP status codes with its response. When you configure a REST API that send messages to a SOAP back-end service, you can set the status code of the HTTP response within the ESB configuration. To achieve this, set the status code parameter within the out sequence of the API definition:
 

<definitions>

   <api name="StockQuoteAPI" context="/stockquote">

     ...

         <outSequence>

            <property name="HTTP_SC" value="201" scope="axis2" />               

         </outSequence>

    </resource>

  </api>
</definitions>

Now you can try the REST call with the following command.

curl -v http://127.0.0.1:8280/stockquote/view/IBM

The response message will contain the following response code (201) as configured above.
 

< HTTP/1.1 201 Created
< Content-Type: text/xml; charset=UTF-8
< Server: Synapse-HttpComponents-NIO
< Date: Thu, 21 Mar 2013 09:03:00 GMT
< Transfer-Encoding: chunked

Enabling REST to JMS

This section describes how a REST API can receive an HTTP message sent from a REST client and place it on a JMS queue.


  
 

The API receives the HTTP message and sends it to a JMS queue that resides in a message broker. The back-end service listens and picks up the message from this queue.

Setting up the back end

For the back-end service, we are using the StockQuote Service that is shipped with ESB. Configure and start the back-end service as described in Starting Sample Back-End Services. We will use cURL as the REST client to invoke the ESB REST API.

For this sample, we use ActiveMQ 5.5.1 as the message broker. Start ActiveMQ and configure the JMS transport in ESB to work with ActiveMQ before you proceed.

Configuring the REST API

Following is a sample ESB API configuration that we can used to implement this scenario:

<api xmlns="http://ws.apache.org/ns/synapse" name="RestToJmsAPI" context="/stockquote">
   <resource methods="POST" url-mapping="/order/*">
      <inSequence>
         <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/>
         <property name="OUT_ONLY" value="true"/>
         <send>
            <endpoint>
               <address uri="jms:/SimpleStockQuoteService?transport.jms.ConnectionFactoryJNDIName=QueueConnectionFactory&java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory&java.naming.provider.url=tcp://localhost:61616" format="soap11"/>
            </endpoint>
         </send>
      </inSequence>
   </resource>
</api>

In this API configuration, we have set the OUT_ONLY property to true, as it is a one-way invocation. The message is sent to the JMS queue using the JMS Transport sender. (Note the prefix “jms” in the address endpoint URI.) Since we have set the FORCE_SC_ACCEPTED property value to true, the ESB returns a 202 response back to the client. 

Executing the sample

Following is a sample cURL command to invoke this API:

curl -v -d @placeorder.xml -H "Content-type: application/xml" http://127.0.0.1:8280/stockquote/order/

Save the following sample place order request as "placeorder.xml" in your local file system and execute the above command.

<placeOrder xmlns="http://services.samples">
  <order>
     <price>50</price>
     <quantity>10</quantity>
     <symbol>IBM</symbol>
  </order>
</placeOrder>

You will see the something similar to the following printed in the sample axis2 server (back-end server) console:

Tue Mar 19 09:30:33 IST 2013 samples.services.SimpleStockQuoteService  :: Accepted order #1 for : 10 stocks of IBM at $ 50.0

Shut down the back-end server and execute the command again. If you go to the ActiveMQ Console and inspect the SimpleStockQuoteService queue, you will see a message queued there.

For information on enabling a JMS client to send messages to a REST back-end service, see Front-End JMS Client and Back-End REST Service.

Exposing a back-end REST service using a different API

In this scenario, we are exposing a back-end REST service using an ESB REST API. That means we are exposing a different REST API to the client for a REST back-end service.  


 
 

Setting up the back end

For the REST back-end service, we are going to use the JAX-RS Basics sample service available in the WSO2 Application Server 5.0.1. Configure and deploy the JAX-RS Basics sample by following the instructions for building and running the sample on the JAX-RS Basics page. Since we are going to run both WSO2 ESB and the WSO2 Application Server on the same machine, we have to offset the ports of Application Server by changing the offset value to 1 in <AS_HOME>/repository/conf/carbon.xml. 

Configuring the REST API

Following is a sample configuration we can use to configure this scenario:

<api xmlns="http://ws.apache.org/ns/synapse" name="ClientServiceAPI" context="/clientservice">
   <resource methods="OPTIONS DELETE GET" uri-template="/clients/{id}">
      <inSequence>

         <property name="REST_URL_POSTFIX" 
expression="fn:concat('/customers/',get-property('uri.var.id'))" 
scope="axis2"/>
         <send>
            <endpoint>
               <address uri="http://localhost:9764/jaxrs_basic/services/customers/customerservice/"/>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send/>
      </outSequence>
   </resource>
   <resource methods="POST PUT" uri-template="/clients">
      <inSequence>
         <property name="REST_URL_POSTFIX" value="/customers" scope="axis2"/>
         <switch source="$ctx:REST_METHOD">
            <case regex="POST">
               <payloadFactory>
                  <format>
                     <Customer xmlns="">
                        <name>$1</name>
                     </Customer>
                  </format>
                  <args>
                     <arg expression="//addClient/name"/>
                  </args>
               </payloadFactory>
            </case>
            <case regex="PUT">
               <payloadFactory>
                  <format>
                     <Customer xmlns="">
                        <id>$1</id>
                        <name>$2</name>
                     </Customer>
                  </format>
                  <args>
                     <arg expression="//updateClient/id"/>
                     <arg expression="//updateClient/name"/>
                  </args>
               </payloadFactory>
            </case>
         </switch>
         <send>
            <endpoint>
               <address uri="http://localhost:9764/jaxrs_basic/services/customers/customerservice/"/>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send/>
      </outSequence>
   </resource>
</api>

The service URL for the back-end REST service is:

http://localhost:9764/jaxrs_basic/services/customers/customerservice/

The service URL for the REST API (ClientServiceAPI) is:

http://localhost:8280/clientservice/                        

As you can see, we have exposed the back-end REST service in a different context using this REST API. In addition to altering the context, the API is accepting the payload in a different format.

For the HTTP POST method, the back-end REST service accepts the payload in the following format:

<Customer>
   <name>Jack</name>
</Customer>

The ClientServiceAPI accepts the payload in the following format:

<addClient>
   <name>Jack</name>
</addClient>

For the HTTP PUT method, the back-end REST service accepts the payload in the following format:

<Customer>
   <id>123</id>
   <name>John</name>
</Customer>

The ClientServiceAPI accepts the payload in the following format:

<updateClient>
   <id>123</id>
   <name>John</name>
</updateClient>

Conversion of the payload format is done using the <payloadFactory> mediator.

We have now exposed a different REST API for the REST back-end services using an ESB REST API. In the ClientServiceAPI, there are two resources. The first resource is for the GET, OPTIONS, and DELETE HTTP methods. URL postfix is needed for the REST back-end service and is calculated using the following expression:

fn:concat('/customers/',get-property('uri.var.id'))

The calculated URL postfix is appended to the back-end service URL, as we have set that value to the REST_URL_POSTFIX property. Therefore, the final request URL will be something similar to the following:

http://localhost:9764/jaxrs_basic/services/customers/customerservice/customers/123

Executing the sample

Following is the cURL command to send a GET request to the API:

curl -v http://localhost:8280/clientservice/clients/123 

You will see the following line printed as the response:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Customer><id>123</id><name>John</name></Customer>

The second resource is for the POST and PUT HTTP methods. The incoming REST request is filtered using the HTTP_METHOD, and the appropriate payload required for the back-end REST service is constructed using the <payloadFactory> mediator.

Following is the cURL command to send a POST request to the API:

curl -v -d @addClient.xml -H "Content-type: application/xml" http://localhost:8280/clientservice/clients 

Content of the addClient.xml file should be in the following format:

<addClient>
   <name>WSO2</name>
</addClient>

When you execute the above command in the application server console, the following line will be printed:

----invoking addCustomer, Customer name is: WSO2

Following is the cURL command to send a PUT request to the API:

curl -v -d @updateClient.xml -X PUT -H "Content-type: application/xml" http://localhost:8280/clientservice/clients

The content of the updateClient.xml file should be in the following format:

<updateClient>
    <id>123</id>
    <name>ESB</name>        
</updateClient>

When you execute the above command in the application server console, the following line will be printed:

----invoking updateCustomer, Customer name is: ESB

Handling non-matching resources

In this scenario, we are defining a sequence to be invoked if the WSO2 ESB is unable to find a matching resource definition for a specific API invocation. This sequence generates a response indicating an error when no matching resource definition is found.

Setting up the back end

For the back-end service, we are using the StockQuote Service that is shipped with ESB. cURL is used as the REST client to invoke the ESB API. Run Sample 800 as follows.

 For further information on how to run an ESB sample, see ESB Samples Setup.

  1. Execute one of the following commands to start the ESB with the configuration for sample 800.
    • On Windows: wso2esb-samples.bat -sn 800
    • On Linux/Solaris: ./wso2esb-samples.sh -sn 800
  2. Execute one of the following commands to start the the Axis2 server.
    • On Windows: axis2server.bat
    • On Linux/Solaris: ./ axis2server.sh
  3. Run ant from the <ESB_HOME>/samples/axis2Server/src/SimpleStockQuoteService directory.

Configuring the API

Create an API using the following configuration:

<api xmlns="http://ws.apache.org/ns/synapse" name="jaxrs" context="/jaxrs">
   <resource methods="GET" uri-template="/customers/{id}">
      <inSequence>
         <send>
            <endpoint>
               <address uri="http://localhost:9764/jaxrs_basic/services/customers/customerservice"/>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send/>
      </outSequence>
   </resource>
</api> 

Creating the sequence

Create a new sequence with the following configuration:

 <sequence xmlns="http://ws.apache.org/ns/synapse" name="_resource_mismatch_handler_">
   <payloadFactory>
      <format>
         <tp:fault xmlns:tp="http://test.com">
            <tp:code>404</tp:code>
            <tp:type>Status report</tp:type>
            <tp:message>Not Found</tp:message>
            <tp:description>The requested resource (/$1) is not available.</tp:description>
         </tp:fault>
      </format>
      <args>
         <arg xmlns:ns="http://org.apache.synapse/xsd" expression="$axis2:REST_URL_POSTFIX"/>
      </args>
   </payloadFactory>
   <property name="RESPONSE" value="true" scope="default"/>
   <property name="NO_ENTITY_BODY" action="remove" scope="axis2"/>
   <property name="HTTP_SC" value="404" scope="axis2"/>
   <header name="To" action="remove"/>
   <send/>
   <drop/>
</sequence>

Executing the sample

Send an invalid request to the back end as follows:

curl -X GEThttp://localhost:8280/jaxrs/customers-wrong/123

You will get the following response:

<tp:fault xmlns:tp="http://test.com">
<tp:code>404</tp:code>
<tp:type>Status report</tp:type>
<tp:message>Not Found</tp:message>
<tp:description>The requested resource (//customers-wrong/123) is not available.</tp:description>
</tp:fault>
  • No labels