<返回更多

一文秒懂Web框架基础之WSGI协议

2020-11-26    
加入收藏

本篇文章我们主要介绍WSGI协议,该协议用来描述Server与Framework之间的通信接口,我们日常使用的Python WEB框架Django、Flask、web.py等都遵循了该协议。下面我们就来详细了解一下该协议的实现吧!

01 简介

WSGI协议全称Web Server Gateway Interface(Web服务器网关接口)。这是Python中定义的一个网关协议,规定了Web Server如何跟应用程序交互。该协议的主要目的就是在Python中所有Web Server程序或者网关程序,能够通过统一的协议跟Web框架或者Web应用进行交互。如果没有这个协议,那每个程序都要各自实现各自的交互接口,而不能够互相兼容,重复造轮子。使用统一的协议,Web应用框架只要实现WSGI协议规范就可以与外部进行交互,不用针对某个Web Server独立开发交互逻辑。

02 Web Server实现

在了解WSGI协议之前,我们先通过socket实现一个Web服务器。通过监听本地端口来接客户端的web请求,然后进行响应,具体如下:

1. #!/usr/bin/env/ python  
2. # -*- coding:utf-8 -*-  
3.   
4. import socket  
5.   
6. END_TAG_F = b'nn'  
7. END_TAG_S = b'nrn'  
8.   
9. # 设置web server响应内容  
10. html_content = '<html><h1>My Frist Web Page<h1></html>'  
11.   
12. # 设置响应headers  
13. resp_args = ['HTTP/1.0 200 OK', 'Date: Sun, 22 nov 2020 19:00:00 GMT',  
14.              'Content-Type: text/html;charset=utf-8',  
15.              'Content-Length: {}rn'.format(len(html_content)), html_content]  
16.   
17. _resp = "rn".join(resp_args)  
18.   
19.   
20. def connet_operate(conn, addr):  
21.     """ 
22.     请求操作 
23.     :param conn:  
24.     :param addr:  
25.     :return:  
26.     """  
27.     request = b''  
28.     while END_TAG_F not in request and END_TAG_S not in request:  
29.         request += conn.recv(1024)  
30.   
31.     print("请求内容: ", request)  
32.     conn.send(_resp.encode())  
33.     conn.close()  
34.   
35.   
36. def web_server():  
37.     # socket.AF_INET用于服务器与服务器之间同行   
38.     # socket.SOCK_STREAM用于基于TCP流的通信  
39.     server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
40.       
41.     # 监听本地8888端口  
42.     server.bind(('127.0.0.1', 8888))  
43.     server.listen()  
44.     print("web server已经启动")  
45.   
46.     try:  
47.         while True:  
48.             conn, address = server.accept()  
49.             connet_operate(conn, address)  
50.     except:  
51.         server.close()  
52.   
53.   
54. if __name__ == "__main__":  
55.     web_server()  

下面我们启动server服务器,查看页面能不能正常访问,同时看看

一文秒懂Web框架基础之WSGI协议

 


一文秒懂Web框架基础之WSGI协议

 

上面代码就是最基本的web服务模型了,通过socket与HTTP协议提供Web服务,但上面的web服务是单线程的,只有前一个请求处理结束才处理第二个请求,我们该造一下上面的代码,通过python threading模块实现多线程的web服务器,具体操作如下:

1. #!/usr/bin/env/ python  
2. # -*- coding:utf-8 -*-  
3.   
4. import traceback  
5. import socket  
6. import errno  
7. import threading  
8.   
9. END_TAG_F = b'nn'  
10. END_TAG_S = b'nrn'  
11.   
12. # 设置web server响应内容  
13. html_content = '<html><h1>这是线程({})的页面 <h1></html>'  
14.   
15. # 设置响应headers  
16. resp_args = ['HTTP/1.0 200 OK', 'Date: Sun, 22 nov 2020 19:00:00 GMT',  
17.              'Content-Type: text/html;charset=utf-8',  
18.              'Content-Length: {}rn']  
19.   
20.   
21. def connet_operate(conn, addr):  
22.     """ 
23.     请求操作 
24.     :param conn: 
25.     :param addr: 
26.     :return: 
27.     """  
28.     request = b''  
29.     while END_TAG_F not in request and END_TAG_S not in request:  
30.         request += conn.recv(1024)  
31.   
32.     print("请求内容: ", request)  
33.     c = threading.current_thread()  
34.     _ = html_content.format(c.name)  
35.     resp_args.Append(_)  
36.     content_length = len(_.encode())  
37.     _resp = "rn".join(resp_args)  
38.   
39.     _resp = _resp.format(content_length)  
40.     conn.send(_resp.encode())  
41.     conn.close()  
42.   
43.   
44. def web_server():  
45.     # socket.AF_INET用于服务器与服务器之间同行  
46.     # socket.SOCK_STREAM用于基于TCP流的通信  
47.     server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
48.     server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  
49.     # 监听本地8888端口  
50.     server.bind(('127.0.0.1', 8888))  
51.     server.listen()  
52.     print("web server已经启动")  
53.   
54.     try:  
55.         n = 0  
56.         while True:  
57.             try:  
58.                 conn, address = server.accept()  
59.             except socket.error as e:  
60.                 if e.args[0] != errno.EAGAIN:  
61.                     raise Exception(e)  
62.                 continue  
63.   
64.             n += 1  
65.             # 通过threading实现web server多线程  
66.             t = threading.Thread(target=connet_operate, args=(conn, address), name='thread{}'.format(n))  
67.             t.start()  
68.     except Exception as e:  
69.         print(traceback.format_exc(e))  
70.         server.close()  
71.   
72. if __name__ == "__main__":  
73.     web_server()  

