2020-06-30 22:37阅读: 190评论: 0推荐: 0

Web框架的本质

1. Web请求和响应的过程

简单来说,服务端接收到用户访问网站的请求时,无非就是将用户发来的请求信息进行分析(请求头+请求体)。

再根据用户请求信息中内容在服务端做对应的处理后,将处理后的结果(字符串)作为响应体,再加上响应头后构建成完整的响应报文返回给客户端。

如我们可以根据请求头中的URL来加载对应的文件进行响应,而这个加载对应文件的过程我们可以做成一个函数,从而可以根据请求头中URL信息来执行不同的函数进行处理。

而对于处理的过程,我们可以是直接加载一个html文件后,将整个html文件中的内容作为一个字符串加入响应体中后构建响应报文,再直接发送给客户端。这就是静态资源的请求过程。

而对于变化的数据(动态资源),我们还需要从数据库中拿到对应的数据后,将这些数据填充进html文件中的指定位置,再将填充后的内容作为一个字符串附着进响应体中,再构建响应报文发送给客户端。

这时,不难发现,现在的这个html文件,已然变成了一个模板,我们可以在这个模板上定义特殊的替换规则后,就可以填充我们想要添加的任何内容。这样就可以实现内容的动态显示。

2. 一个简单的静态Web服务器

根据以上基本原理,我们不难写出一个根据请求的URL来响应对应html网页内容的简单的静态资源web服务器。

import socket
def f1(request):
# request中包含了用户请求的所有内容:请求头+请求体
f = open('index.html', 'rb')
data = f.read()
f.close()
return data
def f2(request):
f = open('article.html', 'rb')
data = f.read()
f.close()
return data
routers = [
('/f1', f1),
('/f2', f2),
]
def run():
sock = socket.socket()
sock.bind(('127.0.0.1', 8080))
sock.listen(5)
while True:
# 等待客户端来连接
conn, addr = sock.accept()
# 获取用户发送的数据,收到的数据是Bytes,(二进制格式)
data = conn.recv(8096)
"""对用户发来的信息进行提取"""
# 将内容转化成字符串类型
data = str(data, encoding='utf-8')
# data = bytes('shit', encoding='utf-8')
# 将内容分割成请求头和请求体
headers, bodys = data.split('\r\n\r\n')
# 得到请求头中的每一行数据
temp_list = headers.split('\r\n')
# 对请求头中的第一行数据进行提取,取得请求方法、请求URL、请求方法
method, url, protocol = temp_list[0].split(' ')
"""根据用户请求的URL来返回不同的内容"""
"""思路:
遍历routers中的每个元组的第一个值和请求头中的url进行比较,
若匹配成功,则将对应的函数名赋值给func_name,进而去执行该URL对应的函数
"""
func_name = None
for item in routers:
if item[0] == url:
func_name = item[1]
break
if func_name:
response = func_name(data) # 把data中的内容传进去
else:
response = b"404 not found"
conn.send(response)
conn.close()
if __name__ == '__main__':
run()

3. 动态网站Web服务器

动态web无非就是数据从数据库来提取,所以数据是会变化的,而非像静态页面那样一成不变。

import socket
import pymysql
# 静态页面
def index(request):
# request中包含了用户请求的所有内容:请求头+请求体
f = open('index.html', 'rb')
data = f.read()
f.close()
return data
# 静态页面
def article(request):
f = open('article.html', 'rb')
data = f.read()
f.close()
return data
# 动态页面
def user_info(request):
# 连接数据库并取得数据
mysql_conn = pymysql.connect(host='10.0.0.204', port=3306, user='hgzero', passwd='woshiniba', db='my_test')
conn_cursor = mysql_conn.cursor(cursor=pymysql.cursors.DictCursor)
ret = conn_cursor.execute("select name,age,gender from first_test")
print("受影响的行数:%s" % ret)
user_list = conn_cursor.fetchall()
conn_cursor.close()
mysql_conn.close()
print("打印从数据库中拿到的内容: %s" % user_list)
# 将从数据库中拿到的内容进行拼接
content_list = []
for row in user_list:
tp = "<tr><th>%s</th><th>%s</th><th>%s</th></tr>" % (row["name"], row["age"], row["gender"])
content_list.append(tp)
user_content = "".join(content_list)
# 将html文件中指定位置的内容替换成数据库中取得后拼接好的内容
f = open("user_list.html", "r", encoding='utf-8')
html_template = f.read()
f.close()
# 替换为指定内容
# 这里其实就是最简单的所谓的模板的渲染
data = html_template.replace("@@content@@", user_content)
return bytes(data, encoding="utf-8")
routers = [
('/index.html', index),
('/article.html', article),
('/user_info.html', user_info),
]
def run():
sock = socket.socket()
sock.bind(('127.0.0.1', 8080))
sock.listen(5)
while True:
# 等待客户端来连接
conn, addr = sock.accept()
# 获取用户发送的数据,收到的数据是Bytes,(二进制格式)
data = conn.recv(8096)
"""对用户发来的信息进行提取"""
# 将内容转化成字符串类型
data = str(data, encoding='utf-8')
# data = bytes('shit', encoding='utf-8')
# 将内容分割成请求头和请求体
headers, bodys = data.split('\r\n\r\n')
# 得到请求头中的每一行数据
temp_list = headers.split('\r\n')
# 对请求头中的第一行数据进行提取,取得请求方法、请求URL、请求方法
method, url, protocol = temp_list[0].split(' ')
"""根据用户请求的URL来返回不同的内容"""
"""思路:
遍历routers中的每个元组的第一个值和请求头中的url进行比较,
若匹配成功,则将对应的函数名赋值给func_name,进而去执行该URL对应的函数
"""
func_name = None
for item in routers:
if item[0] == url:
func_name = item[1]
break
if func_name:
response = func_name(data) # 把data中的内容传进去
else:
response = b"404 not found"
conn.send(response)
conn.close()
if __name__ == '__main__':
run()

