WSGI APP

函数必须两参数,一个是envirn 字典wsgiserver封装成请求字典,另一个是start_response ,然后调用app,

wsgi 原理:

客户端(网站)发送请求报文到 wsgi Server,wsgiserver会将请求封装成environ 字典,还会提供start_response函数,然后通过Django调用app函数,函数必须有两个参数,一个是enviro字典,另一个是start_response 函数(两个参数,一个是status状态码,另一个是二元组比如:start_response(‘200 OK’,[(‘Content-Type’,’text/html;charset=utf-8’)])),在app函数返回前,必须调用start_response(返回响应头),生成响应body体,然后返回给wsgi,wsgi生成响应报文,返回给客户端。

image

WSGI

image

WSGI (Web Server Gateway Interface) 主要规定了服务器端和应用程序间的接口

WEB server主要负责HTTP协议请求和响应,但不一定支持WSGI接口访问。

image

  • environ是简单封装的请求报文的字典
  • start_response 解决响应报文头的函数
  • app函数返回响应报文正文,简单理解就是html

image

image

自定义响应头和响应报文

image

WSGI服务器——wsgiref

wsgiref是python提供的一个wsgi参考实现库,不适合生产环境使用。

wsgiref.simple_server 模块实现了一个简单的wsgi http服务器

# 启动一个WSGI服务器
wsgiref.simple_server.make_server(host, port, app, server_class=WSGIServer, 
handler_class=WSGIRequestHandler) 
# 一个两参数函数,小巧完整的WSGI的应用程序的实现
wsgiref.simple_server.demo_app(environ, start_response)
# 返回文本例子
from wsgiref.simple_server import make_server, demo_app
ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, demo_app) # demo_app应用程序,可调用
server.serve_forever() # server.handle_request() 执行一次

WSGI APP应用程序端

  1. 应用程序应该是一个可调用对象

python中应该是函数、类、实现了__call__方法的类的实例

  1. 这个可调用对象应该接受两个参数
import wsgiref.simple_server
from wsgiref.simple_server import make_server, demo_app
def fn():
    return  """
    <html>
    <head><title>test page</title></head>
    <body>
        <h2> 你好啊,这是我的网站 </h2>
    </body>
    </html>
    """.encode()

#1、函数
def app(environ,start_response):
    content = fn() #返回正文html,本质是字符串
    start_response('200 OK',[('Content-Type','text/html; charset=utf-8')])

    return  [content]#动态生成body
# ret值类型是可迭代对象,每一个元素响应正文一部分
# 2 class
class App:
    def __init__(self,environ,start_response):
        self.environ = environ
        self.start_response = start_response
    def __iter__(self):
        content = fn()
        self.start_response('200 OK',[('Content-Type','text/html; charset=utf-8')])
        return iter(content)

# 3 class的实例,实例是可调用对象
class Test:
    def __init__(self):
        pass
    def __call__(self,environ,start_response):
        content = fn()
        start_response('200 OK',[('Content-Type','text/html; charset=utf-8')])
        return [content]
t = Test()



server = make_server('127.0.0.1',8080,app)
server.serve_forever()

environ 和start_response 这两个参数名都可以是任何合法名,但是一般默认都是这2个名字。

应用程序端还有些其他的规定,暂不关心。

注意:第2、第3种实现调用时的不同

environ

environ 是包含http请求信息的dict字典对象

名称 含义
REQUEST_METHOD 请求方法,GET、POST等
PATH_INFO URL中的路径部分
QUERY_STRING 查询字符串
SERVER_NAME,SERVER_PORT 服务器名、端口
HTTP_HOST 地址和端口
SERVER_PROTOCOL 协议
HTTP_USER_AGENT UserAgent信息
CONTENT_TYPE = 'text/plain'
HTTP_HOST = '127.0.0.1:9999'
HTTP_USER_AGENT = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN) AppleWebKit/537.36 (KHTML, like Gecko) Version/5.0.1 Safari/537.36'
PATH_INFO = '/'
QUERY_STRING = ''
REMOTE_ADDR = '127.0.0.1'
REMOTE_HOST = ''
REQUEST_METHOD = 'GET'
SERVER_NAME = 'DESKTOP-D34H5HF'
SERVER_PORT = '9999'
SERVER_PROTOCOL = 'HTTP/1.1'
SERVER_SOFTWARE = 'WSGIServer/0.2'

start_response

它是一个可调用对象,有3个参数,定义如下:

start_response(status, response_headers, exc_info=None)

参数名 说明
status 状态码和状态描述,例如 200 OK
response_headers 一个元素为二元组的列表,例如[(‘Content-Type’, ‘text/plain;charset=uft-8’
exc_info 在错误处理的时候使用
start_response应该在返回可迭代对象执勤调用,因为它是Response Header。返回的可迭代对象是Response Body

服务器端

服务器程序需要调用符合上述定义的可调用对象APP,传入environ、start_response,APP处理后,返回响应头和可迭代对象的正文,由服务器封装返回浏览器端。

# 返回网页的例子
from wsgiref.simple_server import make_server
def application(environ, start_response):
    status = '200 OK'
    headers = [('Content-Type', 'text/html;charset=utf-8')]
    start_response(status, headers)
    # 返回可迭代对象
    html = '<h1>马哥教育欢迎你</h1>'.encode("utf-8")
    return [html]
ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, application)
server.serve_forever() # server.handle_request() 一次

simple_server 只是参考用,不能用于生产环境。

测试用命令

$ curl -I http://192.168.142.1:9999/xxx?id=5
$ curl -X POST http://192.168.142.1:9999/yyy -d '{"x":2}'

-I 使用HEAD方法

-X 指定方法,-d传输数据

到这里就完成了一个简单的WEB 程序开发。

总结

WSGI 服务器 的作用

  1. 监听HTTP服务端口(TCPServer,默认端口80)接受浏览器端的HTTP请求,这是www server的作用
  2. 解析请求报文封装成environ环境数据
  3. 负责调用应用程序app,将environ和start_response 方法以参数传给app
  4. 例如app的返回值,和start_response返回的值,构建http响应报文
  5. 将响应报文返回给浏览器

2、3、4要实现WSGI协议,该协议约定了和应用程序之间接口(参看PEP333,https://www.python.org/dev/peps/pep-0333/)

WSGI APP应用程序

  • 遵从WSGI协议
  • 本身是一个可调用对象
  • 调用start_response,返回响应头部
  • 返回包含正文的可迭代对象

Django、Flask都是符合WSGI协议且可以快速开发的框架,但本质上是编写Application,说白了,就是编写一个函数,这个函数签名为app(environ, start_response) ,这不过在app函数内部调用非常复杂而已。比如要解决访问数据库、静态HTML页面读取、动态网页生成等。