文章目录
昨日回顾
- 装饰器修复技术如何实现? 装饰器修复技术解决了什么问题?
- token 失效后如何解决? 无感知刷新token的思路是什么?
- JWT禁用的应用场景有哪些?
今日内容
- 我的页面实现
- 用户信息修改
- 用户头像上传
- CDN介绍
- 主页频道
1. 我的页面实现
1.1 我的页面整体分析与接口设计
我的页面包含:
用户头像,
用户昵称,
发布的头条数,
关注人数,
粉丝数,
获赞数
浏览历史
我的收藏
消息通知
用户访问"我的"页面时,完成登录后,必须返回以上相关数据。
接口设计:
进入我的页面时,强制用户登录;
登录后,加载用户的信息,并展示。
请求地址url: /v1/users/userInfo/
method: GET
参数:请求头jwt token
接口描述: 加载用户信息
请求参数:
- headers
- Authorization: Bearer eyJ0eXAiO....
返回数据:
名称 数据类型 是否必须 默认值 描述
message String 必须 提示信息
data, {用户信息}
状态码:
- 400: 请求参数错误
- 401: 用户认证失败
- 200: 请求成功, OK
1.2 后端实现数据返回
思路: 后端验证token,如果token合法, 将user_id放置在g对象中, 视图中从g对象中取出user_id,查询用户数据并返回。
# 用户蓝图 user.py
from flask_restful import Resource, Api
from flask_restful import reqparse
from flask import g, Blueprint
from common.models import db, User
# from common.utils.qiuniu_storage import upload
user_bp = Blueprint("user", __name__)
api = Api(user_bp)
# 用户的蓝图中,定义用户信息视图
class UserInfoResource(Resource):
def get(self):
# 获取用户id
user_id = g.get('user_id')
user = User.query.filter_by(uid=user_id).first()
data = {
'id': user.uid,
'name': user.username,
'photo': user.profile_photo,
'is_media': user.is_media,
'intro': user.introduction,
'certi': user.certificate,
'art_count': user.article_count,
'follow_count': user.following_count,
'fans_count': user.fans_count,
'like_count': user.like_count
}
return {"code": 200, 'message': 'ok', 'data': data}
user_bp = Blueprint('user_bp', __name__)
api = Api(user_bp)
api.add_resource(UserInfoResource, "/v1/users/userInfo/")
1.3 前端数据展示
Home.vue组件中,实现接口加载数据,并展示:CORS(app)实现跨域
getUserInfo(){
this.axios.get("/v1/users/userInfo/", {
headers: {
Authorization: "Bearer " + localStorage.token
},
responseType: "json"
})
.then(res => {
console.log("加载用户信息:", res)
this.nickname = res.data.data.username
this.articles = res.data.data.article_count
this.focus = res.data.data.following_count
this.fans = res.data.data.fans_count
this.support = res.data.data.like_count
})
.catch(err => {
console.log("记载用户信息错误:", err)
})
},
2. 用户信息修改
2.1 接口设计
前端点击编辑资料
,进入个人信息页,用户修改信息, 包含了用户头像,用户昵称等
信息修改的后端接口设计:
地址,/v1/users/userInfo/
请求方法, PUT
接口描述,用户信息更新
参数, 每次提交一个更新的字段 & JWT token
响应,json数据
2.2 后端更新接口实现
class UserInfoResource(Resource):
# ...
def put(self):
request_parser = RequestParser()
request_parser.add_argument("profile_photo", type=str)
request_parser.add_argument("username", type=str)
request_parser.add_argument("introduction", type=str)
request_parser.add_argument("mobile", type=str)
request_parser.add_argument("email", type=str)
args = request_parser.parse_args()
profile_photo = args.get("profile_photo")
username = args.get("username")
introduction = args.get("introduction")
mobile = args.get("mobile")
email = args.get("email")
user_id = g.uid
user = User.query.filter(User.uid==user_id).first()
attr_list = ["profile_photo", "username", "introduction", "mobile", "email"]
for idx, val in enumerate([profile_photo, username, introduction, mobile, email]):
print("before: ", idx, val)
if val:
# 更新
setattr(user, attr_list[idx], val)
db.session.commit()
return {
"code": 200,
"msg": "更新成功",
"data": marshal(user, resource_fields)
}
后端无法接收到前端的json数据时, 前端需使用qs编码:
npm install -s qs
qs.stringify({key: value}) //对象序列化为url形式
qs.parse(url) // 将url解析为对象
2.3 前端更新接口实现
组件:EditInfo.vue
// 组织json数据
orgData() {
let data = {}
switch(this.key){
case "profile_photo":
data = { profile_photo: this.update_value };
break;
case "username":
data = { username: this.update_value };
break;
case "introduction":
data = { introduction: this.update_value };
break;
case "mobile":
data = { mobile: this.update_value };
break;
case "email":
data = { email: this.update_value };
break;
}
return data
},
// 文本框的失去焦点事件
blurEvent() {
this.update_value = document.querySelector("#textArea").value;
console.log("失焦事件处理", this.update_value);
},
// 更新用户的信息
updateUserInfo() {
console.log("更新中...", this.key);
let data = this.orgData()
this.axios
.put("/v1/users/userInfo/", qs.stringify(data), {
headers: {
Authorization: "Bearer " + localStorage.token,
},
responseType: "json",
})
.then((res) => {
console.log("更新的响应:", res);
if(res.data.code == 200){
this.drawer = false
// 删除集中式状态管理中的username
this.$store.state.userInfo.username = null
}
})
.catch((err) => {
console.log("更新的错误:", err);
});
},
3. 用户实现上传头像
3.1 上传头像思路分析与接口设计
- 更新图片, 前端需上传图片
- 后端接收并保存上传的图片,返回响应
- 前端上传成功的回调中,处理头像的路径
- 前端点击确定,请求后端,更新数据库
上传的接口
请求地址:/v1/users/avatar/
method: POST
上传数据: {Authorization:xxx, file: xxxx}
响应数据:{code:xx, msg: xxx, profile_photo: xxxx}
更新的接口
:
重用 2.2部分 的接口
3.2 上传头像接口编写
- 后端接口
# 后端接收数据 FileStorage对象
# file = request.files.get("file")
# file.filename file.stream.read()
class AvatarResource(Resource):
def post(self):
# 1. 接收数据
token = request.form.get("Authorization")
file = request.files.get("file")
print("查看数据:", dir(file), type(file))
# 2. 验证token
# 3. 保存上传的图片
static_path = "static/images/avatar"
if not os.path.exists(static_path):
os.makedirs(static_path)
with open(static_path + "/" + file.filename, "wb") as f:
f.write(file.stream.read())
# 4. 更新数据库
user = User.query.filter(User.uid==1).first()
user.profile_photo = "/" + static_path + "/" + file.filename
db.session.commit()
return {
"code": 200,
"msg": "上传图片成功",
"profile_photo": user.profile_photo
}
api.add_resource(AvatarResource, "/v1/users/avatar/")
- 前端接口
//上传的组件
<el-upload
:action="host + '/v1/users/avatar/'"
:data="uploadData"
:auto-upload="true"
:on-success="uploadSuccess"
:on-error="uploadError">
<el-button type="primary" size="mini">选择图片</el-button>
</el-upload>
//上传成功的回调
uploadSuccess(res, files){
console.log("上传成功的响应:", res, files)
# 更新集中状态管理
this.$store.state.userInfo.profile_photo = res.profile_photo
},
uploadError(err){
console.log("上传错误:", err)
},
4. CDN介绍
- CDN介绍
CDN,全称:Content Delivery Network,内容分发网络。
将源站内容分发至最接近用户的节点,使用户可就近取得所需内容,提高页面响应速度。解决因分布、带宽、服务器性能带来的访问延迟问题,适用于站点加速、点播、直播等场景。
- CDN基本原理
尽可能避开互联网上有可能影响数据传输速度和稳定性的环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的智能虚拟网络,CDN系统能够根据网络流量、各节点的连接、负载状况、用户的距离、响应时间等综合信息,将用户的请求重新导向离用户最近的服务器。
5. 主页频道
5.1 频道接口分析与设计
- 接口设计
请求地址url: /v1/channels/
Method: GET
请求参数:
jwt
接口描述:获取全部频道
响应状态码:
400: 请求错误
200: 请求成功, OK
响应数据:
{
‘code’: 200,
‘msg’: ‘ok’,
‘channels’:[
{‘id’:1, ‘name’: ‘python开发’},
{‘id’:2, ‘name’: ‘人工智能’},
…
]
}
5.2 频道接口实现
后端接口实现
# 2.接口实现:
from flask_restful import Resource
from common.models import Channel
from common.models import db
# 频道列表
class ChannelListResource(Resource):
"""
获取所有频道
"""
def get(self):
# 添加数据用于测试
# 查询数据
channels = Channel.query.all()
return {
'code': 200,
'msg': 'ok',
'channels':[{'id': item.cid, 'name': item.cname} for item in channels]
}
前端接口实现
在这里插入代码片