4. 用Jinja2进行模板渲染

这时不难发现,如果我们手动的对html模板进行内容替换,这将会非常麻烦,然而已经有这样的模板渲染工具了。我们可以按照Jinja2规定要的语法来对我们的html模板进行渲染。

import socket
import pymysql
# 静态页面
def index(request):
# request中包含了用户请求的所有内容:请求头+请求体
f = open('index.html', 'rb')
data = f.read()
f.close()
return data
# 静态页面
def article(request):
f = open('article.html', 'rb')
data = f.read()
f.close()
return data
# 动态页面,手动渲染
def user_info(request):
# 连接数据库并取得数据
mysql_conn = pymysql.connect(host='10.0.0.204', port=3306, user='hgzero', passwd='woshiniba', db='my_test')
conn_cursor = mysql_conn.cursor(cursor=pymysql.cursors.DictCursor)
ret = conn_cursor.execute("select name,age,gender from first_test")
print("受影响的行数:%s" % ret)
user_list = conn_cursor.fetchall()
conn_cursor.close()
mysql_conn.close()
print("打印从数据库中拿到的内容: %s" % user_list)
# 将从数据库中拿到的内容进行拼接
content_list = []
for row in user_list:
tp = "<tr><th>%s</th><th>%s</th><th>%s</th></tr>" % (row["name"], row["age"], row["gender"])
content_list.append(tp)
user_content = "".join(content_list)
# 将html文件中指定位置的内容替换成数据库中取得后拼接好的内容
f = open("template_shit.html", "r", encoding='utf-8')
html_template = f.read()
f.close()
# 替换为指定内容
# 这里其实就是最简单的所谓的模板的渲染
data = html_template.replace("@@content@@", user_content)
return bytes(data, encoding="utf-8")
# 动态页面,用jinja2进行模板渲染
def template_shit(request):
# 连接数据库并取得数据
mysql_conn = pymysql.connect(host='10.0.0.204', port=3306, user='hgzero', passwd='woshiniba', db='my_test')
conn_cursor = mysql_conn.cursor(cursor=pymysql.cursors.DictCursor)
conn_cursor.execute("select name,age,gender from first_test")
user_list = conn_cursor.fetchall()
conn_cursor.close()
mysql_conn.close()
# 获取html模板的内容
f = open("template_shit.html", "r", encoding='utf-8')
html_data = f.read()
f.close()
# 用jinja2进行模板渲染
from jinja2 import Template
template = Template(html_data)
# 用从数据库中拿到的user_list来替换模板中的user_list变量
data = template.render(user_list=user_list)
# 模板中定义的规则:
"""{% for rwo in user_list %}
<tr>
<td>{{row.name}}</td>
<td>{{row.age}}</td>
<td>{{row.gender}}</td>
</tr>
{% endfor %}
"""
print(data) # 看看模板生成的内容
return data.encode("utf-8")
routers = [
('/index.html', index),
('/article.html', article),
('/user_info.html', user_info),
('/template_shit.html', template_shit),
]
def run():
sock = socket.socket()
sock.bind(('127.0.0.1', 8080))
sock.listen(5)
while True:
# 等待客户端来连接
conn, addr = sock.accept()
# 获取用户发送的数据,收到的数据是Bytes,(二进制格式)
data = conn.recv(8096)
"""对用户发来的信息进行提取"""
# 将内容转化成字符串类型
data = str(data, encoding='utf-8')
# data = bytes('shit', encoding='utf-8')
# 将内容分割成请求头和请求体
headers, bodys = data.split('\r\n\r\n')
# 得到请求头中的每一行数据
temp_list = headers.split('\r\n')
# 对请求头中的第一行数据进行提取,取得请求方法、请求URL、请求方法
method, url, protocol = temp_list[0].split(' ')
"""根据用户请求的URL来返回不同的内容"""
"""思路:
遍历routers中的每个元组的第一个值和请求头中的url进行比较,
若匹配成功,则将对应的函数名赋值给func_name,进而去执行该URL对应的函数
"""
func_name = None
for item in routers:
if item[0] == url:
func_name = item[1]
break
if func_name:
response = func_name(data) # 把data中的内容传进去
else:
response = b"404 not found"
conn.send(response)
conn.close()
if __name__ == '__main__':
run()

至此,可以看到,现在它已经基本上实现的一个框架应该实现的功能。

5. Web框架的种类

5.1 Web框架应该实现的功能

  1. Socket服务端
  2. 路由系统:根据URL来返回不同的内容,URL --> 函数
  3. 字符串加入响应体返回给用户:模板引擎渲染,字符串

5.2 Web框架的种类

根据以上的三种主要功能,web框架可以分为以下几类

  • 自己实现了 1,2,3  
    • Tornado
  • 实现了 2,3 ,而使用了第三方的Socket服务端
    • Django
  • 实现了 2 , 而使用了第三方的Socket服务器以及第三方的模板渲染引擎(如Jinja2)
    • flask

 

 

 

本文作者:Pray

本文链接:https://www.cnblogs.com/hgzero/p/13216511.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Praywu  阅读(190)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起