Web框架
Web应用框架(Web application framework)是一种开发框架,用来支持动态网站、网络应用程序及网络服务的开发。Web应用框架有助于减轻网页开发时共通性活动的工作负荷,例如许多框架提供数据库访问接口、标准样板以及会话管理等,可提升代码的可再用性。
Python web框架比较
下面对常见的三种Python web框架进行简单的介绍:
Django
Django已经成为Python最广泛部署的用于创建Web应用程序的框架之一。 Django配备了可能需要的大部分组件,因此它倾向于构建大型应用程序而不是小型应用程序。
Django的socket用的是wsgiref模块,路由与视图函数、模板渲染都是自己的写的。
Flask
关于Python中的Web框架的大多数讨论都是从Flask开始提到的。 Flask是一个成熟的,易于理解的框架,广泛使用且非常稳定,短小精悍,自带的功能模块特别少,大部分都是依赖于第三方模块。
Flask的socket用的是wsgiref模块,路由与视图函数是自己的写的,模板渲染用的是第三方模块jinjia2。
Tornado
Tornado是针对特定用例的另一个小框架,天生异步,性能强悍。Tornado专为构建异步网络应用程序而设计,非常适合创建同时打开大量网络连接并使其保持活动状态的服务,即涉及WebSockets或长轮询的任何内容。
socket、路由与视图函数、模板渲染都是自己的。
Web框架本质
Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以按照HTTP协议来实现一个简单版的Web框架了。
import socket
server = socket.socket() # 不传参数默认就是TCP协议
server.bind(('127.0.0.1',8080)) # 绑定IP和端口
server.listen(5)
while True:
conn, addr = server.accept() # 阻塞 等待客户端链接
data = conn.recv(1024)
# 因为要遵循HTTP协议,所以回复的消息也要加状态行
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
# 手动处理http数据获取用户访问的路径,url是我们从浏览器发过来的消息中分离出的访问路径
current_path = data.decode('utf-8').split('\r\n')[0].split(' ')[1]
if current_path == '/index':
# 路由匹配上之后返回index
# conn.send(b'<h1>index</h1>')
with open('index.html','rb') as f:
# TCP对于发送间隔时间较短和较少的内容会一次发送过去
conn.send(f.read())
else:
# 当匹配不上的时候统一返回404
conn.send(b'404 not found!')
conn.close()
这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。
import socket
import time
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
sk.listen(5) # 监听
# 将返回不同的内容部分封装成函数
def index(url):
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
now = time.strftime('%Y-%m-%d %X')
res = s.replace("@@time@@", now) # 在网页中定义好特殊符号,用动态的数据去替换提前定义好的特殊符号
return res
def home(url):
with open("home.html", "r", encoding="utf8") as f:
res = f.read()
return res
# 定义一个url和实际要执行的函数的对应关系
list1 = [
("/index", index),
("/home", home),
]
while True:
# 等待连接
conn, add = sk.accept()
data = conn.recv(8096) # 接收客户端发来的消息
# 从data中取到路径
data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
# 按\r\n分割
data1 = data.split("\r\n")[0]
url = data1.split(" ")[1] # url是我们从浏览器发过来的消息中分离出的访问路径
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
# 根据不同的路径返回不同内容
func = None # 定义一个保存将要执行的函数名的变量
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = "404 not found!"
# 返回具体的响应消息
conn.send(response.encode('utf-8'))
conn.close()
WSGI
PythonWeb服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是Python应用程序或框架和Web服务器之间的一种接口,已经被广泛接受, 它已基本达成它的可移植性方面的目标。WSGI 没有官方的实现, 因为WSGI更像一个协议. 只要遵照这些协议,WSGI应用(Application)都可以在任何服务器(Server)上运行。
wsgiref模块
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,都由自己来实现很浪费时间和精力。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块。
我们利用wsgiref模块来替换我们自己写的web框架的socket server部分:
from wsgiref.simple_server import make_server
import time
def index(env):
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
now = time.strftime('%Y-%m-%d %X')
res = s.replace("@@time@@", now) # 在网页中定义好特殊符号,用动态的数据去替换提前定义好的特殊符号
return res
def home(env):
with open("home.html", "r", encoding="utf8") as f:
res = f.read()
return res
def error(env):
return '404 error'
urls = [
('/index',index),
('/home',home),
]
def run(env,response):
'''
:param env:请求相关的信息
:param response:响应相关的信息
:return:
'''
response('200 OK',[('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
# env是一个大字典 里面装了一堆处理好了的键值对数据
url = env.get('PATH_INFO') # 取到用户输入的url
func = None
for url_map in urls:
if url == url_map[0]:
func = url_map[1]
break
if func:
res = func(env)
else:
res = error(env)
return [res.encode('utf-8')]
if __name__ == '__main__':
server = make_server('127.0.0.1',8080,run)
server.serve_forever()
jinja2
上面的代码实现了一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。其实模板渲染有个现成的工具: jinja2
jinja2是Flask作者开发的一个模板系统,起初是仿django模板的一个模板引擎,为Flask提供模板支持,由于其灵活,快速和安全等优点被广泛使用。jinja2模块中有一个名为Enviroment的类,这个类的实例用于存储配置和全局对象,然后从文件系统或其他位置中加载模板。
jinja2之所以被广泛使用是因为它具有以下优点:
-
- 相对于Template,jinja2更加灵活,它提供了控制结构,表达式和继承等。
- 相对于Mako,jinja2仅有控制结构,不允许在模板中编写太多的业务逻辑。
- 相对于Django模板,jinja2性能更好。
- Jinja2模板的可读性很棒。
安装
由于jinja2属于第三方模块,首先需要对其进行安装
pip3 install jinja2
使用jinja2渲染html文件:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table table-hover table-striped table-bordered">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>password</th>
</tr>
</thead>
<tbody>
{% for user in user_dict %} <!--[{},{},{},{}]-->
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.password }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
py代码
from wsgiref.simple_server import make_server
from jinja2 import Template
import pymysql
def index(env):
# 连接数据库 获取数据 渲染到前端页面
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
password = 'mysql',
database = 'jinja',
charset = 'utf8',
autocommit = True
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.execute('select * from userifo')
user_dict= cursor.fetchall() # [{},{},{},{}]
with open(r'templates/index.html','r',encoding='utf-8') as f:
data = f.read()
tmp = Template(data)
return tmp.render(user_dict=user_dict)
def error(env):
return '404 error'
urls = [
('/index',index),
]
def run(env,response):
'''
:param env:请求相关的信息
:param response:响应相关的信息
:return:
'''
response('200 OK',[('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
# env是一个大字典 里面装了一堆处理好了的键值对数据
url = env.get('PATH_INFO') # 取到用户输入的url
func = None
for url_map in urls:
if url == url_map[0]:
func = url_map[1]
break
if func:
res = func(env)
else:
res = error(env)
return [res.encode('utf-8')]
if __name__ == '__main__':
server = make_server('127.0.0.1',8080,run)
server.serve_forever()