Setting up a web service with Django and SOAP

13 September 2009 | 0 comments | Tagged as: Django SOAP

With the increase of business to business (B2B) communication, the need for machine to machine software interaction over a network has become vital. Such a system is know as a Web Service and can take a variety of forms, specifications and protocols. One commonly used protocol for web services is called SOAP.

SOAP uses the Internet application layer transport protocols and commonly HTTP to transfer XML Request and Response messages to and from a Web Service provider. In additional the Provider uses the WSDL (Web Services Description Language) to publish an XML format document with a list of service and operations available.

Creating Web Services in Django is simply using Optio Software's http://trac.optio.webfactional.com Soaplib python library which even has automatic wsdl generation.

I will be installing this on Debian, so first ensure you have all the pre-requisites for xml processing installed:

 ~$ sudo apt-get install libxml2 libxml2-dev libxslt1.1 libxslt1-dev

Next install the python binding for these libraries. This can be installed through easy install.

 ~$ sudo easy_install lxml

Now we install Soaplib. The latest version of Soaplib is broken so we need to install an older one. The easiest way to do this is to check it out from an old repository.

 ~$ svn co https://svn.optio.webfactional.com/soaplib/trunk/ soaplib

change into the soaplib directory and then install it with:

 ~$ sudo python setup.py install

Now we have everything installed we can begin writing our service. The following information is based on http://www.djangosnippets.org/snippets/979/ and http://www.djangosnippets.org/snippets/1311/ .

Create a file in the root of the project called soaplib_handler.py and paste in the following:

 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

Create a views.py file in the root of your project and paste in the following:

 from soaplib_handler import DjangoSoapApp, soapmethod, soap_types
 from django.conf import settings
 import inspect
 import logging
 class Service(DjangoSoapApp):
   for app in settings.INSTALLED_APPS:
     try:
       module = __import__(app + '.web_service', [], [], [''])
       for key in module.__dict__:
         func = module.__dict__[key]
           if inspect.isfunction(func):
             if '_is_soap_method' in dir(func):
               locals()[key] = func
      except ImportError, e:
        pass
 service = Service()

This methods will iterate over your installed apps and look for a web_service.py file. Any methods decorated with @soapmethod will be automatically imported into the local namespace. You can therefore separate out your web service methods into there own specific apps. Next edit your urls.py file to include this:

 from django.conf.urls.defaults import *
 urlpatterns = patterns('',
   (r'^', 'my_webservice.views.service'),
   (r'^service.wsdl', 'my_webservice.views.service'),
 )

Finally in you app create a web_service.py file. Inside this file create your web service methods. Each method should be decorated with @soapmethod which contains the method parameters and the return type. An example is as follows:

 from soaplib_handler import DjangoSoapApp, soapmethod, soap_types
 @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

Now you can start your app and it everything goes well you should be able to access your wsdl from your browser at http://127.0.0.1:8000/service.wsdl (substituting your own ip address and port number)

<definitions name="Service" targetnamespace="my_webservice.views.Service">
    <types>
        <schema targetnamespace="webservice-test.views.Service">
            <xs:element name="say_hello" type="tns:say_hello"></xs:element>
                <xs:complextype name="say_hello">
                    <xs:sequence>
                        <xs:element name="name" type="xs:string"></xs:element>
                        <xs:element name="times" type="xs:int"></xs:element>
                    </xs:sequence>
                </xs:complextype>
            <xs:element name="stringArray" type="tns:stringArray"></xs:element>
            <xs:complextype name="say_helloResponse">
                <xs:sequence>
                    <xs:element name="retval" type="tns:stringArray"></xs:element>
                </xs:sequence>
            </xs:complextype>
            <xs:complextype name="stringArray">
                <xs:sequence>
                    <xs:element maxoccurs="unbounded" minoccurs="0" name="string" type="xs:string"></xs:element>
                </xs:sequence>
            </xs:complextype>
            <xs:element name="say_helloResponse" type="tns:say_helloResponse"></xs:element>
        </schema>
    </types>
    <message name="say_hello">
        <part element="tns:say_hello" name="say_hello"></part>
    </message>
    <message name="say_helloResponse">
        <part element="tns:say_helloResponse" name="say_helloResponse"></part>
    </message>
    <porttype name="Service">
        <operation name="say_hello" parameterorder="say_hello">
            <documentation></documentation>
            <input message="tns:say_hello" name="say_hello" />
            <output message="tns:say_helloResponse" name="say_helloResponse"></output>
        </operation>
    </porttype>
    <plnk:partnerlinktype name="Service">
        <plnk:role name="Service">
            <plnk:porttype name="tns:Service"></plnk:porttype>
        </plnk:role>
    </plnk:partnerlinktype>
    <binding name="Service" type="tns:Service">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding>
        <operation name="say_hello">
            <soap:operation soapaction="say_hello" style="document"></soap:operation>
            <input name="say_hello" />
                <soap:body use="literal"></soap:body>
            
            <output name="say_helloResponse">
                <soap:body use="literal"></soap:body>
            </output>
        </operation>
    </binding>
    <service name="Service">
        <port binding="tns:Service" name="Service">
            <soap:address location="http://127.0.0.1:8000/service"></soap:address>
        </port>
    </service>
</definitions>

POST A COMMENT

Markdown available. Required *.

*

*

*

*