资讯专栏INFORMATION COLUMN

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

Mike617 / 1928人阅读

摘要:一入冬懒癌发作,给自己找点事干。之前博客程序写过几次,的写过两次,用写过,随着版本从升级到之前的博客程序也做过升级。这里主要记录一下开发过程中遇到的问题和解决方法。后端使用守护进程即可。

一入冬懒癌发作,给自己找点事干。之前博客程序写过几次,php 的写过两次,nodejs 用 ThinkJS 写过,随着 ThinkJS 版本从1.x 升级到 2.x 之前的博客程序也做过升级。但是因为前面考虑搜索引擎抓取还是用传统的方式开发,没有做前后端分离。这次准备用 vue2.x 和 ThinkJS 3.X 重新写一次。这里主要记录一下开发过程中遇到的问题和解决方法。

地址 https://github.com/lscho/Thin...

尚未写完,持续更新中,后续更新发布在个人博客中:https://lscho.com/tech/vue-th...

设计方案

1.前后端分离
2.后端只提供接口
3.RESTful API
4.使用 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"
  }
结构

|-client 前端
|-server 后端

问题 jwt 身份认证

jwt 的原理很清楚,之前自己也实现过类似的功能,搜索了一下,找到了 node-jsonwebtoken 这个包,使用起来很简单,主要就是加密和解密两个功能。一番折腾之后成功运行,但是去 ThinkJS 仓库看了一下,竟然有发现了 think-session-jwt 这个插件,也是基于 node-jsonwebtoken 的。这个就更好用了,配置完之后直接用 ThinkJS 的 ctx.session 方法就可以生成和验证。配置的时候需要注意一下 tokenType 这个参数,他决定了如何获取 token ,我这里用的是 header ,也就是说后面会从每个请求的 header 中找 token,key 值为配置的 tokenName。

然后要处理前端部分,为每个请求附加上 token。这里我用的是 axios ,在请求拦截器中很方便的就可以加上。

let loadinginstace;
axios.interceptors.request.use(config => {
    if (localStorage.getItem("token")) {   
        config.headers.Authorization = localStorage.getItem("token")
    }    
    return config;
},error => {
    return error;
})

然后登录之后的每个请求中就可以看到

后端权限认证

因为 API 接口遵循 RESTful 风格,所以对除了 GET 类型的请求,都要验证 token 是否有效,ThinkJS 的控制器提供了前置操作 __before。在这里可以做一下逻辑判断,通过的才会继续执行。

  async __before(action) {
    try {
      this.userInfo=await this.ctx.session("userInfo");
    } catch(err) {
      this.userInfo={};
    }
    if(this.resource!="token"&&this.ctx.method!="GET"&&think.isEmpty(this.userInfo)){
      this.ctx.status=401;
      return this.ctx.fail(-1,"请登录后操作");
    }
  }

这里遇到一个问题,就是当 token 为错误时,node-jsonwebtoken 会抛出一个异常,所以这里用了 try catch 捕获,可能有更好的解决办法,暂时放后面处理。

前端身份失效检测

为了安全起见,我们的 token 一般设置的都有效期,所以有两种情况需要我们进行处理.

token 不存在,这种很好处理,直接在路由的前置操作中判断是否存在,存在则放行,不存在则转向登录界面

      beforeEnter:(to, from, next)=>{
        if(!localStorage.getItem("token")){
          next({ path: "/login" });
        }else{
          next();
        }
      }

2.token 超过有效期或者被篡改。这种需要后端检测之后才能知道该 token 是否有效。这里服务端检测失效之后会返回 401 状态码以便前端识别。

    if(this.resource!="token"&&this.ctx.method!="GET"&&think.isEmpty(this.userInfo)){
      this.ctx.status=401;
      return this.ctx.fail(-1,"请登录后操作");
    }

我们在axios的请求响应拦截器中进行判断即可,因为 4XX 的状态码会抛出异常,所以代码如下

axios.interceptors.response.use(data => {
        //这里可以对成功的请求进行各种处理
    return data;
},error=>{
    if (error.response) {
            switch (error.response.status) {
            case 401:
            store.commit("clearToken");
                router.replace("/login");
            break;
            }
    }
    return Promise.reject(error.response.data)
})
markdown 编辑器及文件上传

markdown 编辑器用了 mavonEditor 配置很方便,不多说,主要说一下文件上传遇到的一个问题。
前端代码

            imgAdd(pos, $file){
               var formdata = new FormData();
               formdata.append("image", $file); 
               image.upload(formdata).then(res=>{
                    if(res.errno==0&&res.data.url){
                        this.$refs.md.$img2Url(pos, res.data.url);
                    }
               });               
            }

后端处理

        const file = this.file("image");
        const extname=path.extname(file.name);//path.extname获取文件后缀名,可做控制
          const filename = path.basename(file.path);
          const basename=think.md5(filename)+extname;
          const savepath = "/upload/"+basename;
        const filepath = path.join(think.ROOT_PATH, "www"+savepath);
        think.mkdir(path.dirname(filepath));
        try{
            //跨盘符移动会抛出异常
            await rename(file.path, filepath);
        }catch(e){
            const readStream = fs.createReadStream(file.path);
            const writeStream = fs.createWriteStream(filepath);
            readStream.pipe(writeStream);
        }

