一.Web应用本质
为了了解Django的客户端与服务端的交互原理,我们需要了解Web应用的本质方便以后更加的理解Django原理
在Web应用中,服务器把网页传给浏览器,实际上就是把网页的HTML代码发送给浏览器,让浏览器显示出来。而浏览器和服务器之间的传输协议是HTTP。所以本质上就是:
- 浏览器发送一个HTTP请求
- 服务器收到请求,生成一个HTML文档
- 服务器把HTML文档作为HTTP响应的Body发送给浏览器
- 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。
而在http协议中,我们可以用socket来实现
1.socket本质
web服务器本质上可以认为是一段代码,可以不断的处理http协议的网络请求,而http协议可以使用socket实现,并且http协议是一个无状态的协议,即浏览器发起请求,服务器接收请求,然后给浏览器回复数据,然后断开连接;那么可以用socket来实现一个最简单的web服务器。
- HTTP:
无状态、短连接
- TCP:
不断开(socket连接)
- 服务器把HTML文档作为HTTP响应的Body发送给浏览器
- WEB应用(网站)
- 浏览器(socket客服端)
在客户端访问网址后,通过ping 网址 转化为ip地址进行访问(默认监听80端口)
- 浏览器(socket服务端)
1. 监听ip和端口
2. 收到客户端传来的数据
3. 响应客户端,发送响应数据
4. 断开客户端连接
- 浏览器(socket客服端)
示意如下:
import socket
sock = socket.socket()
# 网页和监听端口(这里先默认本机端口的8080)
sock.bind(('127.0.0.1',8080))
# 等待五个用户
sock.listen(5)
# 因为socket是TCP协议不断开
while True:
#等待
conn,addr= sock.accept()
# 有人来连接
# 获取用户发送的数据
data = conn.recv(8096)
# 回数据,转字节
conn.send(b'123123')
# 断开客户端连接
conn.close()
示意如下:
二.发送HTTP协议、响应
1.HTTP协议
在html中发送获取数据都遵从着HTTP协议
,随着时间的变化一点一点的在改变,直到现在,我们可以通过socket发来的请求打印data来查看http发送的响应数据:
print(data)
#返回
"""
响应头
状态:200 OK
'GET /(get请求数据默认放这) HTTP/1.1' \
'Host: 127.0.0.1:8080' \
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0' \
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' \
'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' \
'Accept-Encoding: gzip, deflate' \
'Connection: keep-alive' \
'Upgrade-Insecure-Requests: 1'
请求体:如果是post请求数据放这
响应体
'GET /favicon.ico HTTP/1.1' \
'Host: 127.0.0.1:8080' \
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0' \
'Accept: image/webp,*/*' \
'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' \
'Accept-Encoding: gzip, deflate' \
'Connection: keep-alive' \
'Referer: http://127.0.0.1:8080/'
"""
很显然我们上面写的代码没有根据HTTP的协议发送请求,应该遵循HTTP协议,用户在页面上看到的内容”字符串“(看到页面效果,是由浏览器解析出来的)
示意如下:
import socket
sock = socket.socket()
# 网页和监听端口(这里先默认本机端口的8080)
sock.bind(('127.0.0.1',8080))
# 等待五个用户
sock.listen(5)
# 因为socket是TCP协议不断开
while True:
#等待
conn,addr= sock.accept()
# 有人来连接
#1. 获取用户发送的数据
data = conn.recv(8096)
# 4. 遵循http协议
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
# 5. 返回数据,转字节
conn.send(b'123123')
# 6. 断开客户端连接
conn.close()
2.HTTP发送响应
但是我们目前怎么样都只有一个页面,不能像其他的网站一样有多个页面,所以我们要从客户端发来的请求中拿到数据在做出响应在返回:
- 获取用户发送的数据
- 接收请求传来的数据
- 分割数据
- 遵循http协议
- 返回相应数据,转字节
- 断开客户端连接
示意如下:
import socket
# 相应数据函数
def f1(request):
return b"f1"
def f2(request):
return b"f2"
# 类似django的路由
routers = [
('/xxx', f1),
('/ooo', f2),
]
# 主函数
def run():
sock = socket.socket()
# 网页和监听端口(这里先默认本机端口的8080)
sock.bind(('127.0.0.1', 8080))
# 等待五个用户
sock.listen(5)
# 因为socket是TCP协议不断开
while True:
# 等待
conn, addr = sock.accept()
# 有人来连接
# 1. 获取用户发送的数据
data = conn.recv(8096)
# 2. 接收请求传来的数据
data = str(data, encoding='utf-8')
# 3. 分割数据,请求头,和请求体
headers, bodys = data.split('\r\n\r\n')
temp_list = headers.split('\r\n')
# 获取一.get请求 二.访问url 三.请求体
res, url, hea = temp_list[0].split(' ')
func_name = None
# 4. 遵循http协议
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
# 这时候打印的就是后缀的值
print(url)
# 遍历路由
for item in routers:
if item[0] == url:
# 判断是否是该url返回函数地址
func_name = item[1]
break
# 判断函数是否存在
if func_name:
response = func_name(data)
else:
response = b"404 not found"
# 5. 返回相应数据,转字节
conn.send(response)
# 6. 断开客户端连接
conn.close()
if __name__ == '__main__':
run()
示意如下:
所以通过上面例子我们可以写一个html然后渲染。
◼ 静态网页
因为如果想要数据库实时动态产生,我们需要数据库的帮助,不过在开始介绍前,我们先写静态网页,首先在当前主目录下创建两个文件夹动态网页和静态网页。
用于区分一下动态网页和静态网页。
在静态文件/index.html中定义视图如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
</tr>
</thead>
<tbody>
<tr>
<th>1</th>
<th>root</th>
<th>root@qq.com</th>
</tr>
</tbody>
</table>
</body>
</html>
在f1中定义函数如下:
def f1(request)
f=open('静态网页/index.html', 'rb')
data=f.read()
f.close()
return data
示意如下:
◼ 动态网页
在前面的文章有写到操作数据库的方法,种类很多这里我们使用pymysql来实现,如果没看到这里的可以点开链接:点我!!!进行查看。
- 导入驱动程序
pip install pymysql
- 创建数据库连接
import pymysql
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='xxx', db='myemployees')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 关闭
cursor.close()
conn.close()
在这里我们把/ooo改成/userlist.htm为了告诉大家django的路由和这个很像
routers = [
('/xxx', f1),
('/userlist.htm', f2),
]
在动态网页/userlist.html中定义视图如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
</tr>
</thead>
<tbody>
<tr>
@@content@@
</tr>
</tbody>
</table>
</body>
</html>
因为返回的是字符串所以我们可以对字符串进行拼接成html看得懂的,然后在从html转化为字节传给浏览器。
在f2中定义函数如下:
def f2(request):
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='myemployees')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 在数据库查询数据
cursor.execute("SELECT employee_id,first_name,phone_number FROM employees")
# 获取所有查到的数据
user_list = cursor.fetchall()
# 关闭
cursor.close()
conn.close()
# 拼接
content_list = []
for row in user_list:
tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" % (row['employee_id'], row['first_name'], row['phone_number'])
# 添加进来
content_list.append(tp)
# 拼接到一起
content = "".join(content_list)
# 打开userlist.html
f = open('动态网页/userlist.html', 'r', encoding='utf-8')
# 获得字符串(模板的渲染)
template = f.read()
# 关闭
f.close()
# 把我之前在html写的@@content@@修改
data = template.replace('@@content@@', content)
# 把写好的字符串转成字节的形式给浏览器渲染成可视化界面
return bytes(data, encoding='utf-8')
此时访问127.0.0.1:8080/userlist.htm
示意如下:
三.jinja2模板渲染
对于上面的一系列操作,相对麻烦,又要替换字符串,转字节,还得自己拼接,这时python中就出来了一个模块jinjia2,它可以帮助我们进行模板的渲染,而渲染的规则要和它一致
。
- 安装jinja2模块
pip install jinja2
- 添加一个路由和函数
routers = [
('/xxx', f1),
('/userlist.htm', f2),
('/host.html',f3),
]
在动态网页/host.html中定义视图如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
</tr>
</thead>
<tbody>
{% for row in user_list %}
<tr>
<td>{{ row.employee_id }}</td>
<td>{{ row.first_name }}</td>
<td>{{ row.phone_number }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
在f3中定义函数如下:
from jinja2 import Template
def f3(request):
f = open('动态网页/host.html', 'r', encoding='utf-8')
# 获得字符串(模板的渲染)
data = f.read()
# 关闭
f.close()
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='myemployees')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 在数据库查询数据
cursor.execute("SELECT employee_id,first_name,phone_number FROM employees")
# 获取所有查到的数据
user_list = cursor.fetchall()
# 关闭
cursor.close()
conn.close()
# 使用jinja2渲染
template=Template(data)
data=template.render(user_list=user_list)
# 上传
return data.encode('utf-8')
此时访问此时访问127.0.0.1:8080/host.html
示意如下:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/66861.html