Axios 插件
在创建Nuxt.js项目的时候我们安装了Axios插件,这里我们需要对Axios插件功能做一个简单的封装。封装主要为了实现以下两个功能:
- 让Axios每次请求的时候自动对 JsonObject 参数进行编码。
- 处理统一的返回状态码和返回结果。
这里需要添加一个第三方依赖qs,我们执行以下命令来添加qs依赖:
npm install qs --save
然后我们创建文件site/plugins/axios.js,完整内容如下:
import qs from 'qs';
export default function ({ $axios, $toast, app }) {
$axios.onRequest((config) => {
config.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
config.transformRequest = [
function (data) {
if (process.client && data instanceof FormData) {
// 如果是FormData就不转换
return data;
}
data = qs.stringify(data);
return data;
},
];
});
$axios.onResponse((response) => {
if (response.status !== 200) {
return Promise.reject(response);
}
const jsonResult = response.data;
if (jsonResult.success) {
return Promise.resolve(jsonResult.data);
} else {
return Promise.reject(jsonResult);
}
});
}
接下来修改nuxt.config.js文件,修改该文件中的plugins,如下:
/*
** Plugins to load before mounting the App
*/
plugins: [
'~/plugins/axios'
],
用户注册页面
新增文件site/pages/user/reg.vue,该文件完整代码如下:
<template>
<section>
<my-nav />
<section class="section">
<div class="container">
<div class="field">
<label class="label">用户名</label>
<div class="control">
<input
v-model="form.username"
class="input"
type="text"
placeholder="请输入用户名"
/>
</div>
</div>
<div class="field">
<label class="label">密码</label>
<div class="control">
<input
v-model="form.password"
class="input"
type="password"
placeholder="请输入密码"
/>
</div>
</div>
<div class="field">
<label class="label">重复密码</label>
<div class="control">
<input
v-model="form.rePassword"
class="input"
type="password"
placeholder="请再次输入密码"
/>
</div>
</div>
<div class="field">
<label class="label">昵称</label>
<div class="control">
<input
v-model="form.nickname"
class="input"
type="text"
placeholder="请输入昵称"
/>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link" @click="postAdd">注册</button>
</div>
</div>
</div>
</section>
<my-footer />
</section>
</template>
<script>
import MyNav from '~/components/MyNav';
import MyFooter from '~/components/MyFooter';
export default {
components: {
MyNav,
MyFooter,
},
head() {
return {
title: '用户注册',
};
},
data() {
return {
form: {
username: '',
password: '',
rePassword: '',
nickname: '',
},
};
},
methods: {
// 提交注册
async postAdd() {
try {
const resp = await this.$axios.post('/api/user/add', this.form);
console.log(resp);
this.$router.push('/user/login'); // 注册成功跳转到登录页
} catch (err) {
alert(err.message || err);
}
},
},
};
</script>
<style>
.container {
min-height: 300px;
}
</style>
然后我们在site目录下执行命令npm run dev来启动前端页面服务,服务启动成功之后,访问路径/user/reg来查看页面效果
用户登录页面
完成用户注册,接下来我们来完善用户登录功能。新建文件site/pages/user/login.vue,完整代码如下:
<template>
<section>
<my-nav />
<section class="section">
<div class="container">
<div class="field">
<label class="label">用户名</label>
<div class="control">
<input
v-model="form.username"
class="input"
type="text"
placeholder="请输入用户名"
/>
</div>
</div>
<div class="field">
<label class="label">密码</label>
<div class="control">
<input
v-model="form.password"
class="input"
type="password"
placeholder="请输入密码"
/>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link" @click="login">登录</button>
</div>
</div>
</div>
</section>
<my-footer />
</section>
</template>
<script>
import MyNav from '~/components/MyNav';
import MyFooter from '~/components/MyFooter';
export default {
components: {
MyNav,
MyFooter,
},
head() {
return {
title: '用户登录',
};
},
data() {
return {
form: {
username: '',
password: '',
},
};
},
methods: {
async login() {
try {
const resp = await this.$axios.post('/api/user/login', this.form);
console.log('登录成功', resp);
} catch (e) {
alert(e.message || e);
}
},
},
};
</script>
<style>
.container {
min-height: 300px;
}
</style>
然后我们访问页面/user/login就能够看到页面效果,输入我们刚刚注册的用户名、密码,点击登录按钮,然后我们打开浏览器控制台,会看到登录的用户信息
如何记录登录状态
由于我们是前后端分离,Go 语言的服务端和 Nuxt.js 的页面服务无法共享 session,所以登录状态我们没法由 session 来存储,所以我们引入了token机制。在用户登录成功之后,同时为该用户生成一个token,它就相当于sessionId,通过它能够找到对应的用户,上面的登录功能在登录成功后服务端给我们返回了用户信息和token,所以登录成功之后需要由浏览器记录下token,并且每次Nuxt.js请求接口的时候都需要带上该token,这样接口服务在收到带token的请求之后就能够知道该请求是哪个用户发起的。总结流程如下:
- 调用登录接口,验证用户名密码,验证成功后接口返回授权令牌(userToken);
- 前端网页收到授权令牌(userToken)后,将他们存储到 cookie 中;
- 前端网页在每次请求后台接口的时候检查 cookie 中是否有userToken,如果有就带上;
- 服务端在收到网页中的接口请求时,检查请求中是否有合法的userToken,没有就返回错误要求网页进行登录;
所以我们需要借助 cookie 来存储token,并且在每次Nuxt.js请求接口的时候都需要带上 cookie 中的token。
利用 cookie 存储 token
Nuxt.js 有 cookie 插件:cookie-universal-nuxt ,接下来我们来使用该插件。
安装cookie-universal-nuxt
npm i --save cookie-universal-nuxt
安装成功之后打开文件nuxt.config.js配置cookie-universal-nuxt,在 modules 中添加如下配置:
modules: [
// Doc: https://github.com/nuxt-community/modules/tree/master/packages/bulma
'@nuxtjs/bulma',
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
['cookie-universal-nuxt', { alias: 'cookies' }]
]
接下来我们修改site/pages/user/login.vue文件,在登录成功之后将token保存到 cookie 中,修改之后的完整代码如下:
<template>
<section>
<my-nav />
<section class="section">
<div class="container">
<div class="field">
<label class="label">用户名</label>
<div class="control">
<input
v-model="form.username"
class="input"
type="text"
placeholder="请输入用户名"
/>
</div>
</div>
<div class="field">
<label class="label">密码</label>
<div class="control">
<input
v-model="form.password"
class="input"
type="password"
placeholder="请输入密码"
/>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link" @click="login">登录</button>
</div>
</div>
</div>
</section>
<my-footer />
</section>
</template>
<script>
import MyNav from '~/components/MyNav';
import MyFooter from '~/components/MyFooter';
export default {
components: {
MyNav,
MyFooter,
},
head() {
return {
title: '用户登录',
};
},
data() {
return {
form: {
username: '',
password: '',
},
};
},
methods: {
async login() {
try {
const resp = await this.$axios.post('/api/user/login', this.form);
console.log('登录成功', resp);
this.$cookies.set('userToken', resp.token, {
maxAge: 86400 * 7,
path: '/',
});
this.$router.push('/');
} catch (e) {
alert(e.message || e);
}
},
},
};
</script>
<style>
.container {
min-height: 300px;
}
</style>
Nuxt.js 的请求带上 token
前面我们已经对axios做了一个简单的封装,接下来我们继续修改该封装,让它在每次请求的时候都检查 cookie 中是否有 token,如果有就自动带上。修改文件site/plugins/axios.js,修改之后的完整内容如下:
import qs from 'qs';
export default function ({ $axios, $toast, app }) {
$axios.onRequest((config) => {
config.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
const userToken = app.$cookies.get('userToken'); // 从cookie中获取token
if (userToken) {
// 如果找到了token,那么将token放到请求头中
config.headers.common['X-User-Token'] = userToken;
}
config.transformRequest = [
function (data) {
if (process.client && data instanceof FormData) {
// 如果是FormData就不转换
return data;
}
data = qs.stringify(data);
return data;
},
];
});
$axios.onResponse((response) => {
if (response.status !== 200) {
return Promise.reject(response);
}
const jsonResult = response.data;
if (jsonResult.success) {
return Promise.resolve(jsonResult.data);
} else {
return Promise.reject(jsonResult);
}
});
}
这样我们在每次使用axios请求的 Go 语言接口时就都会检查并带上 cookie 中保存的token了。
本实例中我们完成了整个用户的登录和注册模块,通过学习本章内容相信你已经具备开发一个独立模块的能力了。下面我们看下本实例完整源码的目录结构:
.
├── server
│ ├── go.mod
│ ├── go.sum
│ ├── main.go
│ ├── user_controller.go
│ ├── user_model.go
│ └── user_service.go
└── site
├── README.md
├── assets
│ └── README.md
├── components
│ ├── Logo.vue
│ ├── MyFooter.vue
│ ├── MyNav.vue
│ └── README.md
├── jsconfig.json
├── layouts
│ ├── README.md
│ └── default.vue
├── middleware
│ └── README.md
├── nuxt.config.js
├── package-lock.json
├── package.json
├── pages
│ ├── README.md
│ ├── index.vue
│ └── user
│ ├── login.vue
│ └── reg.vue
├── plugins
│ ├── README.md
│ └── axios.js
├── static
│ ├── README.md
│ └── favicon.ico
└── store
└── README.md