2010-03-05 39 views
15

Así que estoy tratando de acceder a esta api https://www.clarityaccounting.com/api-docs/ usando SUDS. Aquí está el código que debería funcionar:¿Puede ayudarme a resolver este problema SUDS/SOAP?

from suds.client import Client 
client = Client('https://www.clarityaccounting.com/api/v1?wsdl') 
token = client.service.doLogin('demo', 'demo', 'www.kashoo.com', 'en_US', 300000) 

pero me sale este error:

WebFault: Server raised fault: 'No such operation: (HTTP GET PATH_INFO: /api/v1)' 

Su individuo de la ayuda dice que la solicitud debe tener este aspecto:

<SOAP-ENV:Envelope 
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:api="http://api.service.books/"> 
    <SOAP-ENV:Body> 
    <api:doLogin> 
     <username>demo</username> 
     <password>demo</password> 
     <siteName>www.kashoo.com</siteName> 
     <locale>en_US</locale> 
     <duration>300000</duration> 
    </api:doLogin> 
    </SOAP-ENV:Body> 
</SOAP-ENV:Envelope> 

Pero SUDS' se ve así:

<?xml version="1.0" encoding="UTF-8"?> 
<SOAP-ENV:Envelope 
xmlns:ns0="http://api.service.books/" 
xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> 
    <SOAP-ENV:Header/> 
    <ns1:Body> 
     <ns0:doLogin> 
     <username>demo</username> 
     <password>demo</password> 
     <siteName>www.kashoo.com</siteName> 
     <locale>en_US</locale> 
     <duration>300000</duration> 
     </ns0:doLogin> 
    </ns1:Body> 
</SOAP-ENV:Envelope> 

Soy un SOAP real y SUDS novato pero escuché que SUDS es la mejor biblioteca SOAP para usar desde aquí: What SOAP client libraries exist for Python, and where is the documentation for them?

Así que mi pregunta es simplemente cuáles son las partes cruciales que son diferentes y que hacen que la solicitud falle y ¿cómo puedo configurar SUDS para enviar la solicitud formateada correctamente?

Respuesta

35

A primera vista parece que el problema que tiene es con SSL. Está accediendo a una URL https, y el controlador de transporte para suds.client habla por defecto HTTP.

El problema
Si nos fijamos en la parte inferior del WSDL se especifica la ubicación predeterminada como http://www.clarityaccounting.com/api/v1, que es una URL http, pero el WSDL es SSL.

<wsdl:service name="v1"> 
    <wsdl:port binding="tns:v1SoapBinding" name="BooksApiV1Port"> 
     <soap:address location="http://www.clarityaccounting.com/api/v1"/> 
    </wsdl:port> 
</wsdl:service> 

Si lo hace un HTTP GET en la URL, se obtiene el mensaje de error que recibió:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> 
    <soap:Body> 
     <soap:Fault> 
      <faultcode>soap:Server</faultcode> 
      <faultstring>No such operation: (HTTP GET PATH_INFO: /api/v1)</faultstring> 
     </soap:Fault> 
    </soap:Body> 
</soap:Envelope> 

La solución
Para solucionar este problema, es necesario sustituir la ubicación predeterminada cuando se llamar al constructor Client para que se pegue con https:

>>> url 
'https://www.clarityaccounting.com/api/v1?wsdl' 
>>> client = Client(url, location='https://www.clarityaccounting.com/api/v1') 
>>> token = client.service.doLogin('demo', 'demo', 'www.kashoo.com', 'en_US', 300000) 
>>> token 
(authToken){ 
    authenticationCode = "ObaicdMJZY6UM8xZ2wzGjicT0jQ=" 
    expiryDate = 2010-03-05 12:31:41.000698 
    locale = "en_US" 
    myUserId = 4163 
    site = "www.kashoo.com" 
} 

¡Victoria!

Punta Pro para futuras depuraciones: active la depuración completa del registro. SUDS usa la biblioteca estándar logging, por lo que le da mucho control. Así que manivela todo hasta DEBUG:

import logging 
logging.basicConfig(level=logging.INFO) 
logging.getLogger('suds.client').setLevel(logging.DEBUG) 
logging.getLogger('suds.transport').setLevel(logging.DEBUG) 
logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG) 
logging.getLogger('suds.wsdl').setLevel(logging.DEBUG) 

