Python模块WSGI使用详解
作者:Jack Big 发布时间:2021-02-19 00:05:27
WSGI(Web Server Gateway Interface):Web服务网关接口,是Python中定义的服务器程序和应用程序之间的接口。
Web程序开发中,一般分为服务器程序和应用程序。服务器程序负责对socket服务的数据进行封装和整理,而应用程序则负责对Web请求进行逻辑处理。
Web应用本质上也是一个socket服务器,用户的浏览器就是一个socket客户端。
我们先用socket编程实现一个简单的Web服务器:
import socket
def handle_request(client):
buf = client.recv(1024)
print(buf)
msg = "HTTP/1.1 200 OK\r\n\r\n" #HTTP头信息
client.send(('%s' % msg).encode())
msg = "Hello, World!"
client.send(('%s' % msg).encode())
def main():
ip_port = ("localhost", 8000)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(ip_port)
sock.listen(5)
while True:
conn, addr = sock.accept()
handle_request(conn)
conn.close()
if __name__ == "__main__":
main()
上述代码中,main()函数就是服务器函数,handle_request()就是应用程序。
下面我们再用python的wsgiref模块来实现跟上述代码一样的Web服务器:
from wsgiref.simple_server import make_server
def handle_request(env, res):
res("200 OK",[("Content-Type","text/html")])
body = "<h1>Hello World!</h1>"
return [body.encode("utf-8")]
if __name__ == "__main__":
httpd = make_server("",8000,handle_request)
print("Serving http on port 80000")
httpd.serve_forever()
上面两份代码实现的效果是一样的,调用wsgiref模块则明显节省了代码量,是整个程序更加简洁。
wsgiref模块封装了socket服务端的代码,只留下一个调用的接口,省去了程序员的麻烦,程序员可以将精力放在Web请求的逻辑处理中。
以上述的代码为例,详细看一下wsgiref模块的源码中一些关键的地方:
if __name__ == "__main__":
httpd = make_server("",8000,handle_request)
print("Serving http on port 80000")
httpd.serve_forever()
1、整个程序的入口为make_server()函数:
def make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler):
"""Create a new WSGI server listening on `host` and `port` for `app`"""
server = server_class((host, port), handler_class) #默认创建一个WSGIServer类
server.set_app(app) #将应用程序,即逻辑处理函数传给类
return server
2、make_server()函数默认生成一个WSGIServer类:
class WSGIServer(HTTPServer):
class HTTPServer(socketserver.TCPServer):
class TCPServer(BaseServer):
WSGIServer,HTTPServer两个类没有初始化函数,调用父类的初始化函数,TCPServer类的__init__()函数拓展了BaseServer
类的__init__()函数:
#BaseServer类的__init__()函数:
def __init__(self, server_address, RequestHandlerClass):
"""Constructor. May be extended, do not override."""
self.server_address = server_address
self.RequestHandlerClass = RequestHandlerClass
self.__is_shut_down = threading.Event()
self.__shutdown_request = False
#TCPServer类的__init__()函数:
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
"""Constructor. May be extended, do not override."""
BaseServer.__init__(self, server_address, RequestHandlerClass)
self.socket = socket.socket(self.address_family,self.socket_type)
if bind_and_activate:
try:
self.server_bind()
self.server_activate()
except:
self.server_close()
raise
TCPServer类的初始化函数还调用了server_bind(self),server_bind(self)两个函数:
def server_bind(self):
"""Called by constructor to bind the socket.May be overridden."""
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
def self.server_activate(self):
"""Called by constructor to activate the server.May be overridden."""
self.socket.listen(self.request_queue_size)
可以看到server.bind()函数调用了socket.bind()函数,而server_activate()调用了socket.listen()函数:
3、server.set_app(app),此处传入Web请求的处理逻辑:
def set_app(self,application):
self.application = application
4、httpd.serve_forever()函数调用BaseServer类的_handle_request_noblock()函数处理多路请求:
def _handle_request_noblock(self):
try:
request, client_address = self.get_request() #get_request()调用了socket.accept()函数
except OSError:
return
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
else:
self.shutdown_request(request)
def process_request(self, request, client_address):
self.finish_request(request, client_address)
self.shutdown_request(request)#shutdown_request()调用socket.close()关闭socket
def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self)
5、process_request()函数调用了finish_request()函数,简介调用了make_server函数的默认参数WSGIRequestHandler类:
class WSGIRequestHandler(BaseHTTPRequestHandler):
class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
class StreamRequestHandler(BaseRequestHandler):
#调用BaseRequestHandler类的初始化函数:
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
6、初始化函数调用之后调用WSGIRequestHandler类的handle()函数获取server的逻辑处理函数:
def handle(self):
"""Handle a single HTTP request"""
try:
handler = ServerHandler(self.rfile, stdout, self.get_stderr(), self.get_environ())
handler.request_handler = self # backpointer for logging
handler.run(self.server.get_app()) #此处调用server的逻辑处理函数
finally:
stdout.detach()
7、BaseHandler类的handler.run()函数执行逻辑处理:
def run(self, application):
try:
self.setup_environ()
self.result = application(self.environ, self.start_response)
self.finish_response()
except:
try:
self.handle_error()
except:
self.close()
raise # ...and let the actual server figure it out.
self.environ:一个包含所有HTTP请求信息的dict对象
self.start_response:一个发送HTTP响应的函数。
在application函数中,调用:
res("200 OK",[("Content-Type","text/html")])
这样就发送了HTTP响应的头信息
8、BaseHandler类的setup_environ()函数获取HTTP请求的头信息:
def setup_environ(self):
"""Set up the environment for one request"""
env = self.environ = self.os_environ.copy()
os_environ= read_environ()
read_environ()函数:
def read_environ():
"""Read environment, fixing HTTP variables"""
enc = sys.getfilesystemencoding()
esc = 'surrogateescape'
try:
''.encode('utf-8', esc)
except LookupError:
esc = 'replace'
environ = {}
# Take the basic environment from native-unicode os.environ. Attempt to
# fix up the variables that come from the HTTP request to compensate for
# the bytes->unicode decoding step that will already have taken place.
for k, v in os.environ.items():
if _needs_transcode(k):
# On win32, the os.environ is natively Unicode. Different servers
# decode the request bytes using different encodings.
if sys.platform == 'win32':
software = os.environ.get('SERVER_SOFTWARE', '').lower()
# On IIS, the HTTP request will be decoded as UTF-8 as long
# as the input is a valid UTF-8 sequence. Otherwise it is
# decoded using the system code page (mbcs), with no way to
# detect this has happened. Because UTF-8 is the more likely
# encoding, and mbcs is inherently unreliable (an mbcs string
# that happens to be valid UTF-8 will not be decoded as mbcs)
# always recreate the original bytes as UTF-8.
if software.startswith('microsoft-iis/'):
v = v.encode('utf-8').decode('iso-8859-1')
# Apache mod_cgi writes bytes-as-unicode (as if ISO-8859-1) direct
# to the Unicode environ. No modification needed.
elif software.startswith('apache/'):
pass
# Python 3's http.server.CGIHTTPRequestHandler decodes
# using the urllib.unquote default of UTF-8, amongst other
# issues.
elif (
software.startswith('simplehttp/')
and 'python/3' in software
):
v = v.encode('utf-8').decode('iso-8859-1')
# For other servers, guess that they have written bytes to
# the environ using stdio byte-oriented interfaces, ending up
# with the system code page.
else:
v = v.encode(enc, 'replace').decode('iso-8859-1')
# Recover bytes from unicode environ, using surrogate escapes
# where available (Python 3.1+).
else:
v = v.encode(enc, esc).decode('iso-8859-1')
environ[k] = v
return environ
9、BaseHandler类的start_response()函数:
def start_response(self, status, headers,exc_info=None):
"""'start_response()' callable as specified by PEP 3333"""
if exc_info:
try:
if self.headers_sent:
# Re-raise original exception if headers sent
raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
finally:
exc_info = None # avoid dangling circular ref
elif self.headers is not None:
raise AssertionError("Headers already set!")
self.status = status
self.headers = self.headers_class(headers)
status = self._convert_string_type(status, "Status")
assert len(status)>=4,"Status must be at least 4 characters"
assert status[:3].isdigit(), "Status message must begin w/3-digit code"
assert status[3]==" ", "Status message must have a space after code"
if __debug__:
for name, val in headers:
name = self._convert_string_type(name, "Header name")
val = self._convert_string_type(val, "Header value")
return self.write
来源:http://blog.csdn.net/Liu_Jack/article/details/53643245


