Usage

Getting Started

Here is a sample from tgwebservices/tests/fixtures.py:

from tgwebservices.controllers import WebServicesRoot, WebServicesController, \
                                      wsexpose, wsvalidate

from tgwebservices.runtime import typedproperty, unsigned

class MyService(WebServicesRoot):
    @wsexpose(int)
    @wsvalidate(int)
    def times2(self, value):
        "Multiplies value by two."
        return value * 2

    @wsexpose(int)
    @wsvalidate(int)
    def twentyover(self, value):
        "Divides 20 by value"
        return 20 / value

There are several things of interest in the example above.

  1. Those are all of the imports that you’ll likely need
  2. The top level controller must subclass WebServicesRoot. This is important, because this provides the /soap URL that is required for SOAP access.
  3. wsexpose is called with the type that is returned from the method.
  4. wsvalidate is called with the types of the parameters. You can specify the types positionally (skipping self) or as keyword arguments to wsvalidate.
  5. Unlike when you work with TurboGears proper, you do not need to return a dictionary. Just return the value directly.

To instantiate and use your WebServicesRoot (MyService in the example above), you can do something like this:

cherrypy.root = MyService("http://foo.bar.baz/")

The constructor for WebServicesRoot has a required parameter of baseURL. This parameter sets the URL path of the web service (which will show up as the web service location in the WSDL). There are two optional parameters that will be derived from the baseURL if you don’t provide them. Those parameters are tns and typenamespace. tns is the target namespace declared in the WSDL (the XML namespace for the SOAP operations). typenamepsace is the XML namespace for the types defined in the WSDL. tns defaults to baseURL + “soap/”. typenamespace defaults to tns + “types”.

URLs

class InnerService(WebServicesController):
    @wsexpose(int)
    def times4(self, num):
        return num * 4

class ServiceRoot(WebServicesRoot):
    inner = InnerService()

    @wsexpose(int)
    def times2(self, num):
        return num * 2

Assume that ServiceRoot is instantiated with a baseURL of “http://foo.bar.baz/“. Here are URLs that are available:

URL What is it
http://foo.bar.baz/ nothing there... you could use standard TurboGears expose to put a page there.
http://foo.bar.baz/times2 HTTP access to the times2 method
http://foo.bar.baz/inner/times4 HTTP access to the times4 method on InnerService
http://foo.bar.baz/soap/ URL to POST SOAP requests to
http://foo.bar.baz/soap/api.wsdl URL to get the WSDL file from

SOAP method names are created by taking the URL parts and concatenating and camelCasing them. In the example above, there will be a “times2” SOAP method, as you’d expect. The “inner/times4” method will become “innerTimes4” in SOAP. All of the SOAP methods live in a flat namespace and appear in a single WSDL file that covers your whole web service.

Supported standard types

  • int
  • str, unicode
  • float
  • bool
  • datetime.date
  • datetime.time
  • datetime.datetime
  • NoneType -> None can be used as a value for any type

Lists

When you wish to return an array of items, you can specify this by creating a list that contains one item: the type of the objects in the list:

@wsexpose([str])
def somestrings(self):
    return ["A", "B", "C"]

Since many web services are consumed by statically typed languages like Java, lists that are returned as SOAP arrays can only contain one type of object.

Custom Objects

You can return instances of classes that you create. Whenever you see the term “complex type”, you can think “class”. “Complex type” comes from XML Schema terminology that is used in declaring the properties of the returned type.

Returning complex types is as easy as returning primitive types. However, you do need to take an extra step in declaring complex types that you wish to return. Here is an example:

class FancyValue(object):
    name = ""
    age = int

    def computed(self):
        return "Hello!"
    computed = typedproperty(str, computed)

    def __init__(self, name=None, age=None):
        self.name = name
        self.age = age

class ComplexService(WebServicesRoot):
    @wsexpose(FancyValue)
    def getfancy(self):
        "Returns a fancy value"
        fv = FancyValue()
        fv.name = "Mr. Test"
        fv.age = 33
        return fv

In this example, we’ve created a class called FancyValue that we want to return from a web service method. TGWebServices will only return properties of instances that:

  • are declared at the class level (in the example above, name is defined as a string and age is defined as an int)
  • do not start with _
  • are not methods

With these rules, it’s easy to store whatever housekeeping data you need on your objects without exposing that data to the web service.

Once you’ve defined your class, you can specify it as a return value in wsexpose just as you would a builtin Python type.

See the next section for information about typedproperty which appears in the example above.

Custom Objects as Input

You can also use your own classes as input to methods:

class Person(object):
    name = str
    age = int

class ComplexInput(WebServicesRoot):
    @wsexpose()
    @wsvalidate(Person)
    def savePerson(self, p):
        self.person = p

Using SOAP, it’s fairly natural to submit objects as input to methods. For plain HTTP, it’s not as obvious, but it is still quite easy. You can submit XML (which looks just like the XML output for the same object) or JSON. Both GET and POST can be used :

POST
For XML, you just POST a document with the content-type of text/xml. For JSON, you POST a document with the content-type of application/json.
GET

New in version 1.2.3.

For XML, add a parameter _xml_request with a value as below. For JSON, add a parameter _json_request.

Here is sample JSON to send to /savePerson:

{"p" : {"name" : "Julius", "age" : 2107}}

The XML is similar:

<parameters>
    <p>
        <name>Julius</name>
        <age>2107</age>
    </p>
</parameters>

Complex input with keyword parameters

New in version 1.2.3.

Keywords params can be set to complex types in json of xml.

Here is sample JSON for savePerson, the param p is quoted (the original value is ‘{“name” : “Julius”, “age” : 2107}’:

/savePerson?tg_format=json&p=%7B%22name%22%20%3A%20%22Julius%22%2C%20%22age%22%20%3A%202107%7D

XML is similar (p=’<p><name>Julius</name><age>2107</age></p>’):

/savePerson?tg_format=xml&p=%3Cp%3E%3Cname%3EJulius%3C/name%3E%3Cage%3E2107%3C/age%3E%3C/p%3E

Extra Types You Can Return

The tgwebservices.runtime defines three additional types that you can return:

  • unsigned. Python doesn’t have an unsigned int type, but you can use the tgwebservices.runtime.unsigned class to tell the consumer of your service that you are returning an unsigned integer value.
  • binary. This allow to transfer raw data. It will be transparently base64 encoded/decoded.
  • typedproperty wraps the standard Python property function allowing you to specify what return type will be coming from your property’s getter method. In the example in the previous section, the “computed” property will be a string.

JSONP support

JSONP is supported.