【问题标题】:Can one handler handle both post and get with different URLs in tornado一个处理程序可以在龙卷风中同时处理 post 和 get 的不同 URL
【发布时间】:2021-06-18 15:29:24
【问题描述】:

一个处理程序能否在 tornado 中同时处理 post 和 get 请求(URL 略有不同)?

例如,我想要:

  • /app/user (POST) 使用请求中的数据创建新用户 身体
  • /app/user/<user_id> (GET) 返回给定 id 的用户

目前我有两个处理程序 GetUserPostUser 和两条路线。

【问题讨论】:

    标签: python tornado


    【解决方案1】:

    是的,您可以这样做,但是,到目前为止,我看到的所有快速解决方案在处理位置参数时总是存在一些小的不便。看看这个例子:

    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 中的捕获组是可选的,因此当向/app/user 发送 POST 请求时,None 可能会作为user_id 传递。使用这种方法,tornado 将始终使用正则表达式中第二组的参数调用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()
    

    请记住,tornado 会为 POST 和 GET 请求匹配两条路由,从而导致以下不便:

    • 如果有人向/app/user 发送POST 请求,它会抛出TypeError: post() missing 1 required positional argument: 'user_id'
    • 如果有人向/app/user/123 发送GET 请求,它会抛出TypeError: get() takes 1 positional argument but 2 were given

    您可以解决这些问题,但我认为第一个解决方案以更简洁的方式解决了这个问题。


    编辑:当然,您也可以实现自己的 Matcher 类,它不仅可以区分 url,还可以通过子类化 tornado.routing.PathMatches 来区分请求方法 - 不过这需要更多代码。这里的主要缺点是对于有效的路由,服务器会在应该是405s的地方返回404响应(由于路由/“资源”本身是有效的,只是方法错误)

    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
    

    【讨论】:

    • 感谢您的回答!我很惊讶这不是本机支持的,因为我认为这是一个常见的用例
    【解决方案2】:

    简单

    def options(self):
        return
    get=head=post=delete=patch=put=options
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多