如何将参数传递给RequestHandler?

时间:2014-02-07 15:37:30

标签: python

Python文档包括an example of creating an HTTP server

def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    httpd.serve_forever()

RequestHandler类提供给Server,然后自动处理实例化处理程序。

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

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

似乎应该通过覆盖Server类的部分来实现这一点,但我觉得我忽略了一个更简单,更好的解决方案。

8 个答案:

答案 0 :(得分:22)

使用班级工厂:

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()

答案 1 :(得分:8)

我使用“部分应用程序”在代码中解决了这个问题。

示例是使用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()

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

    与工厂函数定义和返回的嵌套类相比,
  • partial对象更容易进行内部自省。
  • partial对象可以在现代Python中使用pickle进行序列化,而工厂函数中的嵌套类定义则不能。
  • 根据我有限的经验,使用partial将参数显式地“预先附加”到其他Pythonic和普通的类定义上是更容易(较少的认知负担),以便阅读,理解,并验证其正确性,而不是将嵌套函数的参数埋在其中的嵌套类定义。

唯一真正的缺点是许多人不熟悉partial-但以我的经验,每个人都变得熟悉{{1 }}无论如何,因为partial可以在许多地方弹出,作为一种容易且可组合的解决方案,有时会出乎意料,例如此处。

答案 2 :(得分:7)

我只想评论Thomas Orozco的答案,但因为我不能...... 也许这会帮助那些也遇到这个问题的人。在Python3之前,Python有#34; old-style"类,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

以避免TypeError: must be type, not classobj之类的错误。

答案 3 :(得分:4)

为什么不直接使用RequestHandler?

class RequestHandler(BaseHTTPRequestHandler):
     a_variable = None

class Server(HTTPServer):
    def serve_forever(self, variable):
        self.RequestHandlerClass.a_variable = variable 
        HTTPServer.serve_forever(self)

def run(server_class=Server, handler_class=RequestHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    variable = sys.argv
    httpd.serve_forever(variable)

答案 4 :(得分:1)

如果您不需要实例属性,而只需要类属性,则可以使用以下方法:

def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    httpd.RequestHandlerClass.my_custom_variable = "hello!"
    httpd.serve_forever()

或者您可以:

def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    httpd.my_custom_variable = "hello!"
    httpd.serve_forever()

并使用以下命令在您的RequestHandler中进行检索:

self.server.my_custom_variable

答案 5 :(得分:1)

Ref子类化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()

答案 6 :(得分:0)

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

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

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

    def do_GET(self):
        self.send_response(200)
        self.end_headers()
        self.wfile.write(self.message.encode('utf-8'))

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

handler = MyHandler("Hello world")
server = http.server.HTTPServer(('localhost', 8000), handler)
server.serve_forever()

答案 7 :(得分:-3)

永远不要使用global。使用其他答案中描述的工厂。

CONFIG = None

class MyHandler(BaseHTTPRequestHandler):
     def __init__(self, ...
         self.config = CONFIG       # CONFIG is now 'stuff'

if __name__ == "__main__":
    global CONFIG
    CONFIG = 'stuff'
    server_address = ('', 8000)
    httpd = HTTPServer(server_address, MyHandler)
    httpd.serve_forever()

(可能在您自己家中的隐私之外)