前后端分离优点:
1.各司其职
前端:视觉层面,兼容性,前端性能优化
后端:高并发,高可用,性能
2.解耦,前端后端均易于扩展
3.后端灵活搭配各类前端
4.提高用户体验
5.并行开发,提高开发效率
http无状态?-token
如何解决跨域问题?-CORS
解决csrf问题?-token
动态静态资源分离?-CDN(内容网络分发)
跨域:
flask默认5000端口, 向前端服务器发送请求后,通过ajax往django8000端口获取数据,违背了浏览器的同源策略-----涉及到跨域
跨域解决方案
cross-origin resource sharing CORS跨域资源共享
允许浏览器向跨域源服务器发出请求,克服ajax只能同源使用的限制
特点:
1.浏览器自动完成
2.服务器需要支持
原理:
一 简单请求
满足以下条件的请求为简单请求,否则为复杂请求
1.请求方法如下
GET or HEAD or POST
2.请求头仅包含
Accept
Accept-Language
Content-Language
Content-Type
3.Content-Type仅支持
application/x-www-form-urlencoded
multipart/form-data
text/plain
简单请求跨域流程:
1.请求头仅包含
请求头中携带Origin,该字段表明自己来自那个域
2.响应
如果请求头中的Origin在服务器接受范围内,则返回以下响应头
Access-Control-Allow-Origin 服务器接收的域
Access-Control-Allow-Gredentials 是否接受Cookie
Access-Control-Expose-Headers 如果需要获取其他头,需要在此指定
二 复杂请求or预检请求,跨域流程
1.先发OPTIONS请求,携带如下请求头
Origin 表明此请求来自哪个域
Access-Control-Request-Method 此次请求使用的方法
Access-Control-Request-Headers 此次请求使用的头
2.响应
服务器开始评估
返回以下响应头
Access-Control-Allow-Origin 服务器接收的域
Access-Control-Allow-Methods 服务器接受的跨域请求方法
Access-Control-Allow-Headers 所有支持的头部
Access-Control-Allow-Gredentials 是否接受Cookie
Access-Control-Max-Age OPTION 请求缓存时间,单位s
3.主请求阶段
Origin 表明此请求来自哪个域
4.主请求相应阶段
Access-Control-Allow-Origin 服务器接收的域
配置CORS
配置流程:
1,INSTALLED_APPS 中添加 corsheaders
2,MIDDLEWARE 中添加 corsheaders.middleware.CorsMiddleware
位置尽量靠前,官方建议 ‘django.middleware.common.CommonMiddleware’ 上方
3,CORS_ORIGIN_ALLOW_ALL 布尔值 如果为True 白名单不启用
(34互斥,4为白名单列表)
4,CORS_ORIGIN_WHITERLIST = [
'https://example.com'
]
(预检请求的真实请求方法)
5, CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
)
6, CORS_ALLOW_HEADERS = (
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
)
7,CORS_PREFLIGHT_MAX_AGE 默认 86400s
8,CORS_EXPOSE_HEADERS [] (希望ajax能获取到的头信息)
9,CORS_ALLOW_CREDENTIALS 布尔值,默认False (COOKIE信息)
RESTful设计风格
全称:Representational State Transfer
1、资源(Resources)
网络上的一个实体,或者说是网络上的一个具体信息,并且每个资源都有独一无二的URI与之对应,获取资源=直接访问URI
2、表现层(Representation)
如何去表现资源,即资源的表现形式,如HTML,XML,JPG,MP3,JSON等
3.状态转化(State Transfer)
访问一个URI即发生了一次 客户端和服务端的交互;此次交互将会涉及到数据和状态的变化
客户端需要通过某些方法触发具体的变化 - HTTP method 如GET,POST,PUT等等
特征:
1、每一个URI代表一种资源
2、客户端和服务端之间传递着资源的某种表现形式
3、客户端通过HTTP的几个动作 对 资源进行操作 - 发生 ‘状态转化’
设计原则
1.协议- http/https
2.域名:
域名中体现出api的字样,如
https://api.example.com or https://example.org/api/
3.版本:
https://api.example.com/v1/
4.路径
路径中避免使用动词,资源用名词表示
https://api.example.com/v1/users
5.HTTP动词语义
GET:SELECT 从服务器中取出资源 一项或多项
POST:CREATE 在服务器中新建一个资源
PUT: UPDATE在服务器中更新资源(客户端提供改变后的完整资源)
PATCH: UPDATE在服务器中更新资源(客户端提供改变的属性)
DELETE:DELETE 从服务器中删除资源
6.巧用查询字符串
?limit=10:指定返回数量
?offset=10:指定返回的开始位置
?page=2&per_page=100:指定第几页,以及每页的数量
7.状态码
(1)HTTP响应码表达此次请求的结果
(2)自定义内部code进行响应
返回结构如下{'code': 200,'data':{},'error':xxx }
8.返回结果(实际中还是根据业务来设计)
根据HTTP动作不同,返回结果的结构也有所不同
用户系统-ORM
产品经理 — API文档 具体在达内给的API文档附件中
会话状态保持
Cookies存在浏览器 session存在服务器上
都不是太好的方案
cookie数据串改和session存储压力
在cookie上添加数据校验,保证数据未被串改是最优解---利用jwt
jwt前传---base64
b64encode 将输入的参数转化为base64规则的串
b64decode 将base64串解密为明文
urlsafe_b64encode 作用与b64encode作用相同,但是会将 + 替换成 - 将 / 替换成 _
urlsafe_b64decode 同上
HMAC-SHA256
通过一种特别的计算方式之后产生的消息认证码,使用三列算法同时结合一个加密秘钥。用来保证数据的完整性,同时可以用来做某个消息的身份验证。
自定义一个key 结合明文 生成一个密钥
JWT --- json-web-token
为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准,JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便从资源服务器获取资源
三部分b' 第一部分 . 第二部分 . 第三部分 '
第一部分header
格式为字典,元数据格式如下
{ 'alg' : 'HS256', 'typ':'JWT' }
alg : 算法
HS256 : HMAC-SHA256
typ : token类别,此处必须大写JWT
该部分数据转成json串,并用base64转码得到第一部分
第二部分pay-load
格式为字典- 此部分分为公有声明与私有声明
公有声明 : JWT提供了内置关键字用于描述常见的问题,此部分均为可选项,用户根据自己需求 按需添加key,如:
{
'exp' : xxx,# Expiration Time 此token的过期时间和时间戳
'iss' : xxx,# (issuer) Claim 指明token的签发者
'iat' : xxx, # (Issued At) Claim 指明此创建时间的时间戳
'aud' : xxx,# (Audience) Claim 指明此token签发面向群体
}
私有声明 : 用户根据自己的业务需求添加自定义的key
{'username' : 'cs'}
第三部分 signature---签名
根据haeder中的alg确定具体算法
HS256(自定义的key,base64后的header +‘.’+base64后的payload)
JWT检验规则
1,解析header,确认alg
2,签名校验 - 根据传过来的header和payload按照alg指定的算法进行签名,将签名结果和传过来的sign进行对比,对比一致则校验通过
3,获取payload自定义内容
代码层面,python
pip3 install pyjwt
encode(payload,key,algorithm)
decode(jwt,key,algorithm)
import jwt
code = jwt.encode({'username': 'cs'}, 'cs123456', algorithm='HS256')
print(code)
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImNzIn0.J8vGzKLldkRZpT6KIXi-9kw2-WoaGJody9Qyg6BH5d8
import jwt
code = jwt.encode({'username': 'cs'}, 'cs123456', algorithm='HS256')
content = jwt.decode(code, 'cs123456', algorithms='HS256')
print(content)
# {'username': 'cs'}
特殊说明
如果在encode的时候payload添加了exp字段,exp字段需要当前时间戳+此token的有效时间,
在执行decode的时候,若检查到了exp字段,且token过期,则抛出jwt.ExpiredSignatureError
import jwt
import time
code = jwt.encode({'username': 'cs', 'exp': time.time() + 3}, 'cs123456', algorithm='HS256')
print(code)
time.sleep(5)
content = jwt.decode(code, 'cs123456', algorithms='HS256')
print(content)
# ERROR: jwt.exceptions.ExpiredSignatureError: Signature has expired
前后端分离场景下,使用JWT
原则:
1,JWT签发后,交由浏览器保存
2,浏览器可将其存储在‘本地存储’中(防止cookie遭受CSRF攻击,本地存储不会自动提交,需要手动提交)
3,需要 用户登录 才能使用的功能,前端ajax中需要将jwt传至后端;可放在请求头中发送
## 第三方短信平台
需要接入其他平台,让平台帮忙发短信,该服务通常是有偿服务
容联云服务
给URL发Http请求
celery
sudo pip3 install celery
发短信需要请求第三方平台,若第三方平台出现问题,服务器也将出现卡顿。
采用生产者消费者模型,提高项目的可扩展性,横向拓展,保障业务的能正常运行,降低阻塞的可能性
生产者 put -> 队列 -> get 消费者

