SIerだけど技術やりたいブログ

5年目のSIerのブログです

Vuejs Axiosで共通的な例外をあつかう

Vuejsと一緒に利用されることが多いHTTPクライアントライブラリのAxiosでは、interceptorsの仕組みを利用することでレスポンスに関する共通的な処理をはさみこめる。これを利用してVuejsで共通的な例外をあつかう。

Axios interceptors
GitHub - axios/axios: Promise based HTTP client for the browser and node.js


動作確認

モックサーバを用意する

サンプルとしてエラーを返すモックサーバを用意する。

$ npm install -D json-server

モック定義のファイルを用意する。

const jsonServer = require('json-server')
const server = jsonServer.create()
const middlewares = jsonServer.defaults()

server.use(middlewares)
server.listen(3000, () => {
  console.log('JSON Server is running')
})
server.get('/unauthorized', (req, res) => {
  res.status(401).jsonp({
    message: "unauthorized"
  })
})

server.get('/systemerror', (req, res) => {
  res.status(500).jsonp({
    message: "something wrong"
  })
})

モックサーバを起動する。

$ node mock.js
JSON Server is running


システムエラーの場合は、500を返す。

$ curl -i localhost:3000/systemerror
HTTP/1.1 500 Internal Server Error
X-Powered-By: Express
Vary: Origin, Accept-Encoding
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Content-Length: 32
ETag: W/"20-fhnUB5BwaOsQsXyG8exFr0MEGzY"
Date: Wed, 17 Jan 2018 13:16:01 GMT
Connection: keep-alive

{
  "message": "something wrong"
}

認証エラーの場合は、401を返す。

$ curl -i localhost:3000/unauthorized
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Vary: Origin, Accept-Encoding
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Content-Length: 29
ETag: W/"1d-1AQxoXvOBStoEV/A43KaFU1XEOg"
Date: Wed, 17 Jan 2018 13:16:42 GMT
Connection: keep-alive

{
  "message": "unauthorized"
}

アプリを用意する

完成版はgithubに。
GitHub - kimullamen/axios-sample


バージョンは以下の通り。

  "dependencies": {
    "axios": "^0.17.1",
    "vue": "^2.5.2",
    "vue-router": "^3.0.1",
    "vue-toasted": "^1.1.24"
  },

ポイントはAxiosのinterceptorsの仕組みを利用すること。これによってレスポンスの共通処理を作成できる。

今回はvue-toastedというトースト表示のライブラリを利用する。

import Vue from 'vue'
import Axios from 'axios'

const http = Axios.create({
  // for cors
  withCredentials: true
})
http.interceptors.response.use(function (response) {
}, function (error) {
  // 認証エラー時の処理
  if (error.response.status === 401) {
    Vue.toasted.clear()
    Vue.toasted.error(error.response.data.message)
  // システムエラー時の処理
  } else if (error.response.status === 500) {
    Vue.toasted.clear()
    Vue.toasted.error(error.response.data.message)
  }
  return Promise.reject(error)
})

export default http

Axiosをimportし、Vueのprototypeに設定する。こうすることでVueインスタンスから共通処理が設定済みのAxiosを$httpで利用できるようになる。

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import Axios from './axios'
import Toasted from 'vue-toasted'

Vue.prototype.$http = Axios
Vue.use(Toasted)
Vue.config.productionTip = false

/* eslint-disable no-new */

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

実際のアクセス部分。$httpを利用する。

<template>
  <div id="app">
    <button type="button" @click="callUnauthorized">unauthorized</button>
    <button type="button" @click="callSystemError">system error</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    callUnauthorized: function () {
      this.$http.get('http://localhost:3000/unauthorized')
    },
    callSystemError: function () {
      this.$http.get('http://localhost:3000/systemerror')
    }
  }
}
</script>

実行結果

HTTPレスポンスの共通処理が実行されて、トーストが表示される。

f:id:kimulla:20180118235708g:plain

今回の例だと1コンポーネントしかないので、トーストを表示しっぱなしにしている。コンポーネントが切り替わったときにトーストを消したい場合は、vue-router のナビゲーションガードやコンポーネントのdestroyedフック内で Vue.toasted.clear()を呼び出すといい。
ナビゲーションガード · vue-router

結論

HTTPレスポンスの例外は共通化できる。