使用 vue + thinkjs 開發博客程序記錄

一入冬懶癌發做,給本身找點事幹。以前博客程序寫過幾回,php 的寫過兩次,nodejs 用 ThinkJS 寫過,隨着 ThinkJS 版本從1.x 升級到 2.x 以前的博客程序也作過升級。可是由於前面考慮搜索引擎抓取仍是用傳統的方式開發,沒有作先後端分離。此次準備用 vue2.x 和 ThinkJS 3.X 從新寫一次。這裏主要記錄一下開發過程當中遇到的問題和解決方法。javascript

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

還沒有寫完,持續更新中,後續更新發布在我的博客中:https://lscho.com/tech/vue-th...html

設計方案

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 後端vue

問題

jwt 身份認證

jwt 的原理很清楚,以前本身也實現過相似的功能,搜索了一下,找到了 node-jsonwebtoken 這個包,使用起來很簡單,主要就是加密和解密兩個功能。一番折騰以後成功運行,可是去 ThinkJS 倉庫看了一下,居然有發現了 think-session-jwt 這個插件,也是基於 node-jsonwebtoken 的。這個就更好用了,配置完以後直接用 ThinkJS 的 ctx.session 方法就能夠生成和驗證。配置的時候須要注意一下 tokenType 這個參數,他決定了如何獲取 token ,我這裏用的是 header ,也就是說後面會從每一個請求的 header 中找 token,key 值爲配置的 tokenName。java

而後要處理前端部分,爲每一個請求附加上 token。這裏我用的是 axios ,在請求攔截器中很方便的就能夠加上。node

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

而後登陸以後的每一個請求中就能夠看到mysql

QQ截圖20171221200110.jpg

後端權限認證

由於 API 接口遵循 RESTful 風格,因此對除了 GET 類型的請求,都要驗證 token 是否有效,ThinkJS 的控制器提供了前置操做 __before。在這裏能夠作一下邏輯判斷,經過的纔會繼續執行。ios

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 捕獲,可能有更好的解決辦法,暫時放後面處理。git

前端身份失效檢測

爲了安全起見,咱們的 token 通常設置的都有效期,因此有兩種狀況須要咱們進行處理.

  1. 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 配置很方便,很少說,主要說一下文件上傳遇到的一個問題。
前端代碼

<mavon-editor ref=md @imgAdd="imgAdd" class="editor" v-model="formItem.content"></mavon-editor>
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 守護進程便可。

相關文章
相關標籤/搜索