celery是一个简单,灵活可靠的,处理大量消息的分布式系统
它专注于实时处理任务的,同时也支持任务调度 ,RQ,redis
broker - 消息传输的中间件,生产者一旦有消息发送,将发至broker;
backend - 用于存储消息/任务结果,如果需要跟踪和查询任务状态,则需要添加相关配置
worker - 工作者 执行broker中的消息/任务的进程
如何判断是否采用celery
1.是否可能产生阻塞
2.请求需要的响应是否实时,快速
# 使用 -- 难点:worker如何进行处理
from celery import Celery
app = Celery('cs',broker='redis://password@127.0.0.1:6379/0')
# 创建任务函数
@app.task
def task_test():
print('task is running...')
# 启动worker
# ubuntu终端中,tasks.py 文件的同级目录下执行
# celery -A tasks worker --loglevel = info
# 此模式默认为前台启动,终端中会输出相关日志
# 生产者调用
from tasks import task_test
task_test.delay() # 推送至消息队列中,由消费者执行
# worker前台
[2022-07-26 12:44:54,244: INFO/MainProcess] Task tasks.task_test[01f2f6b8-1b3a-40db-b308-559b381987e0] received
[2022-07-26 12:44:54,247: WARNING/ForkPoolWorker-8] test is runing
[2022-07-26 12:44:54,249: INFO/ForkPoolWorker-8] Task tasks.task_test[01f2f6b8-1b3a-40db-b308-559b381987e0] succeeded in 0.002011182999922312s: None
# 存储执行结果-worker
from celery import Celery
app = Celery('cs', broker='redis://:@127.0.0.1:6379/1', backend='redis://:@127.0.0.1:6379/2')
@app.task
def task_test(a, b):
print('test is runing')
return a + b
# worker执行的返回值会作为结果自动存入到backend中
from tasks import task_test
task_test.delay('cs', 'nb')
# worker前台显示
[2022-07-26 13:02:20,273: INFO/MainProcess] Task tasks.task_test[ecb1b515-b62e-468a-be3c-af40a550ac65] received
[2022-07-26 13:02:20,274: WARNING/ForkPoolWorker-8] test is runing
[2022-07-26 13:02:20,279: INFO/ForkPoolWorker-8] Task tasks.task_test[ecb1b515-b62e-468a-be3c-af40a550ac65] succeeded in 0.004370476000076451s: 'csnb'
# redis库中
127.0.0.1:6379[2]> keys *
1) "celery-task-meta-2516cdbb-0aa4-4e17-a96e-a2ed28774964"
127.0.0.1:6379[2]> get "celery-task-meta-2516cdbb-0aa4-4e17-a96e-a2ed28774964"
"{\"status\": \"SUCCESS\", \"result\": \"csnb\", \"traceback\": null, \"children\": [], \"date_done\": \"2022-07-26T05:04:45.279617\", \"task_id\": \"2516cdbb-0aa4-4e17-a96e-a2ed28774964\"}"
django中使用celery
# 在settings同级目录中,创建celery.py编写初始化代码
from celery import Celery
from django.conf import settings
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dadablog.settings')
app = Celery('dadablog')
app.conf.update(
broker_url='redis://:@127.0.0.1:6379/1'
)
# 自动到settings中已注册的app中寻找worker函数
app.autodiscover_tasks(settings.INSTALLED_APPS)
# 在对应的应用中创建tasks.py,编写worker函数
from django.conf import settings
from tools.sms import YunTongXin
from dadablog.celery import app
@app.task
def send_sms_c(phone, code):
yun = YunTongXin(**settings.YUN_CONFIG)
res = yun.run(phone, code)
return res
# 在视图函数中需要生产的时候,使用worker函数.delay()代替直接调用
# 发送验证码
# send_sms(phone, code)
send_sms_c.delay(phone,code)
# 进入manage.py同名目录下 启动celery服务celery -A dadablog worker -l info
# celery自动寻找到worker函数
[tasks]
. user.tasks.send_sms_c
# 并且提示在django环境下,DEBUG=True会出现内存泄露,所以在正式项目中,必须设置为false
正式环境后台启动celery
nohup celery -A proj worker -P gencent -c 1000 > celery.log 2>&1 &
nohup:忽略所有挂断(SIGHUB)信号
&:表示命令在后台执行
celery默认是开多进程,在python中,用协程并发厉害点,可以用-P参数 gevent开启协程运行celery -c 指定具体的协程数量
> celery.log 就是将命令的输出放入.log文件中,在哪执行命令,.log文件就生成在哪
> 2代表标准错误输出,1代表标准输出, 2>&1 就是将错误输出重定向给标准输出
> 意思就是在这个celery.log文件中 不仅有正常输出,还有报错信息等输出