宝塔插件vue开发踩坑 FormData、CSRF
前言
最近开发了几个宝塔插件,后端都是使用宝塔官方标准python+shell脚本编写,宝塔官方demo前端是简单html的,之前有引入vue.js方法开发但是html文件到最后3000多行emmmm,开发中有时候为了改一个小小的地方鼠标滑到头然后又要滑到尾感觉开发起来不是很便利所以最近想改造一下使用vue-cli作为前端展示。但是中途遇到了一个又一个坑,最终还是一一解决。
一号坑 js文件404
刚刚开始vue搭建很顺利,npm run dev 也能正常访问,但是当我打包后放到服务器问题来了,首先是报一个jqruey错误,重新打包几次后发现多了一个错误那就是所有js文件都是404,没办法各种百度各种折腾。搞了好几个小时最后想到会不会是vue打包输出路径配置问题(先不考虑JQ报错),抱着试一试的心态改了一下配置文件果然有效。【最后JQ报错问题也不见了一键解决啊】
配置修改详情:
// config/index.js文件
build: {
// Template for index.html
index: path.resolve(__dirname, '../../index.html'), //index.html生成到上2级目录中
// Paths
assetsRoot: path.resolve(__dirname, '../../'), //root相对目录
assetsSubDirectory: './static', //静态文件生成位置 这里很重要
assetsPublicPath: './xxxx', //你的插件目录(静态文件相对目录) 这里很重要
二号坑 CSRF
当我配置一切都完成的时候,二号坑来了,那就是请求的时候会提示 CSRF验证失败,请重新登录宝塔,这个是CSRF防跨站的token配置,本来以为有laravel开发的基础这个东西应该不是很难,结果各种百度各种搜索还是没有找到正确的答案,最后扒了宝塔官方的public.js文件找到了解决方法。其他网站一般只验证一个token 但是宝塔后端会验证2个,我在没有扒官方源码的时候只通过cookies方法拿到cookie-token 最后才知道还有一个叫http-token的东西是输出到页面上的,需要使用js读取过来。最终的解决方案如下,在axios的http请求拦截器中获取一下cookie-token和http-token然后写到默认请求头里即可。
// http请求拦截器
axios.interceptors.request.use(
config => {
//解决跨站CSRF问题
function getCookie (b) {
var a, c = new RegExp("(^| )" + b + "=([^;]*)(;|$)");
if (a = document.cookie.match(c)) {
return unescape(a[2])
} else {
return null
}
}
var request_token_ele = document.getElementById("request_token_head");
if (request_token_ele) {
var request_token = request_token_ele.getAttribute('token');
if (request_token) {
config.headers['x-http-token'] = request_token
}
}
request_token_cookie = getCookie('request_token');
if (request_token_cookie) {
config.headers['x-cookie-token'] = request_token_cookie
}
return config
},
error => {
return Promise.reject(error)
}
)
三号坑 普通ajax提交后台无法获取到数据
当一切都就绪的时候,该请求后台api了之前都是使用JQ请求的,vue里当然要使用axios请求后端,所以老代码复制过来进行了一些改造,然后测试,但是遇到一个有史以来最大的坑那就是前台使用axios发起post请求后后台死活接受不到前台传过去的值,各种百度无解,最后自己仔细对比了一下请求信息才发现宝塔底层封装的JQ发起的请求和我的axios请求post格式不一样,他们的请求使用的是form-data方式,而我的axios请求始终是Request Payload方式很蒙圈,各种查阅资料别人说的什么qs方法什么修改配置方法都试过了无解。
折腾了5个多小时,实在是坑死人不偿命。最后灵机一动想到一个简单的捷径,那就是封装一个方法将要提交的对象转换成formdata格式,本来以为这个方法不太现实,但是当我看到后台返回的结果时,心里说不出来的喜悦。这种网上没有人提到过的偏方居然也能凑效。废话这么多,下面把我最终解决代码奉上。
methods: {
get_status (restart = '', show_load = 1) {
let that = this;
let timeout = 3600;
status=1;
let data = {
status: status,
callback: 'xxx.get_status'
};
data=that.obj2form(data); //调用object to formData 转换方法
that.$axios.post(`/plugin?action=a&s=get_status&name=xxx`, data, {
timeout: timeout
})
.then(function (response) {
that.logs = response.data.run_logs
}
})
.catch(function (error) {
console.log(error);
});
},
//转换方法
obj2form(obj){
let formData = new FormData();
for(let index in obj){
formData.append(index,obj[index]);
};
return formData;
}
}