如何将参数传递给RequestHandler?

Jak*_*kob 25 python

Python文档包含创建HTTP服务器的示例:

def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    httpd.serve_forever()
Run Code Online (Sandbox Code Playgroud)

RequestHandler提供了一个类Server,然后负责自动实例化处理程序.

假设我想在创建请求处理程序时将自定义参数传递给它.我该怎么做?

更具体地说,我想从命令行传入参数,并且必须访问sys.argv请求处理程序类内部似乎不必要地笨拙.

看起来这应该是可以通过覆盖Server类的部分,但我觉得我忽略了一个更简单和更好的解决方案.

Tho*_*zco 23

使用班级工厂:

def MakeHandlerClassFromArgv(init_args):
    class CustomHandler(BaseHTTPRequestHandler):
        def __init__(self, *args, **kwargs):
             super(CustomHandler, self).__init__(*args, **kwargs)
             do_stuff_with(self, init_args)
    return CustomHandler

if __name__ == "__main__":
    server_address = ('', 8000)
    HandlerClass = MakeHandlerClassFromArgv(sys.argv)
    httpd = HTTPServer(server_address, HandlerClass)
    httpd.serve_forever()
Run Code Online (Sandbox Code Playgroud)

  • 我知道了.顺便说一下,当我在调用super之前添加self.do_stuff_with_self(init_args)时,它只适用于我.我只是在属性中保存参数. (7认同)
  • 需要记住的一件事是,`BaseHTTPRequestHandler` 实际上运行了像 `do_GET` **inside** 其 `__init__` 方法这样的处理函数,所以你必须在调用 `super().__init__` 之前进行初始化,与更典型的默认最佳实践。 (5认同)

mtr*_*eur 11

我在代码中使用"部分应用程序"解决了这个问题.

示例使用Python 3编写,但部分应用程序在Python 2中的工作方式相同:

from functools import partial
from http.server import HTTPServer, BaseHTTPRequestHandler

class ExampleHandler(BaseHTTPRequestHandler):
    def __init__(self, foo, bar, qux, *args, **kwargs):
        self.foo = foo
        self.bar = bar
        self.qux = qux
        # BaseHTTPRequestHandler calls do_GET **inside** __init__ !!!
        # So we have to call super().__init__ after setting attributes.
        super().__init__(*args, **kwargs)

    def do_HEAD(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()

    def do_GET(self):
        self.do_HEAD()
        self.wfile.write('{!r} {!r} {!r}\n'
                         .format(self.foo, self.bar, self.qux)
                         .encode('utf8'))

# We "partially apply" the first three arguments to the ExampleHandler
handler = partial(ExampleHandler, sys.argv[1], sys.argv[2], sys.argv[3])
# .. then pass it to HTTPHandler as normal:
server = HTTPServer(('', 8000), handler)
server.serve_forever()
Run Code Online (Sandbox Code Playgroud)

这与一个类工厂非常相似,但在我看来它有一些微妙的优点:

  • partial 对象内部的内容比工厂函数定义和返回的嵌套类更容易内省.
  • partial对象可以pickle在现代Python中序列化,而工厂函数中的嵌套类定义则不能.
  • 在我有限的经验中,将参数与partial其他Pythonic和普通类定义明确地"预先附加" 比使用包装函数的参数嵌套的类定义更容易(更少的认知负荷)来读取,理解和验证正确性在它里面的某个地方.

唯一真正的缺点是,很多人不熟悉partial-但在我的经验是为了更好的每一个人变得熟悉partial,无论如何,因为partial有弹出在很多地方容易和组合的解决方案,有时会出现异常,喜欢这里的一种方式.

  • 非常有用,谢谢你的想法!在我的情况下,我需要在不同的调用之间保持状态,并且不想让它成为全局的。在这种情况下,请记住每次请求服务时都会创建 ExampleHandler 的实例,因此创建处理程序类对我有帮助:`partial(TheHandler, myStatefulHandler)` 然后是 `def do_POST(self): self.myStatefulHandler.handle (self.path, ...)` (2认同)

Jak*_*kob 7

在撰写本文时,这里的所有答案基本上都坚持(非常尴尬)的意图,即socketserver模块的作者似乎让传入的处理程序是一个类(即构造函数)。实际上,处理程序唯一需要的是它是可调用的(因此mtraceur的回答是使用partial)。因此,我们可以socketserver通过使处理程序类的实例可调用并让它们__init__在调用时运行超类的代码来解决API 。在 Python 3 中:

class MyHandler(http.server.BaseHTTPRequestHandler):
    def __init__(self, message):
        self.message = message

    def __call__(self, *args, **kwargs):
        """ Handle a request """
        super().__init__(*args, **kwargs)

    def do_GET(self):
        self.send_response(200)
        self.end_headers()
        self.wfile.write(self.message.encode('utf-8'))
Run Code Online (Sandbox Code Playgroud)

这使超类的“构造函数”调用__init__远离,从而消除了在子类的构造函数完成之前分派请求(来自超类的构造函数)的可能性。它还使使用 API 看起来更自然:

handler = MyHandler("Hello world")
server = http.server.HTTPServer(('localhost', 8000), handler)
server.serve_forever()
Run Code Online (Sandbox Code Playgroud)


use*_*049 6

我只想评论Thomas Orozco的答案,但是因为我不能......也许这会帮助那些也遇到这个问题的人.在Python3之前,Python有"旧式"类,BaseHTTPRequestHandler似乎就是其中之一.所以,工厂应该是这样的

def MakeHandlerClassFromArgv(init_args):
    class CustomHandler(BaseHTTPRequestHandler, object):
        def __init__(self, *args, **kwargs):
             do_stuff_with(self, init_args)
             super(CustomHandler, self).__init__(*args, **kwargs)
    return CustomHandler
Run Code Online (Sandbox Code Playgroud)

避免像这样的错误TypeError: must be type, not classobj.


Abe*_*Abe 5

参考子类化HTTPServer是另一种选择。服务器上的变量可以通过请求处理程序方法访问self.server.context。它基本上是这样工作的:

class MyHTTPServer(HTTPServer):

    def __init__(self, *args, **kwargs):
        HTTPServer.__init__(self, *args, **kwargs)
        self.context = SomeContextObject()


class MyHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        context = self.server.context
        ...

# Drawback, Notice that you cannot actually pass the context parameter during constructor creation, but can do it within the __init__ of the MyHTTPServer
server = MyHTTPServer(('', port), MyHandler)
server.serve_forever()
Run Code Online (Sandbox Code Playgroud)