As a web developer comming from Ruby on Rails, the "flexibility" of the handlers organization in Tornado is really painful. So I decide to organize the handlers in a Rails way.
By default, in Tornado, the actions of a handler are exactly the http verb:
DELETE. So when I think about how to write a handler, I have to think in the
For example, building a websites of books. The books list page and book detail page both use the same http verb
GET, but they have totally different logic in the backend. If we organize the handler in http verb, then the handler goese like this:
class BooksHandler(RequestHandler): def get(self, id=None): if id is not None: # render book list page else: # render book detail of id
Wouldn't that be great if we could just seperate the 2 unrelated pieces of code into 2 methods, like the following:
class BooksHandler(RequestHandler): def index(self): # render book list page def show(self, id): # render book detail of id
And that is the main goal of the refactoring, to organize the handlers in a
Resource way instead of
Luckly Ruby on Rails already done this before and have been proved to be practisable even in large projects. So I'm just gonna copy the resource abstraction of Rails, that is:
- A Handler is for manumating one resource.
- A Handler can have at most 7 actions. The following table shows what the action should do and how does a http request reach them.
|HTTP Verb||Path||Handler#Action||Used for|
|GET||/photos||photos#index||display a list of all photos|
|GET||/photos/new||photos#new||return an HTML form for creating a new photo|
|POST||/photos||photos#create||create a new photo|
|GET||/photos/:id||photos#show||display a specific photo|
|GET||/photos/:id/edit||photos#edit||return an HTML form for editing a photo|
|PATCH/PUT||/photos/:id||photos#update||update a specific photo|
|DELETE||/photos/:id||photos#destroy||delete a specific photo|
Then What about login? I don't see any resources in that action.
Everything can be resources. For login/logout, it's actually create a session and destroy a session.
Now if we organizing our handlers in this way, there is no need for us to write the router's url matcher manully, a handler should populate the routes by itself, like these:
class BooksHandler(RequestHandler): def index(self): # render book list page def show(self, id): # render book detail of id app = tornado.web.Application([ # no more manully set regex *BooksHandler.routes() ]) app.listen(8888) tornado.ioloop.IOLoop.current().start()
Now, it should be good to visit the 2 url:
curl http://localhost:8888/books curl http://localhost:8888/books/1
I have already implemented this at https://github.com/louis-she/tornado-resource-handler. This can be directly used in existed projects, just try to write new handler in resource way, it will not broke the existing handlers.
Have fun with abstracting Resources.