login button

PHP Web Services: Messaging – SOAP and REST

Story :

Level : Project :

In this article by Samisa Abeysinghe, he discusses various features available in the WSO2 Web Services Framework for PHP that can be used to send and receive messages to and from services and clients. He discusses how to deal with SOAP messages as well as REST style invocations.

 

Applies To

WSO2 WSF/PHP 1.2.0
Environment Windows or Linux

 

Table of Content

  1. Background
  2. SOAP Versions
  3. Rest
  4. SOAP Faults
  5. Summary

 

Background

In a previous article, named “PHP Web Services: After Getting Started”, I introduced an example that dealt with calculating a factorial, and showed how to trace SOAP messages, using the getLastRequest() and getLastResponse() methods of the WSClient class. However, I did not discuss in detail aspects like how to identify the current SOAP versions in use and how to use different SOAP versions. Also, I did not describe how to check if there was a SOAP fault and in case there was one, how to deal with it. In this article I aim to deal with those areas. So, let's dig more into SOAP related features first and then discuss how to do REST.

 

SOAP Versions 

There are two main SOAP versions in use today, SOAP 1.1 and SOAP 1.2. The WSO2 Web services framework for PHP supports both of them. The default SOAP version used by WSF/PHP is SOAP 1.2. In other words, if you do not specify any options related to SOAP version when creating WSClient, then WSClient would use SOAP 1.2 in the request and the service would respond in SOAP 1.2.

 

SOAP Version Namespace Used
SOAP 1.1 http://schemas.xmlsoap.org/soap/envelope/
SOAP 1.2 http://www.w3.org/2003/05/soap-envelope

An interesting question would be that given a SOAP message, how do you identify the SOAP version? Well, the answer is quite simple: just have a look at the XML namespace of the SOAP envelope. As an example the following is a SOAP 1.1 envelope:

 

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header/>
    <soapenv:Body>
        <getFactorial>
            <param>6</param>
        </getFactorial>
    </soapenv:Body>
</soapenv:Envelope>

 

Sometimes, you need to be sensitive to the SOAP version being used, specially when you are using external services, that you did not implement. The service provider may enforce a particular SOAP version that you would then have to use in your requests. We already know how to use SOAP 1.2 with WSF/PHP clients. There is no mystery there, because the default is SOAP 1.2. Let us use the same factorial sample (downloadable from here), that was used in the previous article, and see how we can change it to use SOAP 1.1. All that is required is a miniscule change, i.e. add a new option named useSOAP with the value 1.1, to the options array passed to the WSClient constructor.

$client = new WSClient(array("to" => "http://localhost/tutorial/payload/service.php", "useSOAP" => 1.1));

The rest of the code remains the same. When you run the sample with the above change, you will notice that the messages that you see with trace has the SOAP 1.1 namespace. Now, if you were paying attention, you may have noticed that, you did not need to change anything on the service script to use a different SOAP version. The idea is that with WSF/PHP, the service would use the correct SOAP version, based on the SOAP version used by the client. The number of possible values that could be given to the useSOAP option of the WSClient class is given in the API documentation.

 

REST

The default value assigned to the useSOAP option of the WSClient class is TRUE. This means that we would be sending a SOAP request with SOAP version 1.2. If you take a look at the API documentation, this useSOAP options could also be given a value of FALSE. This means that SOAP should not be used in the request. Then the question arises, what would be sent in the request? Well, in this case, the client would send the payload as it is to the service as the request, without wrapping it in a SOAP envelope. In other words, we would be sending a Plain Old XML (POX) request.

$client = new WSClient(array("to" => "http://localhost/tutorial/payload/service.php", "useSOAP" => FALSE));

If you change the code to reflect the above, i.e. just change useSOAP to FALSE, no SOAP envelope would be sent over the wire. However, if you use getLastRequest() and getLastResponse() methods of WSClient class to trace the messages sent and received, you would still see SOAP envelopes. You see SOAP envelopes because the Apache Axis2/C engine that we are using in WSF/PHP, wraps the POX request in a SOAP envelope to facilitate processing. So when you serialize the XML present after the request and response is processed, it would serialize the internal SOAP envelope representation. However, if you trace the messages with a tool like TCPmon, you will notice that only the payload goes over the wire. The default HTTP method used when useSOAP is set to FALSE is POST. You can use another WSClient option, namely HTTPMethod, to use HTTP GET.

$client = new WSClient(array("to" => "http://localhost/tutorial/payload/service.php/getFactorial", "useSOAP" => FALSE,"HTTPMethod" => "GET"));

