Web应用框架
Web应用框架(Web application framework)是一种开发框架,用来支持动态网站、网络应用程序及网络服务的开发。类型可以分为基于请求(request-based)的和基于组件(component-based)的两种Web框架。(--来源:百度词条 )
应用:有助于减轻网页开发时共通性活动的工作负荷,例如很多框架提供数据库访问接口、标准样板以及会话管理等,可提升代码的可再用性。(--来源:百度词条 )
Web应用本质上就是一个socket服务端,用户的浏览器是一个socket客户端,基于此,可自定义一个简易版Web框架
'''
自定义的简易版Web框架
'''
import socket
from socket import SOL_SOCKET
from socket import SO_REUSEADDR
# 获取socket对象server
server = socket.socket()
# 允许该端口可以多次运行
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 设置服务端的IP和端口
server.bind((
'127.0.0.1',
9527
))
# 设置半连接池
server.listen(5)
while True:
# 等待客户端访问,当前是阻塞状态,直到有客户端访问
conn, addr = server.accept()
# 接收客户端发送的数据
data = conn.recv(1024)
# 打印客户端发送的请求数据
print(data)
# 向客户端发送响应的数据
conn.send(b'hello world')
# 关闭连接
conn.close()
自定义的web框架服务端已完成,通过浏览器访问 127.0.0.1:9527
,会发现这个页面根本不能正常工作。
我们都知道B/S架构基于浏览器访问服务端的时候,需要遵循HTTP协议,因为HTTP协议规定了客户端和服务端之间的通信格式,那么用户在浏览器输入 https:\\127.0.0.1:9527
,服务端接收到的请求数据什么
在此之前,首先学习下HTTP协议
了解了HTTP协议后,如果想要server端给予响应,必须让server端在给客户端回复消息的时候按照HTTP协议的规则加上响应状态行
while True:
# 等待客户端访问,当前是阻塞状态,直到有客户端访问
conn, addr = server.accept()
# 接收客户端发送的数据
data = conn.recv(1024)
# 在向客户端发送响应数据前,必须加上响应状态行
# HTTP/1.1:协议版本号 200 OK:状态码,200表示客户端请求成功 \r\n\r\n:空行
conn.send(b'HTTP/1.1 200 OK \r\n\r\n')
# 向客户端发送响应的数据,即响应体
conn.send(b'hello world')
# 关闭连接
conn.close()
加上响应状态行后,重新访问 https:\\127.0.0.1:9527
,则浏览器可以收到服务端返回的 hello world
- 第一次优化
如果想要Web服务根据用户请求的URL不同,返回不同的内容,实现思路:首先得拿到这个URL,然后根据URL判断,进而返回不同的内容给浏览器
while True:
# 等待客户端访问,当前是阻塞状态,直到有客户端访问
conn, addr = server.accept()
# 接收客户端发送的数据
data = conn.recv(1024)
print(data)
# 在向客户端发送响应数据前,必须加上响应状态行
conn.send(b'HTTP/1.1 200 OK \r\n\r\n')
path_info = data.decode('utf-8').split('\r\n')[0].split()[1]
print(path_info)
# 根据不同的路径向客户端发送响应的数据
if path_info == '/index':
conn.send(b'from index')
elif path_info == '/login':
conn.send(b'from login')
else:
conn.send(b'404 error')
# 关闭连接
conn.close()
函数版
# 访问index页面调用的函数
def index(url):
return f'from [{url}]file'
# 访问login页面调用的函数
def login(url):
return f'from [{url}]file'
# 创建访问页面路径和函数的对应关系
url_list = [
('/index/', index),
('/login/', login)
]
while True:
# 等待客户端访问,当前是阻塞状态,直到有客户端访问
conn, addr = server.accept()
# 接收客户端发送的数据
data = conn.recv(1024)
# 在向客户端发送响应数据前,必须加上响应状态行
conn.send(b'HTTP/1.1 200 OK \r\n\r\n')
path_info = data.decode('utf-8').split('\r\n')[0].split()[1]
# 根据不同的路径向客户端发送响应的数据
# if path_info == '/index':
# conn.send(b'from index')
# elif path_info == '/login':
# conn.send(b'from login')
# else:
# conn.send(b'404 error')
# 函数版
func = None
# 遍历访问页面路径是否在列表中
for line in url_list:
if line[0] == path_info:
func = line[1]
# 请求页面路径不在列表中,返回404响应
if not func:
res = '404 error'
else:
res = func(path_info)
conn.send(res.encode('utf-8'))
# 关闭连接
conn.close()
- 第二次优化
如果想要根据用户请求的URL不同,返回不同的HTML页面
# 访问index页面调用的函数
def index():
# 读取index.html页面中的内容
with open(r'E:\Oldboy\python3\200103自定义Web框架\templates\index.html',
'r', encoding='utf-8') as f:
data = f.read()
# return f'from [{url}]file'
return data
# 访问login页面调用的函数
def login():
with open(r'E:\Oldboy\python3\200103自定义Web框架\templates\login.html',
'r', encoding='utf-8') as f:
data = f.read()
# return f'from [{url}]file'
return data
# 创建访问页面路径和函数的对应关系
url_list = [
('/index/', index),
('/login/', login)
]
while True:
# 等待客户端访问,当前是阻塞状态,直到有客户端访问
conn, addr = server.accept()
# 接收客户端发送的数据
data = conn.recv(1024)
# 在向客户端发送响应数据前,必须加上响应状态行
conn.send(b'HTTP/1.1 200 OK \r\n\r\n')
path_info = data.decode('utf-8').split('\r\n')[0].split()[1]
# 根据不同的路径向客户端发送响应的数据
# if path_info == '/index':
# conn.send(b'from index')
# elif path_info == '/login':
# conn.send(b'from login')
# else:
# conn.send(b'404 error')
# 函数版
func = None
# 遍历访问页面路径是否在列表中
for line in url_list:
if line[0] == path_info:
func = line[1]
# 请求页面路径不在列表中,返回404响应
if not func:
res = '404 error'
else:
res = func()
conn.send(res.encode('utf-8'))
# 关闭连接
conn.close()
- 第三次优化,不再自己手动创建套接字服务端,使用wsgiref模块
from wsgiref.simple_server import make_server
import urls
import views
def run(env, response):
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 浏览器能够接受的内容
"""
response('200 OK', [])
# print(env) # 是一个字典,其中PATH_INFO这个键值对存储的就是用户请求的url
target_url = env.get('PATH_INFO')
# 定义变量,存储可能匹配的函数,不直接调用是因为有可能请求的路径不存在
func = None
for line in urls.url_list:
if line[0] == target_url:
# 符合条件,说明后端已设置对应的页面数据,赋给func
func = line[1]
# 找到后,跳出循环,没必要继续查找
break
# 最后都没有找到,说明请求无效
if not func:
# 调用404页面
res = views.error(env)
else:
res = func(env)
# 将响应页面的数据返回给浏览器,展示给用户
return [res.encode('utf-8')]
if __name__ == '__main__':
# 监听host:port,一旦有客户端(浏览器)访问,立即执行第三个参数(可以是类)
server = make_server('127.0.0.1', 9527, run)
# 启动服务端
server.serve_forever()
了解一下
参照:
https://www.leiue.com/what-is-wsgi
https://blog.csdn.net/laughing2333/article/details/51288660
https://cizixs.com/2014/11/09/dive-into-wsgiref/
WSGI和 wsgiref
WSGI(Web Server Gateway Interface) Web服务器网关接口,是专门为Python语言定义的web服务器与Web应用程序或框架之间的一种简单而通用的接口
wsgiref 是一个实现了WSGI标准的范例实现(用于演示的简单python内置库),里面的功能包含了:1.操作wsgi 的环境变量;2.应答头部的处理;3.实现简单的HTTP server;4.简单的程序端和服务端校验函数
- 第四次优化 基于jinja2模块,实现动态网页
# 导入jinja2模块,使用模板语法
from jinja2 import Template
import pymysql
# 使用jinja2 中的模板语法实现动态网页
def userinfo(env):
# 调用数据库中的记录
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='Ad123',
database='django_test',
charset='utf8',
autocommit=True
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
sql = 'select * from userinfo'
cursor.execute(sql)
# 返回结果集:[{},{},{}]
data = cursor.fetchall()
# 该函数返回一个html页面
with open(r'E:\Oldboy\python3\200103自定义Web框架\templates\userinfo.html',
'r', encoding='utf-8') as f:
res = f.read()
# 利用jinja2
tmp = Template(res)
# 利用对象的render 方法,将从数据库的结果集传给html页面
res = tmp.render(xxx=data)
return res
序号
username
password
{%for user_dict in xxx %}
{{ user_dict.id }}
{{ user_dict.username }}
{{ user_dict.password }}
{% endfor %}
*
了解一下
参照: https://baike.baidu.com/item/jinja2/8911090?fr=aladdin
http://docs.jinkan.org/docs/jinja2/
https://www.jianshu.com/p/f04dae701361
jinja2:基于python的模板引擎,其设计思想来源于django的模板引擎,并扩展了其语法和一系列强大的功能。其中最显著的一个是增加了沙箱执行功能和可选的自动转义功能。
特点:
- 沙箱中执行
- 强大的HTML自动转义系统,可以有效地组织跨站脚本攻击(XSS,利用网站漏铜从用户那里恶意盗取信息)
- 模板继承机制,此机制可以使得所有的模板都具有相似一致的布局, 方便了开发人员对模板的修改和管理
- 高效的执行效率,Jinja2引擎在模板第一次加载时就把源码转换成Python字节码,加快模板执行时间。
- 可选的预编译模式
- 易于调试。异常的行数直接指向模板中的对应行
- 可配置的语法
语法
- 控制结构 {% %}
- 变量取值 {{ }}
jinja2模板中使用{{ }} 语法表示一个变量,是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有数据类型,如列表、字段、对象等
- 注释 {# #}