Go用户模块之前端Axios请求数据

滔哥 2021年02月27日 138次浏览

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。

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