Note that, apart form adding the HTTPMethod option, we have also changed the to address of the service, from http://localhost/tutorial/payload/service.php to http://localhost/tutorial/payload/service.php/getFactorial. This is to include the operation name in the URL, which is required in case of a HTTP GET request. The following shows the messages sent and received over the wire with HTTP protocol elements, when you run the code with above changes.

Request:

GET /tutorial/payload/service.php/getFactorial?param=6 HTTP/1.1

User-Agent: Axis2/C

Host: 127.0.0.1:8080

Response: HTTP/1.1 200 OK

Date: Tue, 22 Jan 2008 05:45:49 GMT

Server: Apache/2.2.6 (Win32) PHP/5.2.5

X-Powered-By: PHP/5.2.5

Content-Length: 71

Content-Type: text/xml;charset=UTF-8

 

<getFactorialResponse> <result>720</result> </getFactorialResponse>

 

You can also send a request with your Web browser using the following URL to the PHP service: http://localhost/tutorial/payload/service.php/getFactorial?param=10

And the service would respond with the result 3628800, which is the factorial of 10. Also note that, like in the case of using SOAP 1.1, we did not touch the service script when using REST from client and the service still responded accordingly. This is because with WSF/PHP, service scripts are capable of either using SOAP or REST based on the request format. So you can write one service script and expose it both as SOAP as well as REST with a single deployment instance.

 

SOAP Faults

A Web service could throw a SOAP fault in case of errors on the server side. The client should be able to catch those faults and figure out what exactly went wrong. WSF/PHP has a class named WSFault to represent SOAP faults. On the server side, from the service operations, you can throw a WSFault instance to indicate failure situations. As an example, let us revisit the factorial sample. Say we would want to return a fault in case the user sent a negative number as the parameter to compute the factorial of. This is how you could modify the PHP function to implement it:

function factorial($number) 
{
    if ($number < 0)
    {
       throw new WSFault("Sender", "Cannot compute factorial for negative values");

    }

    if ($number == 0) return 1;

    return $number * factorial($number - 1);
}

 

 

The first parameter to the WSFault constructor is the fault code, and the second is the fault reason. You could also add the fault role and the fault detail to the fault if you wish with constructor parameters. As an example:

if ($number < 0)

{

    throw new WSFault("Sender", "Cannot compute factorial for negative values", "ultimateReceiver ", "You sent $number");

}

A WSFault instance is thrown from the function implementing the service operation, the underneath SOAP engine would trap the information and send a SOAP fault to the client.

Here is an example SOAP envelope with a SOAP fault corresponding to the PHP code shown above:

<env:envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
    <env:body>
        <env:fault>
            <env:code><env:value>soapenv:Sender</env:value></env:code>
            <env:reason>
                <env:text xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace">Cannot compute factorial for negative values</env:text>
           </env:reason>
           <env:detail><error>You sent -1</error></env:detail>
           <env:role>ultimateReceiver </env:role>
        </env:fault>
    </env:body>
</env:envelope>

On the client side, you can use a try/catch block to catch a WSFault instance in case there was a SOAP fault sent from the service. Here is the example code:

try {
$client = new WSClient(array("to" =>
"http://localhost/tutorial/payload/service.php"));
$response = $client->request($requestPayloadString);
$simplexml = new SimpleXMLElement($response->str);
echo "Result = ".$simplexml->result[0]."
"; 
} catch (Exception $e) {
if ($e instanceof WSFault) {
printf("Soap Fault: %s %s\n", $e->Reason, $e->Detail);
} else {
printf("Message = %s\n",$e->getMessage());}
}

Again the concept is very simple. You just wrap the usual client invocation code with a try catch block. In case everything is going well, we could see the result as usual. However, in case of a SOAP fault, control would fall into the catch block, where we could examine the WSFault instance, to figure out the cause of the fault.

 

Summary

In this article, I explained the messaging techniques that you can use with WSF/PHP. When using SOAP, you can change the version of SOAP to be used with simple options with client. On the server side, there is nothing special to be done, services will form the response based on the SOAP version used by the client. Apart from SOAP, you can also use REST style invocations with WSF/PHP. The advantage is that, a single PHP service could be exposed both as a SOAP endpoint and as a REST endpoint, without any additional configuration.

Resources

Download source code for the factorial sample.

 

Links

 

Author

Samisa Abeysinghe is a Software Architect at WSO2. He is a member of Apache Software Foundation and a project management committee member of the Apache Web services project. He is one of the pioneering members of the Apache Axis2/C project. samisa at wso2 dot com

AttachmentSize
payload.zip1.49 KB
0
No votes yet
Tags :