WSGI和WEBOB和ROUTES主要用于组合起来处理web发送过来的请求并将响应返回给web端,本篇主要来记录以下wsgi模块的作用。
什么是WSGI
在接收到HTTP的请求时,我们不需要关心是怎样接收到的,我们需要关心的是怎样处理传送来的请求,并将响应返回给web端。WSGI的作用就在这里,它主要应用于web server和web application之间。
Web Server和Web application都实现了WSGI规范,然后各司其职:
- web server:接收来自客户端的HTTP请求,然后将请求交给web application
- web application:根据请求来调用响应的处理逻辑,生成response,通过web server把response发送给客户端
这个web application必须是一个可调用对象(类,方法,函数或实现__call__方法的类实例),这个callable对象需要满足下面两个条件:
-
包含两个参数
- environ:是一个字典,里面包含了WSGI的基本信息和HTTP协议里的请求行,请求头,请求数据等信息
- start_response:是一个回调函数,web application通过这个函数将HTTP status code和headers发送给Web Server
-
一个返回值:返回值主要是response_body,最好放在list中,因为如果直接返回字符串,则会迭代字符串中的每一个字符,会降低效率。
查看environ
python中有一个WSGI server,我们可以直接使用
# WSGI server in Python
from wsgiref.simple_server import make_server
def application(environ, start_response):
# 遍历并排序environ中的键值对
response_body = ['%s: %s' % (key, value)
for key, value in sorted(environ.items())]
response_body = '\n'.join(response_body)
#定义响应行
status = '200 OK'
#定义响应头
response_headers = [('Content-Type', 'text/plain'),
('Content-Length', str(len(response_body)))]
#将响应行,响应头返回给web server
start_response(status, response_headers)
return [response_body.encode('utf-8')]#注意转换的字节流
# Instantiate the WSGI server.
# It will receive the request, pass it to the application
# and send the application's response to the client
httpd = make_server(
'localhost', # The host name.
8080, # A port number where to wait for the request.
application # Our application object name, in this case a function.
)
# Wait for a single request, serve it and quit.
#httpd.handle_request()
# Keep the server always alive with serve_forever()
httpd.serve_forever()
web端返回结果如下:
ALLUSERSPROFILE: C:\ProgramData
APPDATA: C:\Users\songlinfeng\AppData\Roaming
COMMONPROGRAMFILES: C:\Program Files\Common Files
COMMONPROGRAMFILES(X86): C:\Program Files (x86)\Common Files
COMMONPROGRAMW6432: C:\Program Files\Common Files
COMPUTERNAME: songlinfeng00
COMSPEC: C:\windows\system32\cmd.exe
CONTENT_LENGTH:
CONTENT_TYPE: text/plain
DRIVERDATA: C:\Windows\System32\Drivers\DriverData
FPS_BROWSER_APP_PROFILE_STRING: Internet Explorer
FPS_BROWSER_USER_PROFILE_STRING: Default
GATEWAY_INTERFACE: CGI/1.1
HOMEDRIVE: C:
HOMEPATH: \Users\songlinfeng
HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
HTTP_ACCEPT_ENCODING: gzip, deflate, br
HTTP_ACCEPT_LANGUAGE: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP_CACHE_CONTROL: max-age=0
HTTP_CONNECTION: keep-alive
HTTP_HOST: 127.0.0.1:8080
HTTP_SEC_CH_UA: " Not;A Brand";v="99", "Microsoft Edge";v="97", "Chromium";v="97"
HTTP_SEC_CH_UA_MOBILE: ?0
HTTP_SEC_CH_UA_PLATFORM: "Windows"
HTTP_SEC_FETCH_DEST: document
HTTP_SEC_FETCH_MODE: navigate
HTTP_SEC_FETCH_SITE: none
HTTP_SEC_FETCH_USER: ?1
HTTP_UPGRADE_INSECURE_REQUESTS: 1
HTTP_USER_AGENT: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/97.0.1072.62
IDEA_INITIAL_DIRECTORY: C:\Users\songlinfeng\Desktop
LOCALAPPDATA: C:\Users\songlinfeng\AppData\Local
LOGONSERVER: \\JTZZDNS01
NUMBER_OF_PROCESSORS: 8
ONEDRIVE: C:\Users\songlinfeng\OneDrive
OS: Windows_NT
PATH: D:\VMware Player\bin\;C:\windows\system32;C:\windows;C:\windows\System32\Wbem;C:\windows\System32\WindowsPowerShell\v1.0\;C:\windows\System32\OpenSSH\;D:\MinGW\bin;D:\Git\cmd;D:\python\Scripts\;D:\python\;C:\Users\songlinfeng\AppData\Local\Microsoft\WindowsApps;;D:\Microsoft VS Code\bin
PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PATH_INFO: /
PROCESSOR_ARCHITECTURE: AMD64
PROCESSOR_IDENTIFIER: Intel64 Family 6 Model 140 Stepping 1, GenuineIntel
PROCESSOR_LEVEL: 6
PROCESSOR_REVISION: 8c01
PROGRAMDATA: C:\ProgramData
PROGRAMFILES: C:\Program Files
PROGRAMFILES(X86): C:\Program Files (x86)
PROGRAMW6432: C:\Program Files
PSMODULEPATH: C:\Program Files\WindowsPowerShell\Modules;C:\windows\system32\WindowsPowerShell\v1.0\Modules
PUBLIC: C:\Users\Public
PYCHARM_HOSTED: 1
PYTHONIOENCODING: UTF-8
PYTHONPATH: D:\python鍩虹\pythonProject1
PYTHONUNBUFFERED: 1
QUERY_STRING:
REMOTE_ADDR: 127.0.0.1
REMOTE_HOST:
REQUEST_METHOD: GET
SCRIPT_NAME:
SERVER_NAME: songlinfeng00.home.langchao.com
SERVER_PORT: 8080
SERVER_PROTOCOL: HTTP/1.1
SERVER_SOFTWARE: WSGIServer/0.2
SESSIONNAME: Console
SYSTEMDRIVE: C:
SYSTEMROOT: C:\windows
TEMP: C:\Users\SONGLI~1\AppData\Local\Temp
TMP: C:\Users\SONGLI~1\AppData\Local\Temp
UATDATA: C:\Windows\CCM\UATData\D9F8C395-CAB8-491d-B8AC-179A1FE1BE77
USERDNSDOMAIN: HOME.LANGCHAO.COM
USERDOMAIN: HOME
USERDOMAIN_ROAMINGPROFILE: HOME
USERNAME: songlinfeng
USERPROFILE: C:\Users\songlinfeng
WINDIR: C:\windows
ZES_ENABLE_SYSMAN: 1
wsgi.errors: <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8'>
wsgi.file_wrapper: <class 'wsgiref.util.FileWrapper'>
wsgi.input: <_io.BufferedReader name=944>
wsgi.multiprocess: False
wsgi.multithread: False
wsgi.run_once: False
wsgi.url_scheme: http
wsgi.version: (1
处理GET请求
当我们执行一个如下的GET请求:
http://127.0.0.1:8080/?name=wilber&hobbies=software
QUERY_STRING(URL中”?“之后的部分)和REQUEST_METHOD这些信息会包含在environ参数中,可以使用cgi模块中的parse_qs将一个由QUERY_STRING生成的字典,方便我们取出请求的变量信息。同时,为了避免客户端的输入可能存在的脚本注入,可以使用cgi模块中的escape函数对输入进行一次过滤。
from wsgiref.simple_server import make_server
from cgi import parse_qs, escape
html = """
<html>
<body>
<form method="get" action="/">
<p>
Name: <input type="text" name="name">
</p>
<p>
Hobbies:
<input name="hobbies" type="checkbox" value="running"> running
<input name="hobbies" type="checkbox" value="swimming"> swimming
<input name="hobbies" type="checkbox" value="reading"> reading
</p>
<p>
<input type="submit" value="Submit">
</p>
</form>
<p>
Name: %s<br>
Hobbies: %s
</p>
</body>
</html>"""
def application(environ, start_response):
print
("QUERY_STRING: %s" % environ['QUERY_STRING'])
print
("REQUEST_METHOD: %s" % environ['REQUEST_METHOD'])
# Returns a dictionary containing lists as values.
d = parse_qs(environ['QUERY_STRING'])
# In this idiom you must issue a list containing a default value.
name = d.get('name', [''])[0] # Returns the first name value.
hobbies = d.get('hobbies', []) # Returns a list of hobbies.
# Always escape user input to avoid script injection
name = escape(name)
hobbies = [escape(hobby) for hobby in hobbies]
response_body = html % (name or 'Empty',
', '.join(hobbies or ['No Hobbies']))
status = '200 OK'
# Now content type is text/html
response_headers = [('Content-Type', 'text/html'),
('Content-Length', str(len(response_body)))]
start_response(status, response_headers)
return [response_body.encode('utf-8')]
httpd = make_server('localhost', 8080, application)
# Now it is serve_forever() in instead of handle_request().
# In Windows you can kill it in the Task Manager (python.exe).
# In Linux a Ctrl-C will do it.
httpd.serve_forever()
处理POST请求
POST请求的query string 不会出现在URL里面,而是包含在request body中。对于environ,request body 放在(environ[‘wsgi.input’]),environ[‘wsgi.input’]对应的是一个file object,可以通过读取文件的方式读取request body.同时,environ.get(‘CONTENT_LENGTH’,0)中存放着request body的size,我们可以根据这个值来读取适当长度的request body.
def application(environ, start_response):
# the environment variable CONTENT_LENGTH may be empty or missing
try:
request_body_size = int(environ.get('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
# When the method is POST the query string will be sent
# in the HTTP request body which is passed by the WSGI server
# in the file like wsgi.input environment variable.
request_body = environ['wsgi.input'].read(request_body_size)
d = parse_qs(request_body)
print "wsgi.input %s" %environ['wsgi.input']
print "request_body_size %s" %environ.get('CONTENT_LENGTH', 0)
print "request_body %s" %request_body
name = d.get('name', [''])[0] # Returns the first name value.
hobbies = d.get('hobbies', []) # Returns a list of hobbies.
# Always escape user input to avoid script injection
name = escape(name)
hobbies = [escape(hobby) for hobby in hobbies]
response_body = html % (name or 'Empty',
', '.join(hobbies or ['No Hobbies']))
status = '200 OK'
response_headers = [('Content-Type', 'text/html'),
('Content-Length', str(len(response_body)))]
start_response(status, response_headers)
return [response_body]
参考
https://www.cnblogs.com/wilber2013/p/4763067.html