python 的 SimpleHTTPServer do_GET 和 do_POST 函数如何工作?

How does python's SimpleHTTPServer do_GET and do_POST functions work?

出于学习目的,我创建了以下小型 HTTP 服务器:

import SimpleHTTPServer
import SocketServer

class ServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

    def do_GET(self):
        print(self.headers)
        SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)

    def do_POST(self):
        print(self.headers)
        form = cgi.FieldStorage(
            fp=self.rfile,
            headers=self.headers,
            environ={'REQUEST_METHOD':'POST',
                 'CONTENT_TYPE':self.headers['Content-Type'],

def main():
    port = 50738
    Handler = ServerHandler(1000)
    httpd = SocketServer.TCPServer(("192.168.X.Y", port), Handler)

    print "serving at port", port
    httpd.serve_forever()


if __name__ == "__main__":
    main()

我的假设如下:

旁白:我通过查看 Python 文档 https://docs.python.org/2/library/simplehttpserver.html 知道 SimpleHTTPServer.SimpleHTTPRequestHandler 有一个名为 do_GET 的方法,我假设它被我的 ServerHandler class?

中的 do_GET 覆盖了

问题: do_GET 和 do_POST 背后发生了什么?是不是一旦我们让这个服务器侦听指向特定 IP:PORT 的 HTTP "activity" ,它就会自动知道传入信号是 GET 还是 POST 并且一旦是遇到服务器调用我的 do_GET 或 do_POST 函数?

当您调用 SocketServer.TCPServer 时,您将 Handler class 指定为 class 以接收传入请求。

SimpleHTTPServer 模块为您提供的全部帮助是提供基本的 HTTP 功能,但您可以自己编写所有这些功能。

所以,正如您所说,当您定义 Handler 时,您继承了 SimpleHTTPRequestHandler class 的所有方法,但随后覆盖了 pre-defined 中的两个方法方法:do_GETdo_POST。您还可以覆盖 class.

中的任何其他方法

但是,如果 SimpleHTTPRequestHandler 中定义的 handle 方法不是这样,这些 do_* 方法将 永远不会被调用 socketserver 模块 就是 调用的这个函数。

因此,如果您只是继承 socketserver.BaseRequestHandler,您将失去所有功能,因为此 class' handle() 方法什么都不做:

class socketserver.BaseRequestHandler

...

handle()

This function must do all the work required to service a request. The default implementation does nothing. Several instance attributes are available to it; the request is available as self.request; the client address as self.client_address; and the server instance as self.server, in case it needs access to per-server information.

...

因此,通过从 SimpleHTTPServer 模块导入 SimpleHTTPRequestHandler,您可以立即获得 HTTP 服务器的基本功能。

所有这些功能都记录在案 here,其 handle 方法中有重要一点:

class http.server.BaseHTTPRequestHandler(request, client_address, server)

...

handle()

Calls handle_one_request() once (or, if persistent connections are enabled, multiple times) to handle incoming HTTP requests. You should never need to override it; instead, implement appropriate do_*() methods.

handle_one_request()

This method will parse and dispatch the request to the appropriate do_*() method. You should never need to override it.

...

所以最后,在分解 socketserver.TCPServer 将如何为 调用 handle() 方法之后 无论什么 class 你传递它,我们看到SimpleHTTPRequestHandler 如何将请求传递到适当的 do_GETdo_POST 或任何方法,具体取决于请求的 headers。


如果您想了解如何自己实现,请查看 /usr/lib/pythonX.Y/http/server.pyGitHub 中的源代码。

我们可以看到 SimpleHTTPServer 继承了 BaseHTTPServer 的内容,这是定义 handle()handle_one_request() 方法的地方:

因此,正如文档所述,handle 只是将请求传递给 handle_one_request,直到连接关闭:

def handle(self):
    """Handle multiple requests if necessary."""
    self.close_connection = True

    self.handle_one_request()
    while not self.close_connection:
        self.handle_one_request()

并且 handle_one_request 是调用 do_* 方法的地方:

def handle_one_request(self):
    """Handle a single HTTP request.
    You normally don't need to override this method; see the class
    __doc__ string for information on how to handle specific HTTP
    commands such as GET and POST.
    """
    try:
        self.raw_requestline = self.rfile.readline(65537)
        if len(self.raw_requestline) > 65536:
            self.requestline = ''
            self.request_version = ''
            self.command = ''
            self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
            return
        if not self.raw_requestline:
            self.close_connection = True
            return
        if not self.parse_request():
            # An error code has been sent, just exit
            return
        mname = 'do_' + self.command   ## the name of the method is created
        if not hasattr(self, mname):   ## checking that we have that method defined
            self.send_error(
                HTTPStatus.NOT_IMPLEMENTED,
                "Unsupported method (%r)" % self.command)
            return
        method = getattr(self, mname)  ## getting that method
        method()                       ## finally calling it
        self.wfile.flush() #actually send the response if not already done.
    except socket.timeout as e:
        #a read or a write timed out.  Discard this connection
        self.log_error("Request timed out: %r", e)
        self.close_connection = True
        return

(注意,我double-hashed(##)我的评论将它们与原作者的分开)