当前位置: 首页 > 后端技术 > Node.js

使用vue+thinkjs开发一个博客程序,记录

时间:2023-04-03 18:08:14 Node.js

冬天的懒癌攻势,给自己找点事做。之前写过几次博客程序,php两次,nodejs用ThinkJS。随着ThinkJS版本从1.x升级到2.x,之前的博客程序也进行了升级。但是因为之前考虑搜索引擎爬取,所以还是按照传统的方式开发,没有前后端分离。这次准备用vue2.x和ThinkJS3.X重写。这里主要记录下开发过程中遇到的问题和解决方法。地址https://github.com/lscho/Thin...还没写,持续更新中。后续更新会发布在个人博客:https://lscho.com/tech/vue-th...设计1.前后端分离2.后端只提供接口3.RESTfulAPI4.使用jwt认证依赖服务器"dependencies":{"think-logger3":"^1.0.0","think-model":"^1.0.0","think-model-mysql":"^1.0.0","think-session":"^1.0.0","think-session-jwt":"^1.0.8","thinkjs":"^3.0.0"}前端"dependencies":{"axios":"^0.17.0","iview":"^2.5.1","mavon-editor":"^2.4.13","vue":"^2.5.2","vue-axios":"^2.0.2","vue-router":"^3.0.1","vuex":"^3.0.0","vuex-router-sync":"^5.0.0"}structure|-clientfront-end|-server后端问题jwt身份认证jwt的原理很清楚,之前实现过类似的功能,搜索了一下,发现node-jsonwebtoken这个包使用起来很简单,主要包括加密和解密。折腾了一番,还是跑成功了,但是去ThinkJS仓库发现了插件think-session-jwt,也是基于node-jsonwebtoken的。这更容易使用。配置完成后,可以直接使用ThinkJS的ctx.session方法生成验证。配置时需要注意tokenType参数,它决定了token的获取方式。我这里用的是header,意思是后面会从每个请求的header中找到token,key值为配置的tokenName。然后处理前端部分,为每个请求附加一个令牌。这里我使用的是axios,在请求拦截器中可以方便的加入。让加载实例;axios.interceptors.request.use(config=>{if(localStorage.getItem('token')){config.headers.Authorization=localStorage.getItem('token')}returnconfig;},error=>{返回错误;})然后就可以在每次登录后的请求中看到后端的授权认证了。因为API接口遵循RESTful风格,所以除了GET类型的请求,都需要验证token是否有效。ThinkJScontroller提供了Pre-operation__before。这里可以进行逻辑判断,只有通过的才会继续执行。async__before(action){try{this.userInfo=awaitthis.ctx.session('userInfo');}catch(err){this.userInfo={};}if(this.resource!='token'&&this.ctx.method!='GET'&&think.isEmpty(this.userInfo)){this.ctx.status=401;returnthis.ctx.fail(-1,"请登录并操作");这里有个问题是当token出错的时候node-jsonwebtoken会抛出异常,所以这里使用trycatch来捕获。可能有更好的解决办法,后面再处理。前端身份失效检测为了安全起见,我们的token一般都设置了有效期,所以有两种情况需要我们处理。token不存在,好办。直接在路由的预运算中判断是否存在。如果不存在,则转向登录界面beforeEnter:(to,from,next)=>{if(!localStorage.getItem('token')){next({path:'/login'});}else{下一步();}}2。令牌已过期或被篡改。这就需要后端检测才能知道token是否有效。这里,服务端在检测失败后会返回一个401状态码,以便前端识别。if(this.resource!='token'&&this.ctx.method!='GET'&&think.isEmpty(this.userInfo)){this.ctx.status=401;returnthis.ctx.fail(-1,"请登录后操作");}我们可以在axios的请求响应拦截器中进行判断,因为4XX的状态码会抛出异常,所以代码如下axios.interceptors.response.use(data=>{//这里可以对成功进行各种处理请求返回数据;},error=>{if(error.response){switch(error.response.status){case401:store.commit("clearToken");router.replace("/login");break;}}returnPromise.reject(error.response.data)})markdown编辑器和文件上传markdown编辑器使用mavonEditor配置非常方便,不多说了,主要说说一个文件上传遇到的问题。前端代码表格数据();formdata.append('图像',$file);image.upload(formdata).then(res=>{if(res.errno==0&&res.data.url){this.$refs.md.$img2Url(pos,res.data.url);}});}后端处理constfile=this.file('image');constextname=path.extname(file.name);//path.extname获取文件后缀constfilename=path.basename(file.path);constbasename=think.md5(文件名)+扩展名;constsavepath='/upload/'+basename;constfilepath=path.join(think.ROOT_PATH,"www"+savepath);think.mkdir(路径.dirname(文件路径));try{//跨盘符移动会抛出异常awaitrename(file.path,filepath);}抓住(e){constreadStream=fs.createReadStream(file.path);constwriteStream=fs.createWriteStream(文件路径);readStream.pipe(writeStream);}这里还用了一个trycatch来捕捉异常,主要是ThinkJS会将上传的文件先放到临时目录下,而windows下临时目录不一定和项目目录在同一个盘符。如果移动会抛出异常:Error:EXDEV,cross-devicelinknotpermitted,没有移动权限,此时只能先读取文件,再写入文件2017-12-27更新@阿特大兄在群里提到,可以指定payload中间件的临时目录为项目下的某个目录,这样就不会跨盘了{handle:'payload',options:{uploadDir:path.join(think.ROOT_PATH,'runtime/data')}}这样就可以直接使用rename来操作了。关于跨盘rename的问题,参考https://github.com/nodejs/nod...找到原因了,大意是操作系统限制rename只能重命名路径引用地址,并没有不移动数据。rename不能跨文件系统操作,所以如果跨文件系统操作需要先复制再删除旧数据deployment因为前端路由使用的是history模式,所以请求要转发到index.html入口页面进行处理,这是一些mvc框架单入口的概念#Requestforwardedtoentrylocation/{try_files$uri$uri//index.html;然后我们要处理后端请求部分。如果不是同一个域名,就要解决跨域问题。这里前后端使用同一个域名,只是对API请求做一个反向代理。设置$node_port8360;位置~^/api/{proxy_http_version1.1;proxy_set_headerX-Real-IP$remote_addr;proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;proxy_set_header主机$http_host;proxy_set_headerX-NginX-Proxytrue;proxy_set_header升级$http_upgrade;proxy_set_header连接“升级”;proxy_passhttp://127.0.0.1:$node_port$request_uri;proxy_redirect关闭;}后端使用pm2守护进程即可。