首页 > 解决方案 > python 的 SimpleHTTPServer do_GET 和 do_POST 函数是如何工作的?

问题描述

出于学习目的,我创建了以下小型 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 类中的 do_GET 覆盖?

问题:与 do_GET 和 do_POST 相关的幕后情况是什么?是不是这样,一旦我们让这个服务器监听指向特定 IP:PORT 的 HTTP“活动”,它会自动知道传入信号是 GET 还是 POST 并且一旦遇到,服务器就会调用我的 do_GET 或 do_POST职能?

标签: pythonserversimplehttpserver

解决方案


当您调用 时SocketServer.TCPServer,您将您Handler的班级分配为接收传入请求的班级。

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

因此,正如您所说,当您定义 时Handler,您将继承SimpleHTTPRequestHandler该类的所有方法,然后覆盖两个预定义的方法:do_GETdo_POST. 您还可以覆盖类中的任何其他方法。

但是,如果不是在中定义的方法,这些do_*方法将永远不会被调用,因为它是模块调用的这个函数。handleSimpleHTTPRequestHandlersocketserver

所以如果你只是继承socketserver.BaseRequestHandler,你会失去所有的功能,因为这个类的handle()方法什么都不做:

class socketserver.BaseRequestHandler

...

handle()

此功能必须完成为请求提供服务所需的所有工作。默认实现什么也不做。它有几个实例属性可用;该请求可作为 self.request 获得;客户端地址为 self.client_address;服务器实例为 self.server,以防它需要访问每个服务器的信息。

...

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

所有这些功能都在此处记录,其中重要的一点是它的handle方法:

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

...

handle()

调用 handle_one_request() 一次(或者,如果启用了持久连接,则调用多次)来处理传入的 HTTP 请求。您永远不需要覆盖它;相反,实现适当的 do_*() 方法。

handle_one_request()

此方法将解析请求并将其分派给适当的 do_*() 方法。您永远不需要覆盖它。

...

因此,最后,在分解了将如何为您传递的任何socketserver.TCPServer类调用handle()方法之后,我们将看到如何将其实现为将请求传递到适当的,或者取决于请求标头的任何方法。SimpleHTTPRequestHandlerdo_GETdo_POST


如果您想了解如何自己实现此功能,请查看GitHub/usr/lib/pythonX.Y/http/server.py中或上的源代码。

我们可以在那里看到它们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_requestdo_*调用方法的地方:

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

(注意,我对我的评论进行了双重哈希(##)以将它们与原作者的评论区分开来)


推荐阅读