Login

SOAP views with on-demand WSDL generation

Author:
chewie
Posted:
August 14, 2008
Language:
Python
Version:
.96
Tags:
soap soaplib wsdl
Score:
4 (after 4 ratings)

Optio's soaplib makes it really straightforward to write SOAP web service views by using a decorator to specify types. Plus it's the only Python library, as of today, which is able to generate WSDL documents for your web service.

You can test it with a soaplib client:

>>> from soaplib.client import make_service_client
>>> from foo.views import HelloWorldService
>>> client = make_service_client('http://localhost:8000/hello_world/', HelloWorldService())
>>> client.say_hello('John', 2)
['Hello, John', 'Hello, John']

And get an WSDL document:

>>> client.server.wsdl('')
'<?xml version=\'1.0\' encoding=\'utf-8\' ?><definitions name="HelloWorldService"

...

</definitions>'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# soaplib_handler.py

from soaplib.wsgi_soap import SimpleWSGISoapApp
from soaplib.service import soapmethod
from soaplib.serializers import primitive as soap_types

from django.http import HttpResponse


class DjangoSoapApp(SimpleWSGISoapApp):

    def __call__(self, request):
        django_response = HttpResponse()
        def start_response(status, headers):
            status, reason = status.split(' ', 1)
            django_response.status_code = int(status)
            for header, value in headers:
                django_response[header] = value
        response = super(SimpleWSGISoapApp, self).__call__(request.META, start_response)
        django_response.content = "\n".join(response)

        return django_response


# views.py - sample view

from soaplib_handler import DjangoSoapApp, soapmethod, soap_types


class HelloWorldService(DjangoSoapApp):

    __tns__ = 'http://my.namespace.org/soap/'

    @soapmethod(soap_types.String, soap_types.Integer, _returns=soap_types.Array(soap_types.String))
    def say_hello(self, name, times):
        results = []
        for i in range(0, times):
            results.append('Hello, %s'%name)
        return results

hello_world_service = HelloWorldService()


# urls.py

urlpatterns = patterns(
    '',
    (r'^hello_world/', 'foo.views.hello_world_service'),
    (r'^hello_world/service.wsdl', 'foo.views.hello_world_service'),
)

More like this

Comments

adamlofts (on August 15, 2008):

This is a great example. Thanks!

#

csar (on August 15, 2008):

This doesn't work for me because of problems with wsgi.input - have you actually successfully run this? The server just hangs for me trying to read the request from the client. I really wish it would work cause it's such a simple way of structuring this and soaplib looked pretty nice.

#

adamlofts (on August 20, 2008):

I've just started to use this (it works). I have 2 comments:

  1. When you use super() you usually pass the class you are in (not the base class).

  2. To automatically follow Django model relationships I have written a little SoapDjangoArray type; which will follow a reverse foreign key or m2m relationship:

`` class SoapDjangoArray(soap_types.Array):

def to_xml(self, values, name='retval'):
    return soap_types.Array.to_xml(self, values.all(), name)``

#

emilianoheyns (on February 12, 2009):

@csar: I'm having the same problem. Do you have a solution yet?

#

emilianoheyns (on February 12, 2009):

Got it: change soaplib_handler into:

soaplib_handler.py

from soaplib.wsgi_soap import SimpleWSGISoapApp from soaplib.service import soapmethod from soaplib.serializers import primitive as soap_types import StringIO

from django.http import HttpResponse

class DumbStringIO(StringIO.StringIO): def read(self, n): return self.getvalue()

class DjangoSoapApp(SimpleWSGISoapApp):

def __call__(self, request):

    django_response = HttpResponse()
    def start_response(status, headers):
        status, reason = status.split(' ', 1)
        django_response.status_code = int(status)
        for header, value in headers:
            django_response[header] = value

    environ = request.META.copy()
    body = ''.join(['%s=%s' % v for v in request.POST.items()])
    environ['CONTENT_LENGTH'] = len(body)
    environ['wsgi.input'] = DumbStringIO(body)
    environ['wsgi.multithread'] = False

    response = super(DjangoSoapApp, self).__call__(environ, start_response)

    django_response.content = "\n".join(response)

    return django_response

#

zdmytriv (on June 18, 2009):

It also hangs for me also,

emilianoheyns's patch works fine.

Thanks

#

lid (on January 7, 2010):

The snippet works very well, the only problem I have is that the reply has no namespace defined :S

#

inuwashi (on July 9, 2010):

Just FYI the WSDL issue in soaplib v0.8.1 is fixed in the trunk so emilianoheyns version of soaplib_handler works like a charm.

#

jonasvp (on November 16, 2010):

emilianoheyns patch saved me... one hint: instead of

body = ''.join(['%s=%s' % v for v in request.POST.items()])

you can simply use

body = request.raw_post_data

#

johngar (on January 7, 2011):

ealekseev I also got 403 forbidden.

I fixed it by turning off the CSRF middleware in the Django settings file.

#

Sharpek (on May 9, 2011):

Hi,

To avoid CSRF Error just add:

@csrf_exempt class HelloWorldService(DjangoSoapApp):

#

hbansal (on November 2, 2011):

soapui is throwing

<s0:Fault>say_helloFault</s0:faultcode> <s0:faultstring>'say_hello'</s0:faultstring> <s0:detail></s0:detail> </s0:Fault>

whenever I try to invoke say_hello service, however it works well with suds client.

#

miltonlab (on June 12, 2012):

@csrf_exempt really work at soaplib 0.8.1 it work too

#

JeffSilverman (on June 22, 2012):

I have tried to get this snippet to work to no avail. Coded as is. I get a 405 from the web server. What I have left out?

#

JeffSilverman (on June 22, 2012):

In the disection of the 405, the message indiates that the GET is not allowed, but POST is.

#

Please login first before commenting.