【问题标题】:How does python's SimpleHTTPServer do_GET and do_POST functions work?python 的 SimpleHTTPServer do_GET 和 do_POST 函数是如何工作的?
【发布时间】:2018-11-29 08:34:15
【问题描述】:

出于学习目的,我创建了以下小型 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()

我的假设如下:

  • 我的类 'ServerHandler' 通过 go_GET 和 do_POST 这两个函数扩展了 SimpleHTTPServer.SimpleHTTPRequestHandler 类
  • main() 函数创建一个服务器处理程序对象和绑定到我的 IP 的服务器套接字。选择的地址和端口,并调用一个函数来无限期地服务/监听。

旁白:通过查看 Python DOC 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职能?

【问题讨论】:

    标签: python server simplehttpserver


    【解决方案1】:

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

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

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

    然而,如果不是SimpleHTTPRequestHandler 中定义的handle 方法,这些do_* 方法将永远 被调用,因为它是这个函数 em> 由socketserver 模块调用。

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

    class socketserver.BaseRequestHandler

    ...

    handle()

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

    ...

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

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

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

    ...

    handle()

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

    handle_one_request()

    这个方法会解析并分发请求 到适当的 do_*() 方法。您永远不需要覆盖 它。

    ...

    所以最后,在分解socketserver.TCPServer 将如何为您传递它的whatever 类调用handle() 方法之后,我们将看到SimpleHTTPRequestHandler 如何将请求传递到适当的do_GETdo_POST 或任何取决于请求标头的方法。


    如果您想了解如何自己实现此功能,请查看/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
    

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

    【讨论】:

    • @ Joe lddon - 谢谢,这是一个很棒的答案。我现在知道了。不幸的是,我的用户评分不足以支持这个答案。再次感谢
    猜你喜欢
    • 2016-08-22
    • 2013-10-23
    • 2017-04-18
    • 2016-02-16
    • 2012-06-08
    • 1970-01-01
    • 2010-12-16
    相关资源
    最近更新 更多