处理 POST 请求

所有之前的示例都集中在 GET 请求上。与 GET 请求不同,POST 请求可以有请求主体 - 请求头之后的额外数据;例如,表示 HTML 表单内容的数据。Twisted Web 通过 Request 对象将此数据提供给应用程序。

以下是一个 Web 服务器示例,它呈现一个静态 HTML 表单,然后在该表单被发布回服务器时生成一个动态页面。免责声明:虽然这对本示例来说很方便,但通常不建议让一个资源 POST 到自身;这与 Twisted Web 无关,而是 HTTP 本身的性质;如果你在真实应用程序中这样做,请确保你了解可能产生的负面影响。

像往常一样,我们从一些导入开始。除了 Twisted 导入之外,本示例还使用 html 模块来 转义用户输入内容 以便将其包含在输出中。

from twisted.web.server import Site
from twisted.web.resource import Resource
from twisted.internet import reactor, endpoints

import html

接下来,我们将定义一个资源,它将执行两件事。首先,它将使用静态 HTML 表单响应 GET 请求

class FormPage(Resource):
    def render_GET(self, request):
        return (b"<!DOCTYPE html><html><head><meta charset='utf-8'>"
                b"<title></title></head><body>"
                b"<form><input name='the-field' type='text'></form>")

这与 上一期 中使用的资源类似。但是,我们现在将添加一个方法来赋予它第二个行为;这个 render_POST 方法将允许它接受 POST 请求

...
    def render_POST(self, request):
        args = request.args[b"the-field"][0].decode("utf-8")
        escapedArgs = html.escape(args)
        return (b"<!DOCTYPE html><html><head><meta charset='utf-8'>"
                b"<title></title></head><body>"
                b"You submitted: " + escapedArgs.encode('utf-8'))

这里需要注意的主要事项是使用 request.args 。这是一个类似字典的对象,它提供对表单内容的访问。此字典中的键是表单中输入的名称。每个值都是一个包含字节对象的列表(因为可以有多个具有相同名称的输入),这就是为什么我们必须提取第一个元素才能传递给 html.escape 。只要使用 application/x-www-form-urlencodedmultipart/form-data 内容类型发出 POST 请求,request.args 就会从表单内容中填充(它也会被任何类型请求的查询参数填充)。

最后,这个示例只需要通常的站点创建和端口设置

root = Resource()
root.putChild(b"form", FormPage())
factory = Site(root)
endpoint = endpoints.TCP4ServerEndpoint(reactor, 8880)
endpoint.listen(factory)
reactor.run()

运行服务器并访问 http://localhost:8880/form ,提交表单,然后观察它生成一个包含你输入到单个字段中的值的页面。

以下是示例的完整源代码

from twisted.web.server import Site
from twisted.web.resource import Resource
from twisted.internet import reactor, endpoints

import html

class FormPage(Resource):
    def render_GET(self, request):
        return (b"<!DOCTYPE html><html><head><meta charset='utf-8'>"
                b"<title></title></head><body>"
                b"<form method='POST'><input name='the-field'></form>")

    def render_POST(self, request):
        args = request.args[b"the-field"][0].decode("utf-8")
        escapedArgs = html.escape(args)
        return (b"<!DOCTYPE html><html><head><meta charset='utf-8'>"
                b"<title></title></head><body>"
                b"You submitted: " + escapedArgs.encode('utf-8'))

root = Resource()
root.putChild(b"form", FormPage())
factory = Site(root)
endpoint = endpoints.TCP4ServerEndpoint(reactor, 8880)
endpoint.listen(factory)
reactor.run()