1 项目介绍
本项目为尚硅谷公开的免费教学项目
1.1 功能
1.2 技术栈
2 改进介绍
由于该项目是教学项目,其中的内容自行查看尚硅谷bilibili账号学习即可
此文章只讲解两处bug的改进
2.1 页面大小修改时出现多次请求的bug
前端在请求时,页面大小修改会发送请求,但此时的页面数并没有改.这样一来.如果原本在第5页每页5条数据,修改为10条后,实际可能根本没有第五页,因此可能造成bug,实机演示中可以发现浏览器出现了不断发请求的情况,页面一直闪烁,说明是一个完全有可能在使用中出现的bug.
演示如下
2.1.1解决方案
把页面大小修改的事件优化一下,在检测到页面大小变化后,先把当前页面置为1,在向后端发起读取新闻的请求,这样一来就一定不会出现上述bug问题
2.1.2 代码实现
script代码如下
<script >
import { getfindNewsPageInfo , removeByHid } from "../../api/index"
import { defineComponent } from 'vue'
export default defineComponent({
name:'HeadlineNews'
})
</script>
<script setup>
import { ref, onMounted, getCurrentInstance, watch } from "vue"
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import pinia from '../../stores/index';
import { useUserInfoStore } from '../../stores/userInfo'
const { Bus } = getCurrentInstance().appContext.config.globalProperties
const userInfoStore = useUserInfoStore(pinia)
const router = useRouter()
const type = userInfoStore.uid
const findNewsPageInfo = ref(
{
keyWords: "", // 搜索标题关键字
type: 0, // 新闻类型
pageNum: 1, // 页码数
pageSize: 5, // 页大小
}
)
const totalSize = ref(0) //分页总数量
// 初始化列表数据
const pageData = ref([{
hid: null,
pageViews: null,
pastHours: null,
publisher: null,
title: "",
type: null
}])
//接收header组件用户搜索的数据
Bus.on('keyword', (keywords) => {
findNewsPageInfo.value.keyWords = keywords
})
// header点击切换高亮的时候传递过来的tid
Bus.on('tid', (type) => {
findNewsPageInfo.value.type = type
})
// 监视初始化参数type的变化,当type发生改变的时候重新发送请求获取列表数据
watch(() => findNewsPageInfo.value, () => {
getPageList()
}, {
deep: true,
})
// 初始化请求分页列表数据
const getPageList = async () => {
let result = await getfindNewsPageInfo(findNewsPageInfo.value)
pageData.value = result.pageInfo.pageData
findNewsPageInfo.value.pageNum = result.pageInfo.pageNum
findNewsPageInfo.value.pageSize = result.pageInfo.pageSize
totalSize.value = +result.pageInfo.totalSize
}
const getPageListWhilePageSizeChange = async () => {
findNewsPageInfo.value.pageNum=1
let result = await getfindNewsPageInfo(findNewsPageInfo.value)
pageData.value = result.pageInfo.pageData
findNewsPageInfo.value.pageNum = result.pageInfo.pageNum
findNewsPageInfo.value.pageSize = result.pageInfo.pageSize
totalSize.value = +result.pageInfo.totalSize
}
// 组件挂载的生命周期钩子
onMounted(() => {
getPageList()
})
// 点击查看全文的回调
const toDetail = (hid) => {
router.push({ name: "Detail" ,query:{ hid }});
}
// 点击删除的回调
const handlerDelete = async (id) => {
await removeByHid(id)
ElMessage.success('删除成功!')
//重新获取列表请求
getPageList()
}
//点击修改的回调
const Modify = (hid) => {
router.push({ name: "addOrModifyNews", query: { hid } });
}
</script>
template代码如下
<template>
<div class="container">
<div class="listItem">
<!-- 每一项头条列表 -->
<div class="containerItem" v-for="item in pageData" :key="item.hid">
<div>
<span class="text">{{ item.title }}</span>
</div>
<div class="detail">
<span>{{ item.type == 1 ? "新闻":item.type == 2 ? "体育": item.type == 3 ? "娱乐": item.type == 4 ? "科技" : "其他" }}</span>
<span>{{item.pageViews}}浏览</span>
<span>{{item.pastHours}}小时前</span>
</div>
<div>
<el-button @click="toDetail(item.hid)" size="small"
style="background: #198754; margin-left: 15px; color: #bbd3dc">查看全文</el-button>
<el-popconfirm v-if="item.publisher == type" @confirm="handlerDelete(item.hid)" :title="`您确定要删除${item.title}吗?`">
<template #reference>
<el-button size="small" style="background: #dc3545; color: #bbd3dc">删除</el-button>
</template>
</el-popconfirm>
<el-button @click="Modify(item.hid)" v-if="item.publisher == type" size="small" style="background: #212529; color: #bbd3dc">修改</el-button>
</div>
</div>
<!-- 分页器 -->
<div style="margin-top: 20px">
<el-pagination
v-model:current-page="findNewsPageInfo.pageNum"
v-model:page-size="findNewsPageInfo.pageSize"
@size-change="getPageListWhilePageSizeChange"
@current-change="getPageList"
:page-sizes="[5,7,10]"
background
layout="prev, pager, next , ->, sizes, total"
:total="totalSize" />
</div>
</div>
</div>
</template>
可以看到,我们为template中的分页器的页面大小事件单独设置了函数getPageListWhilePageSizeChange(在script标签中可以查看),这样当页面大小改变时,就可以单独的为其处理.
2.2 用户可任意修改他人新闻的bug
在修改新闻的接口中,虽然前端页面设置了只有是自己的新闻才给出相应按钮的限制,但如果用户通过浏览器发起请求,只要token对应的用户是登录状态,就能通过后端的过滤器.因此对于新闻的编辑接口(发布 删除 修改)我们只验证了是否登录,但没有验证该新闻的发布者和请求中token对应的用户是否一致.因此将会导致能够任意修改他人的新闻的bug
前端对headline下的资源请求时,会自动的带上token的请求头,根据这一事实,我们再浏览器中向他人新闻发起修改请求,就会测试出该bug
2.2.1 解决方案
此处单对于修改做出方案即可,其他的按照该思路一致编写代码即可.
对于修改新闻的请求,请求会携带新闻的hid,那么我们可以再控制层获取该hid对应的新闻对象,在取出该对象的发布者A1.通过请求的请求头我们拿到token 再根据token获得uid A2 对比A1和A2是否相等即可判断该新闻的发布者和请求中token对应的用户是否一致. 若不一致直接响应未登录即可
2.2.2代码实现
这里给出的代码是 修改新闻的前置请求:数据回显请求接口的控制层代码
/**
* TODO 新闻信息回显接口 主要是供用户点击修改自己的新闻时,在修改页面内展示原有信息使用
* @param req /
* @param resp /
* @throws ServletException /
* @throws IOException /
*/
protected void findHeadlineByHid(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.接收参数 post 键值对 hid=?
int hid = Integer.parseInt(req.getParameter("hid"));
int uid = Math.toIntExact(JwtHelper.getUserId(req.getHeader("token")));
//2.调用服务层 查询新闻
HeadlineDetailVo headlineContent = headlineService.findHeadlineByHid(hid);
/*
虽然前端对于 不是自己的新闻 没有开放修改按钮
但可以通过浏览器输出请求,请求访问他人的新闻,这样一来 用户可以随意地修改别人的新闻
这是比较严重的bug,应当修改
*/
if (headlineContent.getPublisher() !=uid) {
WebUtil.writeJson(resp, Result.build(null, ResultCodeEnum.NOTLOGIN));
}else{
//3.准备结果
Map<String, HeadlineDetailVo> data = new HashMap<>();
data.put("headline", headlineContent);
//4.响应
WebUtil.writeJson(resp,Result.ok(data));
}
}
3 总结
针对第一个问题,即页面大小修改时出现多次请求的 bug,解决方案是优化页面大小修改的事件处理,确保在页面大小变化后将当前页面置为第一页再向后端发起读取新闻的请求,从而避免出现页面数不匹配的情况。
针对第二个问题,即用户可任意修改他人新闻的 bug,解决方案是在控制层对修改新闻的请求进行验证,通过比较请求中的 token 对应的用户和新闻的发布者是否一致来判断是否允许修改,若不一致则直接响应未登录状态。在代码实现上,给出了针对修改新闻的前置请求的数据回显请求接口的控制层代码,通过比较新闻发布者和请求中 token 对应的用户来确保修改操作的合法性。
4 项目获取
bilibili有公开的项目讲解视频