web框架的本质
Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,基于请求做出响应,客户都先请求,服务端做出对应的响应,按照http协议的请求协议发送请求,服务端按照http协议的响应协议来响应请求,这样的网络通信,我们就可以自己实现Web框架了。
通过对socket的学习,我们知道网络通信,我们完全可以自己写了,因为socket就是做网络通信用的,下面我们就基于socket来自己实现一个web框架,写一个web服务端,让浏览器来请求,并通过自己的服务端把页面返回给浏览器,浏览器渲染出我们想要的效果。
一次请求 一次回应 就断开 请求路径
http协议
https://www.cnblogs.com/clschao/articles/9230431.html
http协议特点
1.基于 请求-响应 的模式
2.无状态保存
不保存之前的状态
3.无连接
保证效率,一个客户不会一直用网站
请求:request
响应:response
使用HTTP协议每当有新的请求发送时,就会有对应的新响应产生。协议本身并不保留之前一切的请求或响应报文的信息。
这是为了更快地处理大是事务确保协议的可伸缩性而特意把HTTP协议设计成如此简单的。可是随着Web的不断发展因无状态而
导致业务处理变得棘手的情况增多了。比如,用户登录到一家购物网站即使他跳转到该站的其他页面后也需要能继续保持登录状
态。针对这个实例,网站为了能够掌握是谁送出的请求,需要保存用户的状态。HTTP/1.1 虽然是无状态协议但为了实现期望的保持状
态功能,于是引入了Cookie技术。有了Cookie再用HTTP协议通信就可以管理状态了。有关Cookie的详细内容稍后讲解。
无连接
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种
方式可以节省传输时间,并且可以提高并发性能,不能和每个用户建立长久的连接,请求次相应次,服务端和客户端就中断
了。但是无连接有两种方式,早期的http协议是一个请求一个响应之后,直接就断开了,但是现在的http协议1 1版本不是直接就断
开了,而是等几秒钟,这几秒钟是等什么呢,等着用户有后续的操作,如果用户在这几秒钟之内有新的清求,那么还是通过之前的
连接通道来收发消息,如果过了这几秒钟用户没有发送新的请求,那么就会断开连接,这样可以提高效率,减少短时间内建立连接
的次数,因为建立连接也是耗时的,默认的好像是3秒中现在,但是这个时间是可以通过咱们后端的代码来调整的,自己网站根据自网站用户的行为中公析统计出-个星代的笔结时间
斜杠后是地址
转态码
请求
User-Agent
键值对
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
})
http协议 超文本传输协议
请求 和 响应
请求格式
GET / HTTP/1.1 --- GET /clschao/articles/9230431.html?name=chao&age=18 HTTP/1.1
User-Agent:....#请求头部
xx:xx
请求数据 get请求方法没有请求数据 post请求数据方法的请求数据放在这里
响应格式
HTTP/1.1 200 ok #协议 版本 转态 描述
kl:v1
k2:v2
响应数据
URL: https://www.cnblogs.com/clschao/articles/9230431.html
传送协议。
层级URL标记符号(为[//],固定不变)
服务器。(通常为域名,有时为IP地址)
端口号。(以数字方式表示,若为HTTP的默认值“:80”可省略)
路径。(以“/”字符区别路径中的每一个目录名称) /clschao/articles/9230431.html
查询。(GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
https://www.cnblogs.com/clschao/articles/9230431.html?name=chao&age=18
请求方法
get post
GET提交的数据会放在URL之后,也就是请求行里面,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456.(请求头里面那个content-type做的这种参数形式,后面讲) POST方法是把提交的数据放在HTTP包的请求数据部分中.
GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
GET与POST请求在服务端获取请求数据方式不同,就是我们自己在服务端取请求数据的时候的方式不同了
常用的get请求方式:浏览器输入网址 ,a标签 ,form标签 method='get'
post请求方法,一般都用来提交数据.比如用户名密码登录
其他方法:HEAD PUT DELETE TRACE OPTIONS CONNECT PATCH
响应状态码
1xx消息——请求已被服务器接收,继续处理
2xx成功——请求已成功被服务器接收、理解、并接受
3xx重定向——需要后续操作才能完成这一请求
4xx请求错误——请求含有词法错误或者无法被执行
5xx服务器错误——服务器在处理某个正确请求时发生错误
基于socket来自己实现一个web框架
函数版回复html页面
原理一个页面多个请求,每次请求过来我们都要发送响应格式b'HTTP/1.1 200 ok
'
在针对对应的请求回复对应的页面,最后关闭服务端
import socket
server = socket.socket()
server.bind(('127.0.0.1',8001))
# 127.0.0.1:8001
server.listen()
while 1:
conn,addr = server.accept()
from_brower_msg = conn.recv(1024)
# print(from_brower_msg)
path = from_brower_msg.decode('utf-8').split(' ')[1]
conn.send(b'HTTP/1.1 200 ok
')
if path == '/':
with open('04test.html','rb') as f:
data = f.read()
elif path == '/style.css':
with open('style.css', 'rb') as f:
data = f.read()
elif path == '/test.js':
with open('test.js', 'rb') as f:
data = f.read()
elif path == '/1.jpg':
with open('1.jpg', 'rb') as f:
data = f.read()
conn.send(data)
conn.close()
函数版+多线程web框架
原理一个请求发过来 开启一个线程 for循环urls中文件中urlpatterns列表 对应则调用对应views文件内容中对应的函数
import socket
from urls import urlpatterns
from threading import Thread
server = socket.socket()
server.bind(('127.0.0.1',8003))
server.listen()
while 1:
conn,addr = server.accept()
from_brower_msg = conn.recv(1024)
path = from_brower_msg.decode('utf-8').split(' ')[1]
conn.send(b'HTTP/1.1 200 ok
')
for url in urlpatterns:
if path == url[0]:
t = Thread(target=url[1],args=(conn,))
t.start()
break
urls中文件内容
import views
urlpatterns = [
('/',views.home),
('/center',views.person_center),
('/style.css',views.css),
('/test.js',views.js),
('/1.jpg',views.pic),
('/favicon.ico',views.ico),
views 文件内容
def home(conn):
with open('1.jpg', 'rb') as f:
data = f.read()
conn.send(data)
conn.close
动态页面的web框架
原理字符串替换 以r模式读取html页面在字符串替换 ,先发送响应格式 在以b发送修改后html页面
import socket
from urls import urlpatterns
from threading import Thread
server = socket.socket()
server.bind(('127.0.0.1',8003))
server.listen()
while 1:
conn,addr = server.accept()
from_brower_msg = conn.recv(1024)
path = from_brower_msg.decode('utf-8').split(' ')[1]
conn.send(b'HTTP/1.1 200 ok
')
for url in urlpatterns:
if path == url[0]:
# ret = url[1]()
t = Thread(target=url[1],args=(conn,))
t.start()
break
函数
def home(conn):
username='测试'
with open('cs.html') as f:
data=f.read()
data=data.replace('%xxoo%',username)
conn.send(data.encode('utf-8'))
conn.close
回复不同HTML页面
原理路径分发 先找到urls中文件内容'/center'
执行对应函数views.person_center
urls中文件内容
import views
urlpatterns = [
('/',views.home),
('/center',views.person_center),
('/style.css',views.css),
('/test.js',views.js),
('/1.jpg',views.pic),
('/favicon.ico',views.ico),
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--<h1>欢迎%xxoo%来到xx网站</h1>-->
<h1>欢迎24期来到xx网站</h1>
<a href="/center">个人中心</a>
</body>
</html>
基于wsgiref模块
wsgiref模块其实就是将整个请求信息给封装了起来,就不需要你自己处理了,假如它将所有请求信息封装成了一个叫做request的对象,那么你直接request.path就能获取到用户这次请求的路径,request.method就能获取到本次用户请求的请求方式(get还是post)等,那这个模块用起来,我们再写web框架是不是就简单了好多啊。
from wsgiref.simple_server import make_server
from urls import urlpatterns
def application(environ,start_response):
#里面写逻辑 函数名自己定义 wsgiref模块其实就是将整个请求信息给封装了起来,就不需要你自己处理了,假如它将所有请求信息封装成了一个叫做request的对象,那么你直接request.path就能获取到用户这次请求的路径,request.method就能获取到本次用户请求的请求方式(get还是post)等,那这个模块用起来,我们再写web框架是不是就简单了好多啊。
# environ 所有请求相关信息
# start_response --封装响应数据格式
print('当前请求路径',environ['PATH_INFO'])
path = environ['PATH_INFO']
start_response('200 ok',[]) # conn.send(b'HTTP/1.1 200 ok
')
for url in urlpatterns:
if path == url[0]:
ret = url[1]()
return [ret]#ret是读取的内容 自己send回去
if __name__ == '__main__':
h = make_server('127.0.0.1', 8080, application)
h.serve_forever()
模板渲染JinJa2
其实模板渲染有个现成的工具: jinja2
pip install jinja2
原理循环会生成3个li标签
def home(conn):
with open('08jinja2test.html', 'r',encoding='utf-8') as f:
data = f.read()
t = Template(data)
ret = t.render({'hobby':['金钱','女人','金鑫']})
conn.send(ret.encode('utf-8'))
conn.close()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>个人介绍</h1>
<ul>
{% for i in hobby %}
<li>{{ i }}</li>
{% endfor %}
</ul>
</body>
</html>