root/trunk/README.txt

Revision 23, 9.6 kB (checked in by kevin, 2 years ago)

documentation update for 1.1 release

Line 
1 TGWebServices
2 =============
3
4 :Author: Kevin Dangoor <dangoor+tgwebservices@gmail.com>
5 :Copyright: Copyright 2006, 2007 Kevin Dangoor and Arbor Networks
6 :License: MIT
7 :Homepage: http://tgwebservices.python-hosting.com/
8
9 .. contents:: Table of Contents
10
11 Introduction
12 ------------
13
14 TurboGears gives you a plain HTTP with JSON return values API for your
15 application for free. This isn't always what you want, though. Sometimes,
16 you don't want to expose all of the data to the web that you need to
17 render your templates. Maybe you need to support a protocol that names the
18 function it's calling as part of what it POSTs such as SOAP or XML-RPC.
19
20 TGWebServices provides a super simple API for creating web services that
21 are available via SOAP, HTTP->XML, and HTTP->JSON. The SOAP API generates
22 WSDL automatically for your Python and even generates enough type
23 information for statically typed languages (Java and C#, for example) to
24 generate good client code on their end.
25
26 How easy is it?
27
28 ::
29
30     class Multiplier(WebServicesRoot):
31
32         @wsexpose(int)
33         @wsvalidate(int, int)
34         def multiply(self, num1, num2):
35             return num1 * num2
36
37 With this at the root, SOAP clients can find the WSDL file at
38 /soap/api.wsdl and POST SOAP requests to /soap/. HTTP requests to
39 /multiply?num1=5&num2=20 will return an XML document with the result of
40 100. Add ?tg_format=json (or an HTTP Accept: text/javascript header) and
41 you'll get JSON back.
42
43 The great thing about this is that the code above looks like a '''normal
44 Python function''' and doesn't know a thing about web services.
45
46 Prerequisites
47 -------------
48
49 This document assumes a working knowledge of TurboGears.
50
51 Features
52 --------
53
54 * Easiest way to expose a web services API
55 * Supports SOAP, HTTP+XML, HTTP+JSON
56 * Outputs wrapped document/literal SOAP, which is the most widely
57   compatible format
58 * Provides enough type information for statically typed languages to
59   generate conveniently usable interfaces
60 * Can output instances of your own classes
61 * Can input instances of your own classes (via SOAP, XML or JSON)
62 * Works with TurboGears 1.0
63 * MIT license allows for unrestricted use
64
65 Getting Started
66 ---------------
67
68 Here is a sample from tgwebservices/tests/fixtures.py::
69
70     from tgwebservices.controllers import WebServicesRoot, WebServicesController, \
71                                           wsexpose, wsvalidate
72
73     from tgwebservices.runtime import typedproperty, unsigned
74
75     class MyService(WebServicesRoot):
76         @wsexpose(int)
77         @wsvalidate(int)
78         def times2(self, value):
79             "Multiplies value by two."
80             return value * 2
81    
82         @wsexpose(int)
83         @wsvalidate(int)
84         def twentyover(self, value):
85             "Divides 20 by value"
86             return 20 / value
87
88 There are several things of interest in the example above.
89
90 1. Those are all of the imports that you'll likely need
91 2. The top level controller must subclass WebServicesRoot. This is
92    important, because this provides the /soap URL that is required for
93    SOAP access.
94 3. wsexpose is called with the type that is returned from the method.
95 4. wsvalidate is called with the types of the parameters. You can specify
96    the types positionally (*skipping self*) or as keyword arguments to
97    wsvalidate.
98 5. Unlike when you work with TurboGears proper, you do not need to return
99    a dictionary. Just return the value directly.
100
101 To instantiate and use your WebServicesRoot (MyService in the example above), you can do something like this:
102
103 cherrypy.root = MyService("http://foo.bar.baz/")
104
105 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".
106
107 URLs
108 ----
109
110 class InnerService(WebServicesRoot):
111     @wsexpose(int)
112     def times4(self, num):
113     return num * 4
114
115 class ServiceRoot(WebServicesRoot):
116     inner = InnerService()
117
118     @wsexpose(int)
119     def times2(self, num):
120     return num * 2
121
122 Assume that ServiceRoot is instantiated with a *baseURL* of "http://foo.bar.baz/". Here are URLs that are available:
123
124 +----------------------------------+-------------------------------------------+
125 | URL                              | What is it                                |
126 +==================================+===========================================+
127 | http://foo.bar.baz/              | nothing there... you could use standard   |
128 |                                  | TurboGears expose to put a page there.    |
129 +----------------------------------+-------------------------------------------+
130 | http://foo.bar.baz/times2        | HTTP access to the times2 method          |
131 +----------------------------------+-------------------------------------------+
132 | http://foo.bar.baz/inner/times4  | HTTP access to the times4 method on       |
133 |                                  | InnerService                              +
134 +----------------------------------+-------------------------------------------+
135 | http://foo.bar.baz/soap/         | URL to POST SOAP requests to              |
136 +----------------------------------+-------------------------------------------+
137 | http://foo.bar.baz/soap/api.wsdl | URL to get the WSDL file from             |
138 +----------------------------------+-------------------------------------------+
139
140 SOAP method names are created by taking the URL parts and concatenating and
141 camelCasing them. In the example above, there will be a "times2" SOAP method,
142 as you'd expect. The "inner/times4" method will become "innerTimes4" in SOAP.
143 All of the SOAP methods live in a flat namespace and appear in a single WSDL
144 file that covers your whole web service.
145
146 Lists
147 -----
148
149 When you wish to return an array of items, you can specify this by
150 creating a list that contains one item: the type of the objects in the
151 list::
152
153     @wsexpose([str])
154     def somestrings(self):
155         return ["A", "B", "C"]
156
157 Since many web services are consumed by statically typed languages like
158 Java, lists that are returned as SOAP arrays can only contain *one* type
159 of object.
160
161 Custom Objects
162 --------------
163
164 You can return instances of classes that you create. Whenever you see the
165 term "complex type", you can think "class". "Complex type" comes from XML
166 Schema terminology that is used in declaring the properties of the
167 returned type.
168
169 Returning complex types is as easy as returning primitive types. However,
170 you do need to take an extra step in declaring complex types that you wish
171 to return. Here is an example::
172
173     class FancyValue(object):
174         name = ""
175         age = int
176    
177         def computed(self):
178             return "Hello!"
179         computed = typedproperty(str, computed)
180    
181         def __init__(self, name=None, age=None):
182             self.name = name
183             self.age = age
184
185     class ComplexService(WebServicesRoot):
186         @wsexpose(FancyValue)
187         def getfancy(self):
188             "Returns a fancy value"
189             fv = FancyValue()
190             fv.name = "Mr. Test"
191             fv.age = 33
192             return fv
193
194 In this example, we've created a class called FancyValue that we want to
195 return from a web service method. TGWebServices will only return
196 properties of instances that:
197
198 * are declared at the class level (in the example above, name is defined
199   as a string and age is defined as an int)
200 * do not start with _
201 * are not methods
202
203 With these rules, it's easy to store whatever housekeeping data you need
204 on your objects without exposing that data to the web service.
205
206 Once you've defined your class, you can specify it as a return value in
207 wsexpose just as you would a builtin Python type.
208
209 See the next section for information about *typedproperty* which appears
210 in the example above.
211
212 Custom Objects as Input
213 -----------------------
214
215 You can also use your own classes as input to methods::
216
217     class Person(object):
218         name = str
219         age = int
220
221     class ComplexInput(WebServicesRoot):
222         @wsexpose()
223         @wsvalidate(Person)
224         def savePerson(self, p):
225             self.person = p
226
227 Using SOAP, it's fairly natural to submit objects as input to methods. For
228 plain HTTP, it's not as obvious, but it is still quite easy. You can submit
229 XML (which looks just like the XML output for the same object) or JSON.
230 For XML, you just POST a document with the content-type of text/xml. For
231 JSON, you POST a document with the content-type of application/json.
232
233 Here is sample JSON to post to /savePerson::
234
235     {"p" : {"name" : "Julius", "age" : 2107}}
236
237 The XML is similar::
238
239     <parameters>
240         <p>
241             <name>Julius</name>
242             <age>2107</age>
243         </p>
244     </parameters>
245
246 Extra Types You Can Return
247 --------------------------
248
249 The tgwebservices.runtime defines two additional types that you can
250 return: unsigned and typedproperty. Python doesn't have an unsigned int
251 type, but you can use the tgwebservices.runtime.unsigned class to tell the
252 consumer of your service that you are returning an unsigned integer value.
253
254 typedproperty wraps the standard Python property function allowing you to
255 specify what return type will be coming from your property's getter
256 method. In the example in the previous section, the "computed" property
257 will be a string.
258
259 Getting Help
260 ------------
261
262 Questions about TGWebServices should go to the TurboGears mailing list.
263
264 http://groups.google.com/group/turbogears
Note: See TracBrowser for help on using the browser.