Note 1: If this question is a duplicate, please link to the original question and close this one. I am not sure what the proper terminology for these concepts are. Note 2: If this question is better suited for softwareengineering.SE, please let me know so this question can be closed and I can ask it there. That site doesn't have a tornado
tag though.
Hypothetical Scenario: Consider the following scenario, where the HTTP server at <base_url>
is responsible for four endpoints:
<base_url>/cats/kittens
<base_url>/cats/tigers
<base_url>/dogs/puppies
<base_url>/dogs/wolves
which leads us to have the following routing table of URLSpec
‘s:
server = tornado.web.Application([
(r"/cats/kittens", KittenHandler, kitten_kwargs),
(r"/cats/tigers", TigerHandler, tiger_kwargs),
(r"/dogs/puppies", PuppyHandler, puppy_kwargs),
(r"/dogs/wolves", WolfHandler, wolf_kwargs),
])
where the handlers, for the sake of completeness, could be defined as below:
class KittenHandler(tornado.web.RequestHandler):
def get(self):
self.write("mew")
class TigerHandler(tornado.web.RequestHandler):
def get(self):
self.write("ROAR")
class PuppyHandler(tornado.web.RequestHandler):
def get(self):
self.write("woof")
class WolfHandler(tornado.web.RequestHandler):
def get(self):
self.write("HOWL")
However, let's say that later I want to add handlers for lions, servals, jackals, pumas, coyotes, and foxes, and I want to have the pattern for dogs now additionally match "/canines/"
(not just "/dogs/"
) and the pattern for cats now additionally match "/felines/"
(not just "/cats/"
). And I want to pass keyword arguments cat_kwargs
only to all cat endpoints, and pass keyword arguments dog_kwargs
only to all dog endpoints. What I think should happen is that there should be a "separation of concerns" between a "cat subserver" and a "dog subserver". Then I can make changes affecting all cat handlers in one fell swoop, and changes affecting all dog handlers in one fell swoop, and without changes for one affecting the other.
Question(s):
- Is having (in this case two) "subservers" or "subapplications", one for dogs and one for cats (and maybe in the future one for horses) a pattern or an anti-pattern? I.e. is it actually a good idea, and if not, what would be a better strategy?
- Depending on the answer to the first question, how could one best implement such a pattern using Tornado?
Hypothetical scenario continued:
As far as I understand, tornado.web.URLSpec
is actually a subclass of tornado.routing.RuleRouter
, and so the target
in the (regex, target, kwargs)
tuple does not have to be a subclass of tornado.web.RequestHandler
, it could perhaps be something like a subclass of tornado.web.Application
? So I think what I would like to implement might be like:
DogServer = tornado.web.Application([
(r"/puppies", PuppyHandler, puppy_kwargs),
(r"/wolves", WolfHandler, wolf_kwargs),
])
CatServer = tornado.web.Application([
(r"/kittens", KittenHandler, kitten_kwargs),
(r"/tigers", TigerHandlers, tiger_kwargs),
])
server = tornado.web.Application([
(r"/cats", CatServer, **cat_kwargs),
(r"/dogs", DogServer, **dog_kwargs),
])
Would the above even work? And second, even if it works, what would be the best way to implement a pattern like this? For example, I think it might be useful to somehow have KittenHandler
and TigerHandler
to be attributes of some CatProvider
class (so people could easily modify it by swapping in their own custom KittenHandler
's or TigerHandler
's), or maybe subclasses of some CatHandler
. For example, would something better than the above be the following?
class CatProvider(AnimalProvider):
def __init__(self, kitten_handler=KittenHandler, tiger_handler=TigerHandler):
self.kitten_handler = kitten_handler
self.tiger_handler = tiger_handler
kitten_regex = r"/kittens"
kitten_kwargs = {...}
tiger_regex = r"/tigers"
tiger_kwargs = {...}
def server(self):
return tornado.web.Application([
(self.kitten_regex, self.kitten_handler, self.kitten_kwargs),
(self.tiger_regex, self.tiger_handler, self.tiger_kwargs),
])
class DogProvider(AnimalProvider):
def __init__(self, puppy_handler=PuppyHandler, wolf_handler=WolfHandler):
self.puppy_handler = puppy_handler
self.wolf_handler = wolf_handler
puppy_regex = r"/puppies"
puppy_kwargs = {...}
wolf_regex = r"/wolves"
wolf_kwargs = {...}
def server(self):
return tornado.web.Application([
(self.puppy_regex, self.puppy_handler, self.puppy_kwargs),
(self.wolf_regex, self.wolf_handler, self.wolf_kwargs),
])
server = tornado.web.Application([
(r"/cats", CatProvider(KittenHandler, TigerHandler).server(), **cat_kwargs),
(r"/dogs", DogProvider(PuppyHandler, WolfHandler).server(), **dog_kwargs),
])
I quickly become confused about what should be the appropriate levels of abstraction for everything.
Aucun commentaire:
Enregistrer un commentaire