WebService returning model array

Hi there!

This is my first post on this forum and I would like to say that I really like Yii. It helped me a lot developing my latest application. But I’ve ran into some problems with the WebService. It outputs the following:


<?xml version="1.0" encoding="UTF-8"?>

<definitions xmlns="*link deleted*" xmlns:tns="urn:ApiControllerwsdl" xmlns:soap="*link deleted*" xmlns:xsd="*link deleted*" xmlns:wsdl="*link deleted*" xmlns:soap-enc="*link deleted*" name="ApiController" targetNamespace="urn:ApiControllerwsdl">

  <wsdl:types>

    <xsd:schema xmlns:xsd="*link deleted*" targetNamespace="urn:ApiControllerwsdl">

      <xsd:complexType xmlns:xsd="*link deleted*" name="CartArray">

        <xsd:sequence xmlns:xsd="*link deleted*">

          <xsd:element xmlns:xsd="*link deleted*" name="Cart" type="tns:Cart" minOccurs="0" maxOccurs="unbounded"/>

        </xsd:sequence>

      </xsd:complexType>

      <xsd:complexType name="Cart">

        <xsd:all>

          <xsd:element name="cartID" type="xsd:int"/>

          <xsd:element name="create_date" type="xsd:string"/>

        </xsd:all>

      </xsd:complexType>

      <xsd:complexType name="CartItemArray">

        <xsd:sequence xmlns:xsd="*link deleted*a">

          <xsd:element xmlns:xsd="*link deleted*" name="CartItem" type="tns:CartItem" minOccurs="0" maxOccurs="unbounded"/>

        </xsd:sequence>

      </xsd:complexType>

      <xsd:complexType name="CartItem">

        <xsd:all>

          <xsd:element name="cartID" type="xsd:int"/>

          <xsd:element name="itemID" type="xsd:int"/>

          <xsd:element name="amount" type="xsd:int"/>

        </xsd:all>

      </xsd:complexType>

    </xsd:schema>

  </wsdl:types>

  <wsdl:message name="getHelloRequest"/>

  <wsdl:message name="getHelloResponse">

    <wsdl:part name="return" type="xsd:string"/>

  </wsdl:message>

  <wsdl:message name="loginRequest">

    <wsdl:part name="username" type="xsd:string"/>

    <wsdl:part name="password" type="xsd:string"/>

  </wsdl:message>

  <wsdl:message name="loginResponse">

    <wsdl:part name="return" type="xsd:boolean"/>

  </wsdl:message>

  <wsdl:message name="getCartsRequest"/>

  <wsdl:message name="getCartsResponse">

    <wsdl:part name="return" type="tns:CartArray"/>

  </wsdl:message>

  <wsdl:message name="getCartRequest">

    <wsdl:part name="cartID" type="xsd:int"/>

  </wsdl:message>

  <wsdl:message name="getCartResponse">

    <wsdl:part name="return" type="tns:Cart"/>

  </wsdl:message>

  <wsdl:message name="getCartItemsRequest">

    <wsdl:part name="cartID" type="xsd:int"/>

  </wsdl:message>

  <wsdl:message name="getCartItemsResponse">

    <wsdl:part name="return" type="tns:CartItemArray"/>

  </wsdl:message>

  <wsdl:message name="getItemNameRequest">

    <wsdl:part name="itemid" type="xsd:int"/>

  </wsdl:message>

  <wsdl:message name="getItemNameResponse">

    <wsdl:part name="return" type="xsd:string"/>

  </wsdl:message>

  <wsdl:portType name="ApiControllerPortType">

    <wsdl:operation name="getHello">

      <wsdl:documentation>returns a test string</wsdl:documentation>

      <wsdl:input message="tns:getHelloRequest"/>

      <wsdl:output message="tns:getHelloResponse"/>

    </wsdl:operation>

    <wsdl:operation name="login">

      <wsdl:documentation>authenticate user</wsdl:documentation>

      <wsdl:input message="tns:loginRequest"/>

      <wsdl:output message="tns:loginResponse"/>

    </wsdl:operation>

    <wsdl:operation name="getCarts">

      <wsdl:documentation>Returns all carts</wsdl:documentation>

      <wsdl:input message="tns:getCartsRequest"/>

      <wsdl:output message="tns:getCartsResponse"/>

    </wsdl:operation>

    <wsdl:operation name="getCart">

      <wsdl:documentation>Returns a cart</wsdl:documentation>

      <wsdl:input message="tns:getCartRequest"/>

      <wsdl:output message="tns:getCartResponse"/>

    </wsdl:operation>

    <wsdl:operation name="getCartItems">

      <wsdl:documentation>returns all items for a cart</wsdl:documentation>

      <wsdl:input message="tns:getCartItemsRequest"/>

      <wsdl:output message="tns:getCartItemsResponse"/>

    </wsdl:operation>

    <wsdl:operation name="getItemName">

      <wsdl:documentation>Returns the name of the item</wsdl:documentation>

      <wsdl:input message="tns:getItemNameRequest"/>

      <wsdl:output message="tns:getItemNameResponse"/>

    </wsdl:operation>

  </wsdl:portType>

  <wsdl:binding name="ApiControllerBinding" type="tns:ApiControllerPortType">

    <soap:binding style="rpc" transport="*link deleted*"/>

    <wsdl:operation name="getHello">

      <soap:operation soapAction="urn:ApiControllerwsdl#getHello" style="rpc"/>

      <wsdl:input>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:input>

      <wsdl:output>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:output>

    </wsdl:operation>

    <wsdl:operation name="login">

      <soap:operation soapAction="urn:ApiControllerwsdl#login" style="rpc"/>

      <wsdl:input>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:input>

      <wsdl:output>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:output>

    </wsdl:operation>

    <wsdl:operation name="getCarts">

      <soap:operation soapAction="urn:ApiControllerwsdl#getCarts" style="rpc"/>

      <wsdl:input>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:input>

      <wsdl:output>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:output>

    </wsdl:operation>

    <wsdl:operation name="getCart">

      <soap:operation soapAction="urn:ApiControllerwsdl#getCart" style="rpc"/>

      <wsdl:input>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:input>

      <wsdl:output>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:output>

    </wsdl:operation>

    <wsdl:operation name="getCartItems">

      <soap:operation soapAction="urn:ApiControllerwsdl#getCartItems" style="rpc"/>

      <wsdl:input>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:input>

      <wsdl:output>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:output>

    </wsdl:operation>

    <wsdl:operation name="getItemName">

      <soap:operation soapAction="urn:ApiControllerwsdl#getItemName" style="rpc"/>

      <wsdl:input>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:input>

      <wsdl:output>

        <soap:body use="encoded" namespace="urn:ApiControllerwsdl" encodingStyle="*link deleted*"/>

      </wsdl:output>

    </wsdl:operation>

  </wsdl:binding>

  <wsdl:service name="ApiControllerService">

    <wsdl:port name="ApiControllerPort" binding="tns:ApiControllerBinding">

      <soap:address location="*link deleted*"/>

    </wsdl:port>

  </wsdl:service>

