Да, вы можете сделать это, однако, все быстрые решения, которые я видел до сих пор, всегда имели незначительные неудобства при обработке позиционных аргументов. Взгляните на этот пример:
import tornado.ioloop
import tornado.web
class UserHandler(tornado.web.RequestHandler):
def get(self, __):
self.write("Get request")
def post(self, user_id):
self.write(f"Post request for user id {user_id}")
app = tornado.web.Application([
(r"/app/user/?(\d+)?", UserHandler),
])
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
Это создаст правило маршрутизации, которое будет соответствовать обоим шаблонам URL и назначит UserHandler
. Группа захвата в URL-адресе является необязательной, поэтому None
может передаваться как user_id
, когда запрос POST отправляется на /app/user
. При таком подходе торнадо всегда будет вызывать метод get
с аргументом для второй группы в регулярном выражении. В зависимости от вашего проекта, вы можете захотеть сделать более обширную проверку ошибок (проверьте заданный аргумент, и если это не None
, отправьте 405 Method Not Allowed
или что-то еще подходящее в вашем случае - возможно, вы хотите разрешить GETдля одного пользователя?)
Это также возможно сделать, добавив два правила с одним и тем же обработчиком:
import tornado.ioloop
import tornado.web
class UserHandler(tornado.web.RequestHandler):
def get(self):
self.write("Get request")
def post(self, user_id):
self.write(f"Post request for user id {user_id}")
app = tornado.web.Application([
(r"/app/user", UserHandler),
(r"/app/user/(\d+)", UserHandler),
])
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
Имейте в виду, что оба маршрута будут совпадать для POSTи получает запросы от торнадо, что приводит к таким неудобствам:
- Если кто-то отправляет запрос POST на
/app/user
, он выдаст TypeError: post() missing 1 required positional argument: 'user_id'
. - Если кто-то отправит GETзапрос
/app/user/123
, он выдаст TypeError: get() takes 1 positional argument but 2 were given
Вы можете обойти это, но я думаю, что первое решение решает это более чистым способом.
Редактировать: Конечно, вы также можете реализовать свой собственный класс Matcher
, который позволяет не только различать URL-адреса, но и запрашивать методы, используя подклассы tornado.routing.PathMatches
- хотя это немного больше кода. Основным недостатком здесь является то, что для допустимых маршрутов сервер вернет 404 ответа, где он должен быть 405 с (поскольку сам маршрут / «ресурс» действителен, это просто неправильный метод)
import tornado.ioloop
import tornado.web
import tornado.routing
class MethodAndPathMatch(tornado.routing.PathMatches):
def __init__(self, method, path_pattern):
super().__init__(path_pattern)
self.method = method
def match(self, request):
if request.method != self.method:
return None
return super().match(request)
class UserHandler(tornado.web.RequestHandler):
def get(self):
self.write("Get request")
def post(self, user_id):
self.write(f"Post request for user id {user_id}")
app = tornado.web.Application([
(MethodAndPathMatch("GET", r"/app/user"), UserHandler),
(MethodAndPathMatch("POST", r"/app/user/(\d+)"), UserHandler),
])
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
cURL призывает к тестированию:
$ curl -w '\n' -X POST localhost:8000/app/user
$ curl -w '\n' -X POST localhost:8000/app/user/123
$ curl -w '\n' -X GET localhost:8000/app/user
$ curl -w '\n' -X GET localhost:8000/app/user/123