1. web应用概念
1.1 web应用
web --> 万维网简称.
web应用:通过web访问的程序.
能实现前后端的数据交互。
1.2 web开发架构
web软件开发架构:
1. C/S 架构 client 客户端 server 服务端
2. B/S 架构 browser 浏览器 server 服务端
B/S的本质也是C/S,浏览器充当客户端.
服务器需要具备的特征: 24小时对外提供服务.
1.3 web框架的优/缺点
优点:
1.只需要一个浏览器,不需要安装客户端.(B/S)
2.客户端不需要更新,服务端更新即可.(B/S)
缺点:
1.服务器出问题,客户端受到影响.
2.网页兼容性问题
2.HTTP协议
HTTP协议四大特性:
1.基于请求响应
2.基于tcp/ip之上的应用层协议
3.无状态(不保存用户信息)
4.短/无链接。
数据请求格式:
1.请求首行(http版本,)
2.请求头
3./r/n
4.请求体
响应数据格式:
1.响应首行(状态码)
2.响应头
3./r/n
4.响应体
http协议 数据传输是明文的 (在黑客眼里就是没穿衣服的小可爱)
https协议 数据传输是密文(数据更加的安全)
https = http + http + ssl(证书)
http 默认端口 80
https 默认端口 443
3.web服务端
自己写的服务端,打开浏览访问,是通过http协议通信的.需要send添加上HTTP协议.
响应头:
(b'HTTP/1.1 200 ok\r\n\r\n xxx信息)
HTTP/1.1 HTTP的版本 200 响应状态码 \r\n\r\n 响应体
# 服务端
import socket
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
conn, addr = server.accept()
data = conn.recv(1024)
print(date)
conn.send(b'HTTP/1.1 200 ok\r\n\r\nhello word')
conn.close()
date重点研究对象(先省略掉 b'GET /favicon.ico)
客户端收到的get请求信息:
b'GET / HTTP/1.1\r\n
Host: 127.0.0.1:8080\r\n
Connection: keep-alive\r\n
Cache-Control: max-age=0\r\n
sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"\r\n
sec-ch-ua-mobile: ?0\r\n
Upgrade-Insecure-Requests: 1\r\n
...
Sec-Fetch-Site: none\r\n
Sec-Fetch-Mode: navigate\r\n
Sec-Fetch-User: ?1\r\n
Sec-Fetch-Dest: document\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6\r\n\r\n'
客户端收到的favicon.ico请求信息:
b'GET /favicon.ico HTTP/1.1\r\
...
favicon.ico请求处理:
favicon.ico 图标用于收藏夹图标和浏览器标签上的显示,如果不设置,
浏览器会请求网站根目录的这个图标,如果网站根目录也没有这图标会产生 404。
出于优化的考虑,要么就有这个图标,要么就禁止产生这个请求。
不希望产生 favicon.ico 的请求。
可以在页面的 <head> 区域,加上如下代码实现屏蔽:
<link rel="icon" href="data:;base64,=">
或者详细一点
<link rel="icon" href="data:image/ico;base64,aWNv">
3.给客户端发信息
用户输入的后缀:127.0.0.1:8080/index/
依据后缀返回不同的结果.
思路:
0. 服务端获取的数据是二进制类型,转换为字符串类型。
1. 响应头返回的数据格式为 GET /index/ HTTP/1.1 ···
2. 这个数据以/进行切分,切分后是一个列表,index的索引为1。
3. 判断 列表[1] 是否 = 'index'。
# 服务端
import socket
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
conn, addr = server.accept()
data = conn.recv(1024)
# 解码
data = data.decode('utf-8')
print(f'data 的数据>>>:{data}')
# 对数据进行切分
res = data.split('/')
print(f'res[1] 的值>>>:{res[1]}')
# 先发响应头再发 信息
"""
http特性:当数据量比较小且时间间隔比较短的多次数据,
那么TCP会自动打包成一个数据包发送.
"""
conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
if res[1] == 'index':
conn.send(b'index')
else:
conn.send(b'404')
4.返回前端页面
1.先创建一个my_html.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>100G学习网址</title>
</head>
<body>
<a href="https://www.baidu.com">进入学习</a>
</body>
</html>
2.读取文件,b模式读取为二进制,直接将html文件二进制发给浏览器,浏览器会自动进行解析并渲染。
# 服务端
import socket
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
conn, addr = server.accept()
data = conn.recv(1024)
data = data.decode('utf-8')
conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
with open('my_html.html', mode='rb') as rf:
my_html = rf.read()
conn.send(my_html)
5.wsgiref模块
wsgiref是内置模块,内置一个服务端。
wsgiref模块只要作用
1.请求来的时候解析http格式的数据 封装成大字典
2.响应走的时候给数据打包成符合http格式 再返回给浏览器
from wsgiref.simple_server import make_server
def run(env, response):
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回浏览器的数据
"""
print(env) # env是一个字典,存放请求头内的所有数据
response('200 ok', []) # 响应状态码
return [b'hello word'] # 返回页面的字符串
if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run) # host, port, app 三个参数
# 实时监听地址,只有接受到客户端的请求就交给run()处理,会将env, response两个函数的参数传给run函数
print(type(server)) # <class 'wsgiref.simple_server.WSGIServer'>
server.serve_forever() # 启动服务端
启动服务端
在浏览器中输入: 127.0.0.1:8080/index
env 请求头是字典:
{...一大堆数据···'PATH_INFO': '/index/'···}
5.1给客户端返回信息1
依据后缀名称返回对应的值。
from wsgiref.simple_server import make_server
def run(env, response):
# 请求头 格式是字典
print(env)
# 字典.get('PATH_INFO') 通过键 PATH_INFO 取到 url后面的 '路径' 信息
path_info = env.get('PATH_INFO')
response('200 ok', [])
if path_info == '/index':
return [b'index']
elif path_info == '/login':
return [b'login']
else:
return [b'404']
if __name__ == '__main__':
# 绑定ip 端口 连接成功执行的函数
server = make_server('127.0.0.1', 8080, run) # host, port, app 三个参数
# 实时监听地址,只有接受到客服的请求就交给run()处理,make_server有一个返回值
# 会将env, response两个函数的参数传给run函数
server.serve_forever() # 启动服务端
5.2给客户端返回信息2
依据后缀名称返回对应的值。
from wsgiref.simple_server import make_server
# 定义函数依据不同的值返回不同的信息
def index(env):
return 'index'
def login(env):
return 'login'
def error(env):
return '404'
# 定义一个字典 存放所有函数
urls = [
('/index', index),
('/login', login)
]
def run(env, response):
# 获取 url后面的 '路径'
current_path = env.get('PATH_INFO')
# 响应头
response('200 ok', [])
# 定义一个变量
func = None
# 遍历存放url的字典
for url in urls:
if current_path == url[0]:
func = url[1] # 将函数名作为值赋值给 func
break # 匹配之后立刻结束循环
if func: # 判断func是否为空, 只要不存在于url字典中,它的值就是None
res = func(env)
else:
res = error(env)
return [res.encode('utf-8')] # 返回给客户端的值 是一个 列表套二进制字符串的格式
if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run)
server.serve_forever()
6.动/静态网页
静态网页:页面上的数据在html网页上写死的。
动态网页:数据是实时获取的。
1.后端获取时间展示到html页面。
2.数据是从数据库中获取到html页面。
7.web框架
将之前的一个文件拆分封装。
1.控制层 Control.py 写控制
2.路由层 urls.py 函数对应关系 依据url后面的'路径'返回不同的信息
3.视图层 views.ps 写逻辑
templates 文件夹 专门存储html前端页面
后续添加功能只需要在
urls.py 中书写对应的关系,
views.py 中书写业务逻辑。
# Control.py
from wsgiref.simple_server import make_server
from urls import *
def run(env, response):
print(env)
current_path = env.get('PATH_INFO')
response('200 ok', [])
# 定义一个变量
func = None
for url in urls:
if current_path == url[0]:
func = url[1]
break # 匹配之后立刻结束循环
if func: # 判断func是否为空
res = func(env)
else:
res = error(env)
return [res.encode('utf-8')]
if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run)
server.serve_forever()
# urls.py
from views import *
urls = [
('/index', index),
('/login', login),
('/study', study),
('/get_time', get_time)
]
# views.py
def index(env):
return 'index'
def login(env):
return 'login'
def error(env):
return '404'
def study(env):
with open('templates/myhtml.html', mode='rt', encoding='utf-8') as f:
return f.read()
import datetime
def get_time(env):
current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
with open('templates/my_time.html', mode='rt', encoding='utf-8') as f:
date = f.read() # 以字符串模式读入页面
date = date.replace('xxx', current_time) # 将字符串中xxx替换为时间
return date
<!-- myhtml.html templates 文件夹下 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>100G学习网址</title>
</head>
<body>
<a href="https://www.baidu.com">进入学习</a>
</body>
</html>
<!-- get_time.html templates 文件夹下 动态网页 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>time</title>
</head>
<body>
xxx
</body>
</html>
8.模板语法
将一个字典传递给html文件,并可以在文件时上便捷的操作字典数据。
# 在urls.py中添加关系
('/get_dict', get_dict)
在templates文件下创建get_dir.html文件
模板语法是在后端起作用的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>我是一个页面</h1>
<!-- 模板语法 -->
{{user }}
{{user.get('username')}}
{{user.age}}
{{user['hobby']}}
</body>
</html>
# 在views.py中添加
# 安装 模板语法的Jinja2模块 pip3 install jinja2
from jinja2 import Template
def get_dict(env):
user_div = {'username': 'kid', 'age': 18, 'hobby': 'read'}
with open('templates/get_dic.html', mode='rt', encoding='utf-8') as f:
data = f.read()
tmp = Template(data)
res = tmp.render(user=user_div)
# 个头 get_dic.html传递一个值,页面上通过user就能够拿到use_dic
return res
将html页面信息给Template处理,返回一个对象
render方法修改html模板语法中对象的属性.
修改值的范式
obj.render(k='v')
obj.render({'k': 'v'})
模板语法:
{{user }} 以字典展示 {'username': 'kid', 'age': 18, 'hobby': 'read'}
{{user.get('username')}} 通过get取值 kid
{{user.age}} 通过.取值 18
{{user['hobby']}} 通过键取值 read
9.简易web框架
9.1目录结构
目录结构:
项目:
|----bin
| |-start.py 启动文件
|
|----core
| |-src.py 控制文件
|
|----url
| |-urls.py 函数对应关系
|
|----view
| |-views.py 业务逻辑
|
|----lib
| |-common.py 公共文件
|
|----conf
| |-setting.py 配置文件
|
|----templates 前端页面
|-login.html
|-index.html
|-time.html
|-get_dict.html
9.2启动文件
# start.py
import os
import sys
from core import src
# pycharm中会自动替我们完成下面两步.
# 1.获取当前项目路径
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
# print(BASE_PATH)
# F:/synchro/Project/web_frame
# 2.把 当前项目路径 添加到搜索模块路径当中
sys.path.append(BASE_PATH)
# print(sys.path)
# 启动程序
if __name__ == '__main__':
src.run()
9.3控制文件
# src.py
# 核心控制
from wsgiref.simple_server import make_server
from url import urls
# 服务器 运行的 app 运行之后 服务器将 env, response 两个参数传给app
def web_app(env, response):
print('2.收到请求,启动web_app!')
# 获取请求头 --> 字典
print(f'3.请求头信息{env}')
# 获取url后面的路径 通过键取值
get_url = env.get('PATH_INFO')
print(f'4.客户输入的路径{get_url}')
# 准备好可以访问的资源
print('5.判断路径是否存在...')
for url in urls.accessible:
if get_url == url:
msg = urls.accessible[get_url]()
print('6.路径存在!')
break
else:
print('6.路径不存在!')
# 定义一个变量存放返回的信息
msg = '404'
print('7.发送响应通用')
response('200 ok', [])
print(f'8.发送响应体>>:\n {msg}')
return [msg.encode('utf8')]
# 启动程序
def run():
print('0.程序启动!')
# 创建服务器对象 需要传入三个参数 ip 端口 app
server = make_server('127.0.0.1', 8080, web_app)
print('1.绑定信息成功! \n 开始监听...')
# 0.5秒检查一次服务是否在启动
server.serve_forever()
9.4函数对应关系
# urls.py
# 存储函数对应关系
from view import views
# 路由字典
accessible = {
# 登入
'/logon': views.login,
# 目录索引
'/index': views.index,
# 获取时间
'/time': views.time
}
9.5业务逻辑
# views.py
# 业务逻辑
from conf import setting
from lib import common
import datetime
# 获取 login.html 的信息
def login():
# 路径拼接 拿到 login.html 的路径
login_html_path = setting.get_html_path('login.html')
# 读取文件
html_msg = common.get_html_msg(login_html_path)
# 返回文件信息
return html_msg
# 获取 index.html 的信息
def index():
# 路径拼接 拿到 index.html 的路径
index_html_path = setting.get_html_path('index.html')
# 读取文件
html_msg = common.get_html_msg(index_html_path)
# 返回文件信息
return html_msg
def time():
# 路径拼接 拿到 time.html 的路径
time_html_path = setting.get_html_path('time.html')
html_msg = common.get_html_msg(time_html_path)
# print(html_msg)
# 获取当前时间
new_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 将页面中的xxx 替换为当前时间
html_msg = html_msg.replace('xxx', new_time)
# 返回文件信息
return html_msg
9.6公共文件
# common.py
# 读取网页信息
def get_html_msg(html_path):
# 以二进制模式读取
with open(html_path, mode='rt', encoding='utf8') as rf:
# 返回 页面信息
return rf.read()
9.7配置文件
# setting.py
# 获取 templates 路径. templates 存取前端页面
import os
# 获取项目的路径
BATH_PATH = os.path.dirname(os.path.dirname(__file__))
# 拼接 templates 的路径
templates_path = os.path.join(BATH_PATH, 'templates')
# 拼接 templates下的 html文件
def get_html_path(html_name):
html_path = os.path.join(templates_path, html_name)
return html_path
9.8前端页面
<!-- login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登入页面</title>
<link rel="icon" href="data:image/ico;base64,aWNv">
</head>
<body>
<h1>登入页面</h1>
</body>
</html>
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>目录索引</title>
<link rel="icon" href="data:image/ico;base64,aWNv">
</head>
<body>
<h1>目录索引</h1>
</body>
</html>
<!-- time.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>获取时间</title>
<link rel="icon" href="data:image/ico;base64,aWNv">
</head>
<body>
xxx
</body>
</html>
<!-- get_dict.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板语法</title>
<link rel="icon" href="data:image/ico;base64,aWNv">
</head>
<body>
<h1>信息</h1>
<!-- 模板语法 -->
{{user }}
{{user.get('username')}}
{{user.age}}
{{user['hobby']}}
</body>
</html>
在浏览器输入127.0.0.1:8080/get_dict
0.程序启动!
1.绑定信息成功!
开始监听...
2.收到请求,启动web_app! # 在这输入
3.请求头信息{...}
4.客户输入的路径/get_dict
5.判断路径是否存在...
127.0.0.1 - - [01/Mar/2022 15:34:19] "GET /get_dict HTTP/1.1" 200 308
<Template memory:23ec6971a90> <class 'jinja2.environment.Template'>
6.路径存在!
7.发送响应通用
8.发送响应体>>:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板语法</title>
<link rel="icon" href="data:image/ico;base64,aWNv">
</head>
<body>
<h1>信息</h1>
<!-- 模板语法 -->
{'username': 'kid', 'age': 18, 'hobby': 'read'}
kid
18
read
</body>
</html>