</definitions>

Using this WSDL i’ve created a test in PHP:




$wsdlUrl=Yii::app()->request->hostInfo.$this->createUrl('api');

$client=new SoapClient($wsdlUrl);


if(!$client->login("username", "password"))

	die("could not login!");


foreach($client->getCarts() as $cart) {

	$number_of_items = sizeof($client->getCartItems($cart->cartID));

	echo $cart->cartID . " created at " . $cart->create_date . " contains " . $number_of_items . " items<br/>";

	echo "items: <br/>";

	foreach($client->getCartItems($cart->cartID) as $cartitem) {

		echo $client->getItemName($cartitem->itemID) . " (" . $cartitem->amount . ")<br/>";

	}

	echo "<br/>";

}



And this works! The output is as follows:




1 created at 2012-01-16 13:11:14 contains 2 items

items: 

Shampoo (2)

Diepvries pizza (1)


2 created at 2012-01-16 22:01:58 contains 1 items

items: 

Diepvries pizza (2)



Next I wrote this test in C#:




public void TryGetCartsWithLogin()

{

    var service = new ApiControllerService();

    var result = service.login("username", "password");

    Assert.IsTrue(result);


    var carts = service.getCarts();

    Assert.IsNotEmpty(carts);

}



And this fails. It returns an empty array. I’ve determined that the cause is that C# cannot read the array of models.

My question is the following: why can’t C# read the array of models and PHP can?

Thank you in advance for taking the time to read my (very long) first post!

Perhaps this is what’s need to be added?

/Tommy

Thanks for the quick and accurate response Tommy!

I’ve tested your theory and it is correct.

C# doesn’t automaticly start a (full) session when trying to connect to a SOAP webservice.

I’ve replaced the session based login with basic authentication. This results in the following code:




if (!isset($_SERVER['PHP_AUTH_USER'])) {

			header('WWW-Authenticate: Basic realm="My Realm"');

			header('HTTP/1.0 401 Unauthorized');

			exit;

		} else {

			$username = $_SERVER['PHP_AUTH_USER'];

			$password = $_SERVER['PHP_AUTH_PW'];

				

			$this->_identity=new UserIdentity($username,$password);

			if(!$this->_identity->authenticate()) {

				header('WWW-Authenticate: Basic realm="My Realm"');

				header('HTTP/1.0 401 Unauthorized');

				exit;

			} else {

				$this->_identity = User::model()->findByPk($this->_identity->getID());

			}

		}



To login: simply send the login and username in the header. In PHP this will result in the following code:




$wsdlUrl='/the/path/to/API';

$client=new SoapClient($wsdlUrl, array('login' => 'login', 'password' => 'password'));