Ultima revisión 01/02/2013
Errores comunes cuando se trabaja con SOAP
Hay mucho errores, sobre todo al principio, cuando trabajamos en archivos de descripción WSDL.
A continuación os muestro una lista con los errores más frecuentes y sus respectivas explicaciones y soluciones para evitar futuros dolores de cabeza. Cada vez que me encuentre con uno lo añadiré a este artículo a ver si, con el tiempo, consigo que sea una buena fuente de ayuda. De momento, ya os dejo varios.
Listado de errores analizados
- Cannot use object of type stdClass as array php
- DTD are not supported by SOAP
- Looks like we got no xml document
- Se envía un Array Multidimensional y se recibe un Array de Objetos
- Call to a member function addSoapHeader() on a non-object
- Llamamos a una función de nuestro Servicio Web y, sin embargo, se ejecuta otra
Cannot use object of type stdClass as array php
Se produce cuando se utiliza la forma de acceder a la información como un array ( $array['item'] ) en vez de como un objeto. La forma correcta es $array->item.
DTD are not supported by SOAP
Se está devolviendo una página, probablemente en HTML, debido a un error interno del Servidor, 404, 500, ... También puede ser porque existe un error de sintaxis en los ficheros asociados al SOAP Client o SOAP Server.
Looks like we got no xml document
Normalmente es uno de los siguiente problemas.
- El WSDL está mál formateado.
- El fichero asociado al SOAP Server tiene uno o varios errores de sintaxis.
- Se está llamando al mismo mensaje por varios métodos u operaciones. La regla que se debe seguir es un MENSAJE, una OPERACIÓN.
Se envía un Array Multidimensional y se recibe un Array de Objetos
El problema, normalmente, está en el atributo use de la operación del elemento binding que está establecido como literal. La solución es cambiarlo a encoded como se muestra a continuación:
<soap:body use='literal' />
por:
<soap:body use='encoded' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' />
Call to a member function addSoapHeader() on a non-object
Este error se provoca cuando se establece la clase que definirá los métodos del Web Service, concretamente al establecer el setClass. En este método hay que indicarle el resource asignado al SOAP Server. Si este no se recibe correctamente la solución es llamar a la función a través de $GLOBALS['<nombre_resource_servidor>'].
class Prueba{
protected static $_SOAPSERVER = NULL;
protected static $_HEADERVARS = "";
protected static $_HEADER = "";
function __construct(){
self::$_SOAPSERVER = $GLOBALS['soap_server'];
}
/**
* Function for set from, to and function fields in to header of SOAP message.
* @param string $from The username or string to include in 'from' field.
* @param string $to The username or string to include in 'to' field.
* @param string $function String to include in 'function' field.
*/
public function addHeaders(){
self::$_HEADERVARS = new SoapVar(array('to' => "user", 'from' => "IslaVisual WSDL"), SOAP_ENC_OBJECT);
self::$_HEADER = new SoapHeader('http:/www.islavisual.com/?wsdl', "operacion_prueba", self::$_HEADERVARS);
self::$_SOAPSERVER->addSoapHeader(self::$_HEADER);
}
}
$soap_server = new SoapServer("http://www.islavisual.com/?wsdl");
$soap_server->setClass("Prueba", $soap_server);
$soap_server->addFunction(SOAP_FUNCTIONS_ALL);
$soap_server->handle();
Ahora ya se puede llamar a la función que inserta las cabeceras desde cualquier método para cualquier operación del Web Service.
Llamamos a una función de nuestro Servicio Web y, sin embargo, se ejecuta otra
Cuando usamos el estilo predefinido document, puede suceder que llamemos a una función de nuestro Servicio Web y, sin embargo, se ejecute otra. Por raro que parezca, si lo analizamos un poco no será de extrañar.
Ocurre cuando tenemos 2 métodos que tienen el mismo número de parámetros de entrada que, además, coinciden en nombre y tipo. Al no haber reglas, si el documento WSDL se encuentra con dos métodos que llaman distinto pero tienen los mismos parámetros de entrada, el segundo método lo tomará como un error y se hará internamente un 'alias' al primero.
El motivo de por qué se produce este conflicto está en el concepto de namespace y de cómo se envía la información. Cuando se realiza una petición SOAP, en la cabecera de la llamada, se envían los parámetros como se definirieron en el WSDL. Si los parámetros de varias funciones de llaman igual, lo que sucederá es que se tomará como petición el primer mensaje del listado de mensajes que coincida con los parámetros enviados. Por esta razón, es que no se pueden referenciar varios part iguales en message distintos.
La forma de solucionarlo, es cambiar el nombre del parámetro dentro del WSDL o ponerles a cada uno un prefijo o sufijo distinto.
Supongamos queremos que un Servicio Web realice la operaciÓ de MOSTRAR LA DESCRIPCIÓN DE UN MODELO DE COCHE y la operación de MOSTRAR SU DISPONIBILIDAD. Necesitaríamos en el WSDL lo siguiente:
NOTA: Sólo mostramos la parte que nos interesa. Lo demás sería igual que cualquier otro WS.
<message name='descripcionRequest'>
<part name='marca' type='xsd:string' />
<part name='desc.modelo' type='xsd:string' />
</message>
<message name='disponibilidadRequest'>
<part name='marca' type='xsd:string' />
<part name='disp.modelo' type='xsd:string' />
</message>
<operation name='descripcion'>
<input message='xsd:descripcionRequest' />
<output message='tns:descripcionResponse' />
</operation>
<operation name='disponibilidad'>
<input message='xsd:disponibilidadRequest' />
<output message='tns:disponibilidadResponse' />
</operation>
Si no pusiésemos los prefijos en la propiedad name de los elementos message, aunque llamásemos a la operación de DISPONIBILIDAD, siempre se ejecutaría DESCRIPCIÓN ya que es la primera. De esta manera, con los prefijos, lo evitamos y el Web Service ya funciona correctamente.