框架引入
# 知识储备
对 业务状态码、日志查询 的初次了解!! (⁎⁍̴̛ᴗ⁍̴̛⁎)
前端 - 后端 - 数据库
HTTP协议
■ 四大特性
1> 基于请求响应
2> 基于TCP/IP协议之上的应用层协议
3> 无状态 -- 不能保存用户信息
Ps:解决方向 cookie、session、token
4> 短链接 -- 发送请求,响应后断开.
Ps:长链接建立链接在一定时间C/S之间没有请求响应才会断开,应用场景>即时通信(聊天)!
■ 请求数据格式
请求首行 (请求方式、http版本..)
请求头
\r\n
请求体 注意!get没有、post有
■ 响应数据格式
响应首行 (状态码)
响应头
\r\n
响应体
■ 响应状态码 -- 是http协议规定的!
200 请求成功,ok
404 请求资源不存在
500 服务器内部错误
★ PS:业务状态码 (一般只规定一个正确的,比如规定200是正确的,其余的全部为异常)
以登陆为例
1.用户名不存在
2.验证码错误
1) 服务端随机生成 通过阿里云的接口发送短信
情况一:限制用户一天只能发送3次验证码.
服务端会将随机生成的验证码存入数据库(mysql、redis)中!
表结构 id code phone create_time
会通过sql语句判断数据库中同一phone是否有了3条数据.
情况二:不限制用户一天当中发送的次数.
会将随机生成的验证码放到session中!
2) 校验验证码
3.密码错误
伪代码如下:
def func():
user_dic = {'code':1001,'msg':'验证码错误','data':[]}
user_dic = {'code':1002,'msg':'用户名不存在','data':[]}
user_dic = {'code':1003,'msg':'密码错误','data':[]}
# -- 若都正确
user_dic = {'code':200,'msg':'ok','data':[]}
json.dumps(user_dic).encode('utf-8')
Ps:若业务系统很多,crm、财务、销售、主站,我们可以规定多位状态码的前两位来表示哪个系统!
11、22、33、44 便于我们定位错误信息!!
https和http都是超文本传输协议,前者比后者更加安全!
简单理解就是https需要申请证书,该证书是配置到nginx服务器上的,我们想拿数据时,需要与第三方比对公钥和私钥!
url"统一资源定位符,即网址" = 协议 + 域名 + 路径 + get的请求参数"?k1=v1&k2=v2"
题外话:关于日志的查询 进阶程序员需要晓得的东西..(つД`)ノ
日志量比较小,存放到数据库中
日志量很大,用ES查询
ES都不够用啦,从主表中移除数据进行日志备份、归档
-- 文件归档
-- 时间归档
-- (归档有个问题,看不到近期的订单啦)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# web框架
# 小试牛刀
需求:在网址里输入不同的后缀,浏览器页面打印出不同的内容
"""
需求:输入不同的后缀,返回不同的内容
1.知道用户输入的后缀
2.做判断(伪代码如下)
if path == '/index':
:return 'index' # -- 简单的返回一个字符串
:return index() # -- 复杂的逻辑,将结果返回
"""
import socket
server = socket.socket()
server.bind(("127.0.0.1", 8080)) # S端的 IP/哪台电脑 和 端口/电脑上的哪个程序
server.listen(5) # 允许多少人等待
while True:
conn, addr = server.accept() # 1.阻塞,等待链接
# -- 2.接收浏览器发过来的数据
data = conn.recv(1024) # -- 小而多,多分段 ->粘包
"""
需求分析:
在浏览器端输入 http://127.0.0.1:8080/index/
在服务端可以通过print(data)拿到结果
"""
# b'GET /index/ HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnec...
print(data)
data = data.decode('utf-8')
current_path = data.split()[1] # -- 取['GET','/index','...']列表第2个元素
# -- 3.给浏览器返回数据
# -- 表明是基于http协议的!
conn.send(b"HTTP/1.1 200 ok \r\n\r\n")
# -- 判断,并向浏览器发送数据
if current_path == "/index/":
conn.send(b"index")
elif current_path == "/login/":
conn.send(b"login")
else:
conn.send(b'404')
conn.close() # 4.断开连接
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# wsgiref模块
该内置模块了解即可! 其内部本质帮我们写了socket服务端!底层已经写了.
我们使用该模块实现上面的需求!
# 简易版本
from wsgiref.simple_server import make_server
def run(env, response):
"""
:param env: 帮我们封装了http请求过来的所有数据,表现形式为字典格式!
:param response: 返回给客户端的响应response
:return: 返回值
"""
current_path = env.get('PATH_INFO')
response('200 ok', []) # -- 表明是基于http协议的,放在哪都行
if current_path == '/index/':
return [b'index']
elif current_path == '/login/':
return [b'login']
return [b'404']
if __name__ == '__main__':
# -- 相当于帮我们实现了socket服务端,帮我们实时监听127.0.0.1:8080
# 当请求来的时候,全部交过run方法处理!run方法有两个参数.
server = make_server('127.0.0.1', 8080, run)
server.serve_forever() # -- 启动服务端
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 优化版本
可以将index、login函数放到一个名为views.py视图函数文件,urls放到一个名为urls.py路由文件
run方法和剩下的main代码构成启动文件!!
启动文件就不用改动啦! 在urls.py和views.py中添加路由和对应的函数即可!!
reg.html叫做模版文件!!
urls.py -- 后缀 -- 书写路由/后缀与函数的对应关系
views.py -- 函数 -- 处理数据的逻辑
templates -- 模版 -- 存储html文件
start.py / manage.py -- 启动文件
若需要加功能,只需要在urls.py中添加一个路径,然后在views.py中写逻辑就可以啦!!
from wsgiref.simple_server import make_server
def index():
return 'index'
def login():
return 'login'
def reg():
with open('reg.html','r') as f:
data = f.read()
return data
urls = (
('/index/', index),
('/login/', login),
('/reg/', reg),
)
def run(env, response):
response('200 ok', [])
current_path = env.get('PATH_INFO')
func = None
for url in urls:
if current_path == url[0]:
func = url[1]
break
if func:
res = func()
return [res.encode('utf-8')]
else:
return [b'404']
if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run)
server.serve_forever()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# Python主流框架
Django
特点:大而全,重量级框架
缺点:笨重
flask
特点:小而美,轻量级框架
缺点:依赖于第三方的模块
tornado
特点:异步非阻塞,支持高并发,甚至可以用于充当游戏服务器
缺点:难
还有一些占比较小,但是也很厉害的框架 fastapi、sanic..
题外话,项目比较大,用flask的话,需要用到很多第三方的组件,有多个组件也可以实现同一个功能,选择哪个?以及与flask版本是否适应?很痛苦.
同步框架VS异步框架
- 异步非阻塞:tornado、sanic、fastapi
django也支持但支持的不好
- 同步:django、flask、bottle、webpy
目前不看好异步框架,用异步框架的很小众,因为:
1> 增加编程的难度,生态不完善
比如fastapi实现异步功能,在遇到IO操作时前面得加关键字awiait. 不追求技术的公司,更在乎的快速.
完成该异步功能,使用的第三方组件. 遇到问题,没有更多的解决方案.需要自己阅读源码.
2> 项目中不会有那么多IO功能 (一般指网络IO等待). 遇到了,Django使用celery就能解决!!
谈一谈fastapi.
1.参考了flask,使用过flask的可以很快的上手
2.使用了python最新的注解功能
3.可以快速生成restfulAPI,(这不是fastapi特有的,Django使用第三方组件也可以)
4.异步
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Django安装与创建
# 安装
django版本
1.x 2.x 3.x
1.x发布时间是2018年
2.x和3.x的区别不大!!
django3.X:自带异步功能
django2.X:默认不支持异步
django1.X:默认不支持异步
'''
学习基于django1.X版本即可
原因:
老项目中使用频率最高、最广泛的版本!!!
与2.X功能几乎一致,后续会讲解两者区别之处
与3.X最主要的区别在于新增了一个异步的功能
'''
命令行输入命令pip install django==1.11.29 或者 通过pycharm安装
C:python39
- python.exe
- Scripts
- pip.exe
- django-admin.exe
- Lib
- re.py
- random.py
- site-packpage
- django=3.2
- ...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 创建
注意! 一个pycharm窗口只打开一个django项目!!
说在前面,在mac上,默认在终端生效的是python2.7,我们要先让python3生效需要进行如下配置!
每次打开新的终端都要执行 source .bash_profile
, 永久生效没研究..因为更多时候是通过pycharm创建!
# cmd终端
source .bash_profile
cd Desktop # -- 想要在哪个目录下创建项目就切换到哪个目录下
django-admin startproject mysite # -- 在桌面创建名为mysite的django项目
cd mysite # -- 切换到项目目录下,即与manage.py的同级目录之下
python3 manage.py runserver # -- 启动项目,默认端口8000
# 当然可以指定端口
# python3 manage.py runserver 127.0.0.1:8001
# !! 当然可以先创建app应用后再启动项目.
# python3 manage.py startapp app01
# -- 何为应用?
# 若将项目比作一所大学,那么应用就相当于二级学院!
# 以淘宝工程为例,它应该有一系列的应用.
# 用户相关 user
# 订单相关 order
# 地址相关 address
# 商品相关 goods
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
访问127.0.0.1:8000,展示以下内容,表明项目创建启动成功!
# pycharm创建
通过pycharm方式的创建和通过cmd的创建的区别:
1> 前者多一个templates文件夹!
2> 前者settings.py中TEMPLATES部分的DIRS属性会自动写好.
3> 前者创建项目时创建的app会自动注册到INSTALLED_APPS中.
特别强调: 创建完应用后,必须在INSTALLED_APPS中注册应用!!!
mac通过pycharm创建时,自动填充的是这样的 -- 'DIRS': [BASE_DIR / 'templates']
导致项目启动不起来!直接报错
'DIRS': [BASE_DIR / 'templates']
TypeError: unsupported operand type(s) for /: 'str' and 'str'
解决方案 -- 将其改为 'DIRS': [os.path.join(BASE_DIR,'templates')]
2
3
4
5
# 项目文件介绍
mysite # -- 项目名
├── app01 # -- 应用名
│ ├── __init__.py
│ ├── admin.py # -- 后台管理
│ ├── apps.py # -- 跟注册相关,暂且忽略
│ ├── migrations # -- 数据库的迁移记录
│ │ └── __init__.py
│ ├── models.py # -- 模型层,跟数据库打交道的!
│ ├── tests.py # -- 测试文件
│ └── views.py # -- 视图层
├── manage.py # -- 入口文件,启动文件
├── mysite
│ ├── __init__.py
│ ├── __pycache__
│ ├── settings.py # -- 程序启动时,会先读取django内部配置,再读取该settings.若重复,覆盖.以settings中的为主.
│ ├── urls.py # -- 路由与视图函数的对应关系,总路由层/主路由
│ └── wsgi.py # -- 与wsgiref模块相关,同步的,充当socket服务器.
└── templates # -- 模版层,存储html文件的
Ps:运行项目后会自动创建db.sqlite3,这是django自带的小型数据库!!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 补充
# 了解Django
B/S -- 浏览器作为客户端,开发的“网站/web应用”作为服务端.(简单理解就是,浏览器发送请求,从服务端拿到资源.)
C/S -- 安装在电脑上的应用软件作为客户端,对应的服务器作为服务端.
无论是B/S,还是C/S架构,本质上是通过socket实现网络通信!
在前面小试牛刀那,服务端accept等待连接,浏览器输入网址127.0.0.1:8080
-- 浏览器向服务端发送请求,发送给服务端的具体数据如下:
第一行是请求头首行
往下到 \r\n\r\n 都是请求头,包含浏览器的一些信息
若发送的是post请求,\r\n\r\n再往下是请求体数据,eg:username='dc'&password='123456'
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: "Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"\r\n
sec-ch-ua-mobile: ?0\r\n
sec-ch-ua-platform: "macOS"\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\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,ga;q=0.7\r\n
Cookie: sessionid=wbq858wf6byt2yklu4p4tpt7rm6v2o7d; csrftoken=PgQaxtc0LEHKbs0a5rcC6et9U1fKX0LKjuGHSya72mnzdYQm81i9UqEOwSle6Mpn\r\n\r\n'
关键点:
http的应用 - 浏览器向服务器发送请求,就是按照http协议来的.
GET请求 -- 只有请求头没有请求体 请求数据在url中
POST请求 -- 既有请求头又有请求体 请求数据在请求体中
注: 请求头之间用/r/n分割,请求头与请求体之间用/r/n/r/n分割!
★ 一次请求和一次响应后,断开连接.(一问一答) -> 短连接 -> 无状态
eg:输入用户名和密码后,后端返回"欢迎xxx",连接断开,该用户再次连接的时候,后端是不知道该用户是谁的!!
解决方法 - 在请求头中加入cookie!
参考链接: https://bbs.huaweicloud.com/blogs/285330
-- HTTP 的 Keep-Alive 也叫 HTTP 长连接, 该功能是由「应用程序」实现的
可以使得用同一个 TCP 连接来发送和接收多个 HTTP 请求/应答,减少 HTTP 短连接带来的多次 TCP 连接建立和释放的开销/反复连接的消耗.
web 服务软件一般都会提供 keepalive_timeout 参数, 用来指定 HTTP 长连接的超时时间.
-- TCP 的 Keepalive 也叫 TCP 保活机制,该功能是由「内核」实现的
当客户端和服务端长达一定时间没有进行数据交互时,内核为了确保该连接是否还有效
就会发送探测报文,来检测对方是否还在线,然后来决定是否要关闭该连接.
HTTP长连接的本质还是HTTP协议,工作模式依旧是一问一答.
即:客户端发起一次请求,服务器回应最多一次响应.这个本质并没有得到改变,改变的只是在同一个TCP连接上可以进行多次请求和多次响应.
Websocket协议跟http协议不一样,客户端可以只请求一次服务器,然后服务器返回多次响应.
即:当连接建立之后,服务器可以主动给客户端发送信息,这点是HTTP做不到的.
当然了,为了检查Websocket连接是否还在,前端会使用心跳检测,但这不影响当连接建立之后,服务器可以主动给客户端发送信息的本质.
Ps:频繁的收发数据的时候会用到websocket,有一定的实时性,比如腾讯文档.
无论是http、https,还是websocket协议.都依赖于socket套接字层来实现.即以什么格式接受发数据等.
像游戏类的应用对性能要求比较高,会基于socket自己来编写协议!!
即无论啥协议都是在socket的基础上构建出来的!!!
通常web应用程序会包含三部分:
1.socket服务器
2.web框架
浏览器一访问,会发很多请求头请求体这些请求相关的数据,请求头数据不是/r/n分割的嘛
Django会帮忙进行处理,封装打包到request对象中!
即request封装了浏览器访问咱们网址时所带的浏览器信息或数据.
3.业务功能
像Django、flask、tornado只实现了web框架的功能.
而对于接受用户请求、返回数据,框架内部一般不编写,用的是现成的第三方的工具来编写.eg:uwsgi、werkzurg等.
Ps:tornado自己独立编写了一个实现该功能的模块(本质都是用socket实现的!!),当然可用自己的,也可以用第三方的.
注:安装Django时会自动安装wsgiref模块用于本地开发,项目部署的时候用的是性能更好的uwsgi.
Q:那不同的第三方模块编写的socket服务端是如何与同一个web框架无缝对接的呢?
A:不同的第三方模块编写的socket服务端会约定俗成的将接口所需参数定义成一样的!!
比如,web框架只需将ip和端口交给它就行了.
- wsgiref
server = make_server('127.0.0.1', 8080, run)
server.serve_forever()
- werkzurg
run_simple('127.0.0.1', 4000, application)
run函数和application都只需要environ、start_response参数.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
要想彻底了解异步非阻塞的框架,需要知道 socket、多线程、多进程、IO多路复用是怎么一回事.
# 创建指定版本的Django
运用pycharm自动创建的Django项目是最新版本的!! 我们想指定版本, 得这样做!
pip list
; pip install django==3.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
;
django-admin startproject day02 .
(.保证了在项目根目录下创建了day02,不加就变成了day02/day02/day02)
ok, 此时就可以点击绿色按钮,快速启动项目啦!!!
也许会报错: ModuleNotFoundError: No module named 'settings'
. 如何解决呢?
settings.py所在的文件夹右键 - 将目标标记为 - 源代码根目录..
# 多app
记得进行app注册 , 'apps.api.apps.ApiConfig'
或者 'apps.api'
都能成功运行.
# 纯净版Django
INSTALLED_APPS = [
# 'django.contrib.admin',
# 'django.contrib.auth',
# 'django.contrib.contenttsypes',
# 'django.contrib.sessions',
# 'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', # 在前后端分离项目中,它也用不到,应注释掉
# 'django.contrib.auth.middleware.AuthenticationMiddleware',
# 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'day02.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
# 'django.contrib.auth.context_processors.auth',
# 'django.contrib.messages.context_processors.messages',
],
},
},
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 拿到别人的项目
可以自动生成requirements.txt文件!!
(.venv) DC的MacBook:day02 One_Piece$ pip freeze > requirements.txt
别人拿到我们写的项目 (.venv里的不用给,因为虚拟环境中像什么内置模块还是用的系统py解释器里的).
step1: 创建虚拟环境.
或者选择上图中的现有环境,在里面找.进行关联 通过virtualenv ccc
命令可将虚拟环境创建在项目根目录下.
注意: 创建后, 点击终端没有进入虚拟环境.. 看看 编译器右下角选没选 python解释器!!!
有些时候,会犯病,明明新创建虚拟环境了..就是识别不到.
这个时候你别慌,随便打开个项目文件. 会提示 是否将 ... 作为环境. 你点击确认即可.
还是不成功,就只能用conda来解决了.
2
3
step2: 直接在终端运行命令一下命令安装依赖:
(.venv) DC的MacBook:day02 One_Piece$ pip install -r requirements.txt
关于这个,可以一劳永逸的配置下载源!
linux下,修改 ~/.pip/pip.conf (没有就创建一个) ,修改 index-url 为国内镜像地址,内容如下:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
2
3
4
5
6
7
step3: 依赖安装完后尝试运行项目.