Building RESTful APIs with Tornado
2016-03-12 15:03
597 查看
from: http://www.drdobbs.com/open-source/building-restful-apis-with-tornado/240160382
The Tornado Web framework makes it easy to write RESTful APIs in Python. How easy? Have a look
Tornado is a Python Web framework and asynchronous networking library that provides excellent scalability due to its non-blocking network
I/O. It also greatly facilitates building a RESTful API quickly. These features are central to Tornado, as it is the open-source version of FriendFeed's Web server. A few weeks ago, Tornado 3. was released, and it introduced many improvements. In this article,
I show how to build a RESTful API with the latest Tornado Web framework and I illustrate how to take advantage of its asynchronous features.
running
To build a RESTful API with Tornado, it is necessary to map URL patterns to your subclasses of
For example, if you want to handle an HTTP
Then, you map the URL pattern in
Listing One shows a very simple RESTful API that declares two subclasses of
Listing One: A simple RESTful API in Tornado.
?
The code is easy to understand. It creates an instance of
make up the Web application. The code passes a list of tuples to the
(
Then, the call to
When the Web application receives a request, Tornado iterates over that list and creates an instance of the first
matches the request path, and then calls the
with the corresponding parameters for the new instance based on the HTTP request. For example, Table 1 shows some HTTP requests that match the regular expressions defined in the previous code.
Table 1: Matching HTTP requests.
The simplest case is the
The method creates a
writes the received chunk to the output buffer. Because the chunk (
the response to
If you want to send the data with a different
the response header name and the desired value for it. You have to call
of the default
Listing Two: Changing the content type.
?
The following lines show the example response for
The
that includes the integer value received for the
include any validation for the
perform validations in Python. The following lines show the example response for
If you need to access additional request parameters such as the headers and body data, you can access them through
that provides all the information about the HTTP request. The
If you've ever worked with callbacks, you know how difficult it is to read and understand code split into different methods. Tornado provides a generator-based interface (
code in handlers in a single generator.
You simply need to use the
Listing Three shows a new subclass of
to add two imports to add the code to the previous listing:
Listing Three: A different subclass of tornado.web.RequestHandler.
?
Because I've added a new subclass of
to the
Listing Four: Mapping a URL to a handler.
?
The
Then, the code calls the
is an
to retrieve the
The call to
its execution with a successful response code equal to
page in
with the
Premium Content" with "Most Recent Content." Then, the code calls the
Listing Five is the equivalent code, which uses the
that works as the callback for the
Listing Five: The equivalent functionality using a decorator.
?
When the
it is your responsibility to call
As you can see, it is much easier to use the
The
variable in your
?
The default code for the
and
Listing Six: The get() method.
?
Whenever the Web application receives a request and matches the URL pattern, Tornado performs the following actions:
It creates a new instance of the
It calls the
to save the arguments into member variables.
No matter what the HTTP request, Tornado calls the
Tornado won't call any additional methods. You can override the
It calls the method according to the HTTP request with the arguments based on the URL regular expression that captured the different groups. As you already know, you must override the methods
you want your
the different arguments.
If the handler is synchronous, Tornado calls
asynchronous, Tornado executes
the
If the client closes the connection in asynchronous handlers, Tornado calls
must be included in the
Listing Seven shows a new version of the
just saves
Listing Seven: Overriding the initialize() method.
?
The following code shows the changes in the arguments passed to the
handler:
?
In this case, I've used a simple string as the value passed from the application. However, the most common usage will be to pass one or more common objects (for example, a database object).
Listing Eight shows the code for the
Listing Eight: A simple error request handler.
?
It is necessary to add the appropriate mapping to the
?
If
as the second parameter. If
HTTP error code to the browser. Any other
code.
The three different mechanisms to return errors return the default error page, which you can change by overriding the
In this article, I've provided many examples to show how to easy it is to start developing RESTful APIs with Tornado. I've covered basic features, but you will definitely want to dive deeper into Tornado's additional useful features when building a complete
RESTful API. Tornado's source code comes with good documentation for the different methods and variables, so you can easily build the methods you need to use as you write code.
The Tornado Web framework makes it easy to write RESTful APIs in Python. How easy? Have a look
Tornado is a Python Web framework and asynchronous networking library that provides excellent scalability due to its non-blocking network
I/O. It also greatly facilitates building a RESTful API quickly. These features are central to Tornado, as it is the open-source version of FriendFeed's Web server. A few weeks ago, Tornado 3. was released, and it introduced many improvements. In this article,
I show how to build a RESTful API with the latest Tornado Web framework and I illustrate how to take advantage of its asynchronous features.
Mapping URL Patterns to Request Handlers
To get going, download the latest stable version and perform a manual installation or execute an automatic installation withpipby
running
pip install tornado.
To build a RESTful API with Tornado, it is necessary to map URL patterns to your subclasses of
tornado.web.RequestHandler, which override the methods to handle HTTP requests to the URL.
For example, if you want to handle an HTTP
GETrequest with a synchronous operation, you must create a new subclass of
tornado.web.RequestHandlerand define the
get()method.
Then, you map the URL pattern in
tornado.web.Application.
Listing One shows a very simple RESTful API that declares two subclasses of
tornado.web.RequestHandlerthat define the
getmethod:
VersionHandlerand
GetGameByIdHandler.
Listing One: A simple RESTful API in Tornado.
?
tornado.web.Applicationnamed
applicationwith the collection of request handlers that
make up the Web application. The code passes a list of tuples to the
Applicationconstructor. The list is composed of a regular expression (
regexp) and a
tornado.web.RequestHandlersubclass
(
request_class). The
application.listenmethod builds an HTTP server for the application with the defined rules on the specified port. In this case, the code uses the default
8888port.
Then, the call to
tornado.ioloop.IOLoop.instance().start()starts the server created with
application.listen.
When the Web application receives a request, Tornado iterates over that list and creates an instance of the first
tornado.web.RequestHandlersubclass whose associated regular expression
matches the request path, and then calls the
head(),
get(),
post(),
delete(),
patch(),
put()or
options()method
with the corresponding parameters for the new instance based on the HTTP request. For example, Table 1 shows some HTTP requests that match the regular expressions defined in the previous code.
HTTP verb and request URL | Tuple (regexp, request_class) that matches the request path | RequestHandler subclass and method that is called |
GET http://localhost:8888/getgamebyid/500 | (r"/getgamebyid/([0-9]+)", GetGameByIdHandler) | GetGameByIdHandler.get |
GET http://localhost:8888/version | (r"/version", VersionHandler) | VersionHandler.get |
The simplest case is the
VersionHandler.getmethod, which just receives
selfas a parameter because the URL pattern doesn't include any parameter.
The method creates a
responsedictionary, then calls the
self.writemethod with
responseas a parameter. The
self.writemethod
writes the received chunk to the output buffer. Because the chunk (
response) is a dictionary,
self.writewrites it as JSON and sets the
Content-Typeof
the response to
application/json. The following lines show the example response for
GET http://localhost:8888/version[/code] and the response headers:
?
Content-Type, you can call the
self.set_headerwith
"Content-Type"as
the response header name and the desired value for it. You have to call
self.set_headerafter calling
self.write, as shown in Listing Two. It sets the
Content-Typeto
text/plaininstead
of the default
application/jsonin a new version of the
VersionHandlerclass. Tornado encodes all header values as UTF-8.
Listing Two: Changing the content type.
?
GET http://localhost:8888/version[/code] and the response headers with the new version of theVersionHandlerclass:
?
GetGameByIdHandler.getmethod receives two parameters:
selfand
id. The method creates a
responsedictionary
that includes the integer value received for the
idparameter, then calls the
self.writemethod with
responseas a parameter. The sample doesn't
include any validation for the
idparameter in order to keep the code as simple as possible, as I'm focused on the way in which the
getmethod works. I assume you already know how to
perform validations in Python. The following lines show the example response for
GET http://localhost:8888/getgamebyid/500[/code] and the response headers:
?
self.request. This variable is a
tornado.httpserver.HTTPRequestinstance
that provides all the information about the HTTP request. The
HTTPRequestclass is defined in
httpserver.py.
Working with Asynchronous Code
If you've ever worked with callbacks, you know how difficult it is to read and understand code split into different methods. Tornado provides a generator-based interface (tornado.gen) to enable you to write asynchronous
code in handlers in a single generator.
You simply need to use the
@tornado.gen.coroutinedecorator for asynchronous generators in the
requiredmethod and you don't need to add the
@tornado.web.asynchronousdecorator.
Listing Three shows a new subclass of
tornado.web.RequestHandlerand defines the
get()method with the
@tornado.gen.coroutinedecorator. You need
to add two imports to add the code to the previous listing:
import tornado.genand
import tornado.httpclient.
Listing Three: A different subclass of tornado.web.RequestHandler.
?
RequestHandler, it is necessary to map the URL pattern in
tornado.Web.Application. Listing Four shows the new code that maps the
/getfullpageURL
to the
GetFullPageAsyncHandler.
Listing Four: Mapping a URL to a handler.
?
GetFullPageAsyncHandler.getmethod creates a
tornado.httpclient.AsyncHTTPClientinstance (
http_client) that represents a non-blocking HTTP client.
Then, the code calls the
http_client.fetchmethod of this instance to asynchronously execute a request. The
fetchmethod returns a
Future, whose result
is an
HTTPResponse, and raises an
HTTPErrorif the request returns a response code other than
200. The code uses the
yieldkeyword
to retrieve the
HTTPResponsefrom the
Futurereturned by the
fetchmethod.
The call to
fetchretrieves the Dr. Dobb's Web Development page from
http://www.drdobbs.com/web-developmentwith an asynchronous execution. When
fetchfinishes
its execution with a successful response code equal to
200,
http_responsewill be an
HTTPRequestinstance with the contents of the retrieved HTML
page in
http_response.body. The method continues its execution with the line after the call to
fetch. You have all the code that needs to be executed in the
getmethod
with the
@tornado.gen.coroutinedecorator, and you don't have to worry about writing a callback for
on_fetch. The next line decodes the response body to a string and replaces "Most Recent
Premium Content" with "Most Recent Content." Then, the code calls the
self.writemethod to write the modified string and sets the
Content-Typeof the response to
application/html.
Listing Five is the equivalent code, which uses the
@tornado.web.asynchronousdecorator instead of
@tornado.gen.coroutine. In this case, it is necessary to define the
on_fetchmethod
that works as the callback for the
http_client.fetchmethod; therefore, the code is split into two methods.
Listing Five: The equivalent functionality using a decorator.
?
fetchmethod finishes retrieving the content, it executes the code in
on_fetch. Because the
getmethod uses
@tornado.web.asynchronous,
it is your responsibility to call
self.finish()to finish the HTTP request. Thus, the
on_fetchmethod calls
self_finishin its last line, after calling
self.writeand
self.set_header.
As you can see, it is much easier to use the
@tornado.gen.coroutinedecorator.
Understanding How Tornado Works with a RequestHandler Subclass
The RequestHandlerclass defines a
SUPPORTED_METHODSclass variable with the following code. If you need support for different methods, you need to override the
SUPPORTED_METHODSclass
variable in your
RequestHandlersubclass:
?
head(),
get(),
post(),
delete(),
patch(),
put(),
and
options()methods is a single line that raises an
HTTPError. Listing Six shows the code for the
getmethod:
Listing Six: The get() method.
?
It creates a new instance of the
RequestHandlersubclass that has been mapped to the URL pattern.
It calls the
initializemethod with the keyword arguments specified in the application configuration. You can override the
initializemethod
to save the arguments into member variables.
No matter what the HTTP request, Tornado calls the
preparemethod. If you call either
finishor
send_error,
Tornado won't call any additional methods. You can override the
preparemethod to execute code that is necessary for any HTTP request, then write your specific code in the
head(),
get(),
post(),
delete(),
patch(),
put()or
options()method.
It calls the method according to the HTTP request with the arguments based on the URL regular expression that captured the different groups. As you already know, you must override the methods
you want your
RequestHandlersubclass to be able to process. For example, if there is an HTTP
GETrequest, Tornado will call the
getmethod with
the different arguments.
If the handler is synchronous, Tornado calls
on_finishafter the previous method called, according to the HTTP request returns. But if the handler is
asynchronous, Tornado executes
on_finishafter the code calls
finish. The previous asynchronous example showed the usage of
finish. You can override
the
on_finishmethod to perform cleanup or logging. Notice that Tornado calls
on_finishafter it sends the response to the client.
If the client closes the connection in asynchronous handlers, Tornado calls
on_connection_close. You can override this method to clean up resources in this specific scenario. However, the cleanup after processing the request
must be included in the
on_finishmethod.
Listing Seven shows a new version of the
GetGameByIdHandlerclass that overrides the
initializemethod to receive a string specified in the application configuration. The
initializemethod
just saves
common_stringinto a member variable, then the
getmethod uses the string in the response:
Listing Seven: Overriding the initialize() method.
?
tornado.web.Applicationconstructor to pass a value for
common_stringin a dictionary for the
GetGameByIdHandlerrequest
handler:
?
Returning Errors in a Request Handler
Listing Eight shows the code for the ErrorHandlerrequest handler that demonstrates the simplest use of the three different mechanisms to return errors in a handler method.
Listing Eight: A simple error request handler.
?
Applicationconstructor parameters with the following line:
?
error_codeis equal to 1, the
getmethod calls
self.set_status, which sets the status code for the response. It is also possible to specify a
reasonstring
as the second parameter. If
error_codeis equal to
2, the
getmethod calls
self.send_error, which sends the specified
HTTP error code to the browser. Any other
error_codevalue will make the
getmethod raise a
tornado.web.HTTPErrorexception with the
500status
code.
The three different mechanisms to return errors return the default error page, which you can change by overriding the
write_errormethod in your
RequestHandlersubclasses.
Conclusion
In this article, I've provided many examples to show how to easy it is to start developing RESTful APIs with Tornado. I've covered basic features, but you will definitely want to dive deeper into Tornado's additional useful features when building a completeRESTful API. Tornado's source code comes with good documentation for the different methods and variables, so you can easily build the methods you need to use as you write code.
相关文章推荐
- HDU 3836 - Equivalent Sets【强连通分量 基础题】
- UItabBarController
- QBImagePicker用法详解
- quick 3.3 display.newColorLayer setContentSize
- Debian安装Arduino IDE
- CodeForces - 622A Infinite Sequence (思想)水
- Codeforces--622A--Infinite Sequence(数学)
- Codeforces--622A--Infinite Sequence(数学)
- 《NumPy Beginner's Guide》笔记Chapter2
- UI控件--UIImageView
- UVa 540 - Team Queue
- 《iOS Human Interface Guidelines》——Image View
- 对于League of Legends的分析
- Hibernate之Query接口的uniqueResult()方法
- UIviewcontroller与UIview
- [手游新项目历程]第3天-fatal error LNK1000: Internal error during IncrBuildImage
- iOS上传失败提示"Request failed: unacceptable content-type: text/html"
- String,StringBuffer与StringBuilder之间的区别
- UIscrollView和UIPageControl的循环滚动
- MYSQL的索引类型:PRIMARY, INDEX,UNIQUE,FULLTEXT,SPAIAL 有什么区别?各适用于什么场合?