猜你喜欢
- 本文实例为大家分享了python实现坦克大战的具体代码,供大家参考,具体内容如下本游戏制作代码量较大具体代码与图片声源可以在我的GitHub
- FLV在线转换,是目前主流播客网上通用的一种视频解决方案需要用到的组件 ASPExecmencoderffmpeg.exe第一步骤: 在线转
- 见下面的代码:<html><head><title>精彩春风之月份查询</title><
- Python 模块安装一. 打开命令提示符win + R 输入 cmd 点击确定或者win + S 搜索输入 cmd二. 环境变量没有问题的
- 一、random模块简介Python标准库中的random函数,可以生成随机浮点数、整数、字符串,甚至帮助你随机选择列表序列中的一个元素,打
- 目录一、变量、常量的区别二、变量1. Python中的变量不需要声明类型2. 用“=”号来给变量赋值3. 赋值4. 变量5. “=”6. P
- 本文为大家分享了mysql 5.7.17 winx64解压版安装配置方法,供大家参考,具体内容如下一、下载下载地址二、配置1. 解压下载的m
- 本文实例为大家分享了python定义带参数装饰器的具体代码,供大家参考,具体内容如下案例: &
- 在Navicat中,时间戳(timestamp)格式的数据表现为:查询某一天的数据SELECT * FROM my_tableWHERE `
- 前言Python一般使用Matplotlib制作统计图形,用它自己的说法是‘让简单的事情简单,让复杂的事情变得可能'。用它可以制作折
- 在命令行中使用 Python 时,它可以接收大约 20 个选项(option),语法格式如下:python [-bBdEhiIOqsSuvV
- MySQL全文索引一种特殊的索引,它会把某个数据表的某个数据列出现过的所有单词生成一份清单。alter table tablename ad
- 本文实例为大家分享了python实现五子棋游戏的具体代码,供大家参考,具体内容如下话不多说,直接上代码:全部工程文件,在GitHub:五子棋
- 集成 FCKEditor v2.6(当前为最新版本)的基本步骤如下:1. 下载FCKeditor 2.6 基本文件(Main Code)。将
- 话不多说上代码!源代码from tkinter import *import pyttsx3class Application(Frame)
- 用VBS语言实现的一个简单网页计算器,功能:可以进行加法、减法、乘法、除法、取反、开根号、及指数运算。虽然简单但是比起windows xp自
- tips: 路由绑定、菜单跳转、网页后退高亮显示1. 问题描述使用antd-vue 的 a-layout布局和a-menu菜单做一个侧边栏菜
- 1、卓越亚马逊的首页轮换图片,每刷新一次,都是随机不同的顺序显示,这样的设计解决了对于较多图片轮换而靠后的图片信息很少被看到的问题,这点对于
- 本文实例讲述了JS获得选取checkbox整行数据的方法。分享给大家供大家参考。具体实现方法如下:<html><head&
- 本文实例为大家分享了tkinter+pygame+spider实现音乐播放器,供大家参考,具体内容如下1.确定页面SongSheet&nbs