阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

WSGI接口

31次阅读
没有评论

共计 2761 个字符,预计需要花费 7 分钟才能阅读完成。

了解了 HTTP 协议和 HTML 文档,我们其实就明白了一个 Web 应用的本质就是:

  1. 浏览器发送一个 HTTP 请求;
  2. 服务器收到请求,生成一个 HTML 文档;
  3. 服务器把 HTML 文档作为 HTTP 响应的 Body 发送给浏览器;
  4. 浏览器收到 HTTP 响应,从 HTTP Body 取出 HTML 文档并显示。

所以,最简单的 Web 应用就是先把 HTML 用文件保存好,用一个现成的 HTTP 服务器软件,接收用户请求,从文件中读取 HTML,返回。Apache、Nginx、Lighttpd 等这些常见的静态服务器就是干这件事情的。

如果要动态生成 HTML,就需要把上述步骤自己来实现。不过,接受 HTTP 请求、解析 HTTP 请求、发送 HTTP 响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态 HTML 呢,就得花个把月去读 HTTP 规范。

正确的做法是底层代码由专门的服务器软件实现,我们用 Python 专注于生成 HTML 文档。因为我们不希望接触到 TCP 连接、HTTP 原始请求和响应格式,所以,需要一个统一的接口,让我们专心用 Python 编写 Web 业务。

这个接口就是 WSGI:Web Server Gateway Interface。

WSGI 接口定义非常简单,它只要求 Web 开发者实现一个函数,就可以响应 HTTP 请求。我们来看一个最简单的 Web 版本的“Hello, web!”:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'<h1>Hello, web!</h1>']

上面的 application() 函数就是符合 WSGI 标准的一个 HTTP 处理函数,它接收两个参数:

  • environ:一个包含所有 HTTP 请求信息的 dict 对象;
  • start_response:一个发送 HTTP 响应的函数。

application() 函数中,调用:

start_response('200 OK', [('Content-Type', 'text/html')])

就发送了 HTTP 响应的 Header,注意 Header 只能发送一次,也就是只能调用一次 start_response() 函数。start_response()函数接收两个参数,一个是 HTTP 响应码,一个是一组 list 表示的 HTTP Header,每个 Header 用一个包含两个 strtuple表示。

通常情况下,都应该把 Content-Type 头发送给浏览器。其他很多常用的 HTTP Header 也应该发送。

然后,函数的返回值 b'<h1>Hello, web!</h1>' 将作为 HTTP 响应的 Body 发送给浏览器。

有了 WSGI,我们关心的就是如何从 environ 这个 dict 对象拿到 HTTP 请求信息,然后构造 HTML,通过 start_response() 发送 Header,最后返回 Body。

整个 application() 函数本身没有涉及到任何解析 HTTP 的部分,也就是说,底层代码不需要我们自己编写,我们只负责在更高层次上考虑如何响应请求就可以了。

不过,等等,这个 application() 函数怎么调用?如果我们自己调用,两个参数 environstart_response我们没法提供,返回的 bytes 也没法发给浏览器。

所以 application() 函数必须由 WSGI 服务器来调用。有很多符合 WSGI 规范的服务器,我们可以挑选一个来用。但是现在,我们只想尽快测试一下我们编写的 application() 函数真的可以把 HTML 输出到浏览器,所以,要赶紧找一个最简单的 WSGI 服务器,把我们的 Web 应用程序跑起来。

好消息是 Python 内置了一个 WSGI 服务器,这个模块叫 wsgiref,它是用纯 Python 编写的 WSGI 服务器的参考实现。所谓“参考实现”是指该实现完全符合 WSGI 标准,但是不考虑任何运行效率,仅供开发和测试使用。

运行 WSGI 服务

我们先编写hello.py,实现 Web 应用程序的 WSGI 处理函数:

# hello.py

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'<h1>Hello, web!</h1>']

然后,再编写一个 server.py,负责启动 WSGI 服务器,加载application() 函数:

# server.py
# 从 wsgiref 模块导入:
from wsgiref.simple_server import make_server
# 导入我们自己编写的 application 函数:
from hello import application

# 创建一个服务器,IP 地址为空,端口是 8000,处理函数是 application:
httpd = make_server('', 8000, application)
print('Serving HTTP on port 8000...')
# 开始监听 HTTP 请求:
httpd.serve_forever()

确保以上两个文件在同一个目录下,然后在命令行输入 python server.py 来启动 WSGI 服务器:

WSGI 接口

注意:如果 8000 端口已被其他程序占用,启动将失败,请修改成其他端口。

启动成功后,打开浏览器,输入http://localhost:8000/,就可以看到结果了:

WSGI 接口

在命令行可以看到 wsgiref 打印的 log 信息:

WSGI 接口

Ctrl+C 终止服务器。

如果你觉得这个 Web 应用太简单了,可以稍微改造一下,从 environ 里读取PATH_INFO,这样可以显示更加动态的内容:

# hello.py

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    body = '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'web')
    return [body.encode('utf-8')]

你可以在地址栏输入用户名作为 URL 的一部分,将返回Hello, xxx!

WSGI 接口

是不是有点 Web App 的感觉了?

小结

无论多么复杂的 Web 应用程序,入口都是一个 WSGI 处理函数。HTTP 请求的所有输入信息都可以通过 environ 获得,HTTP 响应的输出都可以通过 start_response() 加上函数返回值作为 Body。

复杂的 Web 应用程序,光靠一个 WSGI 函数来处理还是太底层了,我们需要在 WSGI 之上再抽象出 Web 框架,进一步简化 Web 开发。

参考源码

hello.py

server.py

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-07发表,共计2761字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中