我们再访问该服务,其返回如下:

一文秒懂Web框架基础之WSGI协议

 

通过上述改造我们就实现了多线程的web服务器,了解了web服务的基本实现,下面我们就来看看WSGI的具体实现。

03 WSGI Application实现

在了解了基本的web服务的实现,我们看WSGI协议,WSGI协议分为两部分,一部分是web server或者网关就是上面web server代码一样,它监听在某个端口上接受外部的请求,另外一部分就是web应用,web server将接受到的请求数据通过WSGI协议规定的方式把数据传递给web应用,web应用处理完数据后设置对应的状态码与header然后返回,web server拿到返回数据之后再进行HTTP协议封装然后返回给客户端,下面我们看看WSGI协议通过代码的具体实现

1. #!/usr/bin/env/ python  
2. # -*- coding:utf-8 -*-  
3.   
4. import os  
5. import sys  
6.   
7.   
8. def _app(environ, response):  
9.     status = "200 OK"  
10.     resp_hearders = [('Content-Type', 'text/html')]  
11.     response(status, resp_hearders)  
12.     return [b'<h1>simple wsgi app</h1>n']  
13.   
14. def _to_bytes(content):  
15.     return content.encode()  
16.   
17. def run_with_cgi(application):  
18.     environ = dict(os.environ.items())  
19.     environ['wsgi.input'] = sys.stdin.buffer  
20.     environ['wsgi.errors'] = sys.stderr  
21.     environ['wsgi.version'] = (1, 0)  
22.     environ['wsgi.multithread'] = False  
23.     environ['wsgi.multiprocess'] = True  
24.     environ['wsgi.run_once'] = True  
25.   
26.     if environ.get('HTTPS', 'off') in ('on', '1'):  
27.         environ['wsgi.url_scheme'] = 'https'  
28.     else:  
29.         environ['wsgi.url_scheme'] = 'http'  
30.   
31.     headers_set = []  
32.     headers_sent = []  
33.   
34.     def write(data):  
35.         out = sys.stdout.buffer  
36.   
37.         if not headers_set:  
38.             raise ValueError("write before response()")  
39.   
40.         elif not headers_sent:  
41.             # 输出数据前, 先发送响应头  
42.             status, response_headers = headers_sent[:] = headers_set  
43.             out.write(_to_bytes('Status: {}rn'.format(status)))  
44.             for header in response_headers:  
45.                 out.write(_to_bytes('{}: {}rn'.format(header, header)))  
46.             out.write(_to_bytes('rn'))  
47.   
48.         out.write(data)  
49.         out.flush()  
50.   
51.     def response(status, response_headers, error_info=None):  
52.         if error_info:  
53.             try:  
54.                 if headers_sent:  
55.                     # 已经发送header就抛出异常  
56.                     raise (error_info[0], error_info[1], error_info[2])  
57.   
58.             finally:  
59.                 error_info = None  
60.   
61.         elif headers_set:  
62.             raise ValueError("Headers already set")  
63.   
64.         headers_set[:] = [status, response_headers]  
65.         return write  
66.   
67.     result = application(environ, response)  
68.   
69.     try:  
70.         for data in result:  
71.             # 没有body数据则不发送header  
72.             if data:  
73.                 write(data)  
74.         if not headers_sent:  
75.             write('')  
76.   
77.     finally:  
78.         if hasattr(result, 'close'):  
79.             result.clost()  
80.   
81. if __name__ == "__main__":  
82.     run_with_cgi(_app)  

现在我们运行编写的WSGI应用,具体如下:

一文秒懂Web框架基础之WSGI协议

 

通过执行该应用直接返回了状态信息、Header及body内容。上述代码就是Application在WSGI协议的实现。我们要实现Application只需要能够接收一个环境变量以及一个回调函数即可,如上面代码的“result = application(environ, response)”,在处理完请求通过回调函数respose来设置响应的状态和header,最后再返回body。在完成Application之后可以通过一些Web Server来调用,如Gunicorn Web server,限于篇幅限制就不详细讲解了,刚兴趣的朋友可以安装Gunicorn然后进行调用。

04 总结

至此我们WSGI协议就讲完了,如有什么问题欢迎在文章后面进行留言,最后如果喜欢本篇文章不要忘了点赞、关注与转发哦!

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>