什么是框架?
在生活中就像我们想到的一样,框架指的我们在做一件事情的时候搭的骨架来完成基础的功能。
例如盖楼,开发商建的毛坯房就是楼的骨架,毛坯房的基础功能就是能住。如果想住的舒服
用户根据自己的需求来摆放物品。例如客厅放沙发,电视。主卧放床和衣柜等等。
例如明星开演唱会,舞台的搭建就是一个骨架,舞台基础的功能就是明星能有地方唱歌跳舞,如果想要气氛,可以在舞台上摆放不同的物品。
程序中的框架和生活中搭建的框架的功能是相同的,框架来完成一些基础的工作,
程序员在此基础上开发实现自己业务功能的代码;
把程序员从繁琐的重复性的代码中解脱出来,提交开发效率;
WEB框架的本质
我们可以这样理解:所有的Web应用框架本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 一些常用框架(Django、Tornado、Flash)是对socket服务端进行的封装,使得基础功能更加完善。
自定义初始web框架
import socket
server_sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_sk.bind(('127.0.0.1',9999))
server_sk.listen(128)
while True:
print('等待客户连接')
client_sk,addr=server_sk.accept()
content=client_sk.recv(1024) #默认是二进制内容
print(content) #接收到是内容是请求报文
content=content.decode('utf-8')
print(content)
#给浏览器发送内容
client_sk.send('HTTP/1.1 200 OK\r\n'.encode('utf-8')) #设置响应首行
# client_sk.send('Content-Type: text/html;charset=gbk')
client_sk.send('\r\n'.encode('utf-8')) #设置响应空格
client_sk.send('很好'.encode('utf-8')) #设置响应体内容,可以发送英文,但是发送中文会变化乱码
client_sk.close()
我们通过十几行代码简单地演示了web 框架的本质。
接下来就让我们继续完善我们的自定义web框架吧!
扩展:
解决返回给浏览器中文乱码问题:
import socket
server_sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_sk.bind(('127.0.0.1',9999))
server_sk.listen(23) #可以连接客户端的个数
while True:
client_sk,addr=server_sk.accept()
content=client_sk.recv(1024).decode('utf-8')
print(content)
#向浏览器发送内容
msg1='HTTP/1.1 200 OK\r\n'.encode('utf-8') #设置响应首行
# 设置响应头,告诉浏览器返回的是文本类型的html,并且以utf-8格式解码
msg2='Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8') #通过设置响应头来告诉浏览器以什么方式解码来避免中文出现乱码
msg3='\r\n'.encode('utf-8')
msg4='你好浏览器'.encode('utf-8')
client_sk.send(msg1)
client_sk.send(msg2)
client_sk.send(msg3)
client_sk.send(msg4)
client_sk.close()
(2).根据不同的路径返回不同的内容
如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?
我们可以从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断...
import socket
server_sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_sk.bind(('127.0.0.1',9999))
server_sk.listen(34)
while True:
client_sk,addrs=server_sk.accept()
content=client_sk.recv(1024).decode('utf-8')
print("客户端发来贺电:")
print(content)
#切出index
con=content.split(' ')
print(con)
con1=con[1].split('/')
path=con1[1]
#根据url中的内容返回不同页面
if path=='index':
msg='这是{}页面'.format(path).encode('utf-8')
elif path=='home':
msg='这是{}页面'.format(path).encode('utf-8')
else:
msg='sorry {} not find'.format(path).encode('utf-8')
#向页面返回内容
msg1='HTTP/1.1 200 ok\r\n'.encode('utf-8')
msg2='Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8')
msg3='\r\n'.encode('utf-8')
client_sk.send(msg1)
client_sk.send(msg2)
client_sk.send(msg3)
client_sk.send(msg)
#关闭客户端
client_sk.close()
(3).根据不同的路径返回不同的内容--函数版
上面的代码解决了不同URL路径返回不同内容的需求。
但是问题又来了,如果有很多很多路径要判断怎么办?难道要挨个写if判断? 当然不用,我们有更聪明的办法。
import socket
server_sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_sk.bind(('127.0.0.1',9999))
server_sk.listen(34)
while True:
client_sk,addrs=server_sk.accept()
content=client_sk.recv(1024).decode('utf-8')
print("客户端发来贺电:")
print(content)
#切出index
con=content.split(' ')
print(con)
con1=con[1].split('/')
path=con1[1]
# 向页面返回内容
msg1 = 'HTTP/1.1 200 ok\r\n'.encode('utf-8')
msg2 = 'Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8')
msg3 = '\r\n'.encode('utf-8')
client_sk.send(msg1)
client_sk.send(msg2)
client_sk.send(msg3)
def index():
msg = '这是{}页面\r\n'.format(path).encode('utf-8')
client_sk.send(msg)
# return msg
def home():
msg = '这是{}页面'.format(path).encode('utf-8')
client_sk.send(msg)
def other():
msg = 'sorry {} not find'.format(path).encode('utf-8')
client_sk.send(msg)
#根据url中的内容返回不同页面
if path=='index':
index()
elif path=='home':
home()
else:
other()
#关闭客户端
client_sk.close()
看起来上面的代码还是要挨个写if判断,怎么办?我们还是有办法!
import socket
server_sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_sk.bind(('127.0.0.1',9999))
server_sk.listen(34)
def index(path):
msg = '这是{}页面\r\n'.format(path).encode('utf-8')
return msg
def home(path):
msg = '这是{}页面'.format(path).encode('utf-8')
return msg
def error(path):
msg = 'sorry {} not find'.format(path).encode('utf-8')
return msg
#定义一个url和执行函数对应关系的列表
path_lst = [
('/index', index),
('/home', home),
]
while True:
client_sk,addrs=server_sk.accept()
content=client_sk.recv(1024).decode('utf-8')
print("客户端发来贺电:")
print(content)
#切出index
con=content.split(' ')
path=con[1]
# 向页面返回内容
msg1 = 'HTTP/1.1 200 ok\r\n'.encode('utf-8')
msg2 = 'Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8')
msg3 = '\r\n'.encode('utf-8')
client_sk.send(msg1)
client_sk.send(msg2)
client_sk.send(msg3)
# 2.定义一个变量来接受对应的函数名称
func = None
# 3.变量列表,查收是否有对应的url
for path_tup in path_lst:
if path_tup[0] == path:
func = path_tup[1]
if func:
msg = func(path)
else:
msg = error(path)
client_sk.send(msg)
#关闭客户端
client_sk.close()
完美解决了不同URL返回不同内容的问题。 但是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?
没问题,不管是什么内容,最后都是转换成字节数据发送出去的。 我们可以打开HTML文件,读取出它内部的二进制数据,然后再发送给浏览器。
import socket
server_sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_sk.bind(('127.0.0.1',9999))
server_sk.listen(34)
def index(path):
#通过这里把对应界面返回
with open('index.html','rb') as f:
msg=f.read()
return msg
def home(path):
with open('home.html', 'rb') as f:
msg = f.read()
return msg
def error(path):
with open('error.html', 'rb') as f:
msg = f.read()
return msg
path_lst = [
('/index', index),
('/home', home),
]
while True:
client_sk,addrs=server_sk.accept()
content=client_sk.recv(1024).decode('utf-8')
print("客户端发来贺电:")
print(content)
#切出index
con=content.split(' ')
path=con[1]
# 向页面返回内容
msg1 = 'HTTP/1.1 200 ok\r\n'.encode('utf-8')
msg2 = 'Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8')
msg3 = '\r\n'.encode('utf-8')
client_sk.send(msg1)
client_sk.send(msg2)
client_sk.send(msg3)
func = None
for path_tup in path_lst:
if path_tup[0] == path:
func = path_tup[1]
if func:
msg = func(path)
else:
msg = error(path)
client_sk.send(msg)
#关闭客户端
client_sk.close()
这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。
没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。(这里使用时间戳来模拟动态的数据)
import socket
server_sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_sk.bind(('127.0.0.1',9999))
server_sk.listen(34)
def index(path):
#通过这里把对应界面返回
with open('index.html','r',encoding='utf-8') as f:
msg=f.read()
msg=msg.replace('xxoo',path).encode('utf-8')
return msg
def home(path):
with open('home.html', 'r',encoding='utf-8') as f:
msg = f.read()
msg=msg.replace('xxoo', path).encode('utf-8')
return msg
def error(path):
with open('error.html', 'r',encoding='utf-8') as f:
msg = f.read()
msg=msg.replace('xxoo', path).encode('utf-8')
return msg
path_lst = [
('/index', index),
('/home', home),
]
while True:
client_sk,addrs=server_sk.accept()
content=client_sk.recv(1024).decode('utf-8')
print("客户端发来贺电:")
print(content)
#切出index
con=content.split(' ')
path=con[1]
# 向页面返回内容
msg1 = 'HTTP/1.1 200 ok\r\n'.encode('utf-8')
msg2 = 'Content-Type:text/html;charset=utf-8\r\n'.encode('utf-8')
msg3 = '\r\n'.encode('utf-8')
client_sk.send(msg1)
client_sk.send(msg2)
client_sk.send(msg3)
func = None
for path_tup in path_lst:
if path_tup[0] == path:
func = path_tup[1]
if func:
msg = func(path)
else:
msg = error(path)
client_sk.send(msg)
#关闭客户端
client_sk.close()
目录结构
总结
1.web框架的本质:socket 服务端 与浏览器的通讯。
2.socket 服务端功能可以划分为3部分:
- a.负责与浏览器收发消息(socket)在python中有专门的框架wsgiref/uWsgi/gunicorn...
- b.根据用户访问不同的路径执行不同的函数
- c.从HTML中读取出内容,并且完成字符串的替换
3.Python中 web 框架的分类:
- 按照2上面的功能分类
(1).框架自带a,b,c 功能 ----> Tornado
(2).框架自带b,c,使用第三方的a ---->Django
(3).框架自带b,使用第三方的a,c ---->Flask
-按照另一个维度划分
(1).Diango ,Tornado-->大而全(做一个网站用到的技术都有)
(2).其他 例如 Flask 轻量级只封装了核心功能。a部分和b、c 部分通讯需要遵守WSGI 协议。