Esto es lo que me ayudó a reducirla, ya que estaba diciendo claramente que iba a enviar a través de http:

DEBUG:suds.transport.http:sending: 
URL:http://www.clarityaccounting.com/api/v1 
(xml output omitted) 

Y entonces la respuesta, dijo el fin bien:

DEBUG:suds.client:http failed: 
+1

¡Me alegro de ayudar! Fue un problema divertido descubrir el viernes por la tarde. – jathanism

+3

+1 Buen toque para incluir la ayuda de depuración – Draemon

+0

¡Gracias! Solo estaba experimentando el mismo problema con un servicio web diferente, pero el mismo problema de https/http. No pude encontrar cómo configurar el cliente para que use https aunque el WSDL dijo que usara http. –

1

No debería ser un problema relacionado con la conexión a un servicio a través de HTTPS. Estoy usando espuma para hacer lo mismo. He intentado algunos enfoques para su archivo WSDL (no soy un experto) y encontré el mismo error. Sin embargo, lo que debe hacer para practicar con espuma es usar el método de fábrica, p.

login = client.factory.create('doLogin') 
login.username = 'username' 
etc... 

Donde todo lo enviado a la función de creación es uno de los tipos definidos en el archivo WSDL. Si crea ese tipo en el shell, puede ejecutar 'print login' para ver sus propiedades adicionales.

Espero que esto al menos te indique dónde está el problema (con HTTPS). Además, noté que los encabezados de soapAction no están establecidos en el archivo WSDL, no estoy seguro de cómo suds o el servicio maneja las solicitudes sin eso.

+0

Como puede ver en mi respuesta revisada, el problema no fue conectarse a través de https, sino una inconsistencia en el WSDL que se sirve sobre https, mientras tanto apuntando las llamadas SOAP a http. Supongo que la forma en que tienen configurado el servicio para redirigir las URL entrantes de http a https es la fuente del problema. Todo lo que realmente hemos hecho se solucionó. – jathanism

+0

Depurando para el zee win! – bennylope

1

Usando suds-jurko https://pypi.python.org/pypi/suds-jurko que es una horquilla mantenida de espuma. Puede pasar una opción __inject donde puede darle el xml sin procesar que desea enviar.

from suds.client import Client 

username, password, sitename, locale, duration = 'demo', 'demo', 'www.kashoo.com', 'en_US', 300000 

raw_xml = """<SOAP-ENV:Envelope 
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:api="http://api.service.books/"> 
    <SOAP-ENV:Body> 
    <api:doLogin> 
     <username>{0}</username> 
     <password>{1}</password> 
     <siteName>{2}</siteName> 
     <locale>{3}</locale> 
     <duration>{4}</duration> 
    </api:doLogin> 
    </SOAP-ENV:Body> 
</SOAP-ENV:Envelope>""".format(username, password, sitename, locale, duration) 

client = Client(url, location) 
result = client.service.doLogin(__inject={'msg':raw_xml}) 

Siento que debo documentar todas las formas en que uno puede inspeccionar el jabón en bruto que genera espuma aquí.

  1. Uso de una bandera de nariz al construir el cliente. Tenga en cuenta que con la bandera configurada en True, la espuma generará el jabón pero no lo enviará.

    client =Client(url, nosend=True)
    res = client.service.example()
    print res.envelope #prints el jabón crudo

  2. Utilización del registro cronológico. Aquí solo estamos registrando suds.transport.http, por lo que solo se generará lo que se envíe/reciba.

    import logging
    import sys
    handler = logging.StreamHandler(sys.stderr)
    logger = logging.getLogger('suds.transport.http')
    logger.setLevel(logging.DEBUG), handler.setLevel(logging.DEBUG)
    logger.addHandler(handler)

  3. Uso de la MessagePlugin

    from suds.plugin import MessagePlugin
    class MyPlugin(MessagePlugin):
    def marshalled(self, context):
    #import pdb; pdb.set_trace()
    print context.envelope.str()

    client = Client(url, plugins=[MyPlugin()])

No sólo el MessagePlugin le dan la capacidad para inspeccionar el jabón genera pero también se puede modificar antes de enviarlo, ver->https://jortel.fedorapeople.org/suds/doc/suds.plugin.MessagePlugin-class.html

Cuestiones relacionadas