这里也用了一个 try catch 来捕获异常,主要是因为 ThinkJS 会将上传的文件先放到临时目录中,而在 windows 下临时目录可能和项目目录不在同一盘符下,进行移动的话就会抛出一个异常:Error: EXDEV, cross-device link not permitted,没有权限移动,这时候就只能先读文件,再写文件

2017-12-27 更新 在群里@阿特 大佬提到,可以对 payload
这个中间件设置指定临时目录为项目下的某个目录,这样就不存在跨盘

    {
        handle: "payload",
        options: {
            uploadDir: path.join(think.ROOT_PATH, "runtime/data")
        }
    }

这样就可以直接使用 rename 来操作了,关于跨盘 rename 的问题,在 https://github.com/nodejs/nod... 找到了原因,大意是操作系统限制 rename 仅仅是重命名路径引用地址,并没有将数据移动过去,重命名不能跨文件系统操作,所以如果跨文件系统操作需要先复制、然后删除旧数据

部署

因为前端路由使用 history 模式,所以要将请求转发至 index.html 入口页面处理,跟有些 mvc 框架单入口是一个概念。

        # 请求转发至入口
        location / {
            try_files $uri $uri/ /index.html;
        }

然后还要处理一下后端请求部分,如果不是同一域名,就要解决跨域问题。这里前后端使用同一个域名,针对 api 请求做一下反向代理即可。

        set $node_port 8360;
        location ~ ^/api/ {
            proxy_http_version 1.1;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header X-NginX-Proxy true;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_pass http://127.0.0.1:$node_port$request_uri;
            proxy_redirect off;
        }

后端使用 pm2 守护进程即可。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/51640.html

相关文章

  • Vue+thinkJs博客网站(一)之vue多页面应用的webpack3配置

    摘要:首先看下的代码编译前删除之前编译生成的静态资源首先需要改的是入口文件,因为是多页面应用,需要多个入口文件来保证打包成不同的。 一.项目简介     本项目使用vue作为前端框架,thinkJs作为后端框架,构建个人博客网站,页面分为博客展示和后台管理,主要目的是学习使用thinkJs。现在只完成了主要的博客增删改功能,发现webpack的配置遇到了一些坑,这里先记录下。项目目录结构如下...

    NickZhou 评论0 收藏0
  • Vue+thinkJs博客网站(一)之vue多页面应用的webpack3配置

    摘要:首先看下的代码编译前删除之前编译生成的静态资源首先需要改的是入口文件,因为是多页面应用,需要多个入口文件来保证打包成不同的。 一.项目简介     本项目使用vue作为前端框架,thinkJs作为后端框架,构建个人博客网站,页面分为博客展示和后台管理,主要目的是学习使用thinkJs。现在只完成了主要的博客增删改功能,发现webpack的配置遇到了一些坑,这里先记录下。项目目录结构如下...

    dongxiawu 评论0 收藏0
  • Vue+thinkJs博客网站(二)之thinkJs使用

    摘要:插件机制可以说是自定义的钩子函数。首先在新建文件夹,新建文件以为例更新内容缓存里对应的数组表示钩子函数的调用名,具体调用的是下面的方法。 一.简介 这个个人博客网站最初制作的目的就是练习使用thinkJs,这一篇就主要讲一下thinkJs的一些特性和注意事项。涉及到了文件上传,thinkJs的插件机制,model层建立以及CURD的编写方式等。本项目github地址在这里。 项目thi...

    desdik 评论0 收藏0
  • Vue+thinkJs博客网站(二)之thinkJs使用

    摘要:插件机制可以说是自定义的钩子函数。首先在新建文件夹,新建文件以为例更新内容缓存里对应的数组表示钩子函数的调用名,具体调用的是下面的方法。 一.简介 这个个人博客网站最初制作的目的就是练习使用thinkJs,这一篇就主要讲一下thinkJs的一些特性和注意事项。涉及到了文件上传,thinkJs的插件机制,model层建立以及CURD的编写方式等。本项目github地址在这里。 项目thi...

    lucas 评论0 收藏0
  • Vue+thinkJs博客网站(二)之thinkJs使用

    摘要:插件机制可以说是自定义的钩子函数。首先在新建文件夹,新建文件以为例更新内容缓存里对应的数组表示钩子函数的调用名,具体调用的是下面的方法。 一.简介 这个个人博客网站最初制作的目的就是练习使用thinkJs,这一篇就主要讲一下thinkJs的一些特性和注意事项。涉及到了文件上传,thinkJs的插件机制,model层建立以及CURD的编写方式等。本项目github地址在这里。 项目thi...

    _Suqin 评论0 收藏0

发表评论

0条评论

Mike617

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<