前后端分离,如何解决跨域(代理模式)、路由拦截(进入页面需要登录)以及请求拦截(登录TOKEN失效)等问题(初学者)

前端时间项目需要发布一个较大的版本,工作比较忙,加了好多个晚上的班,感觉自己有点缺氧了。最近稍微闲下来了,顺便调休了三天,刚刚给家里来了个大扫除,看着这干干净净的小家,心里顿时舒服了很多。 在这里插入图片描述

下面进入正题(本文以vue项目为例)→

  • 首先,安装axios:
npm install axios
  • 然后在 main.js 里引入 axios,然后进行全局挂载在vue原型上
import axios from 'axios';
Vue.prototype.$axios = axios;

使用方式:

 this.$axios
        .post(api, this.form)
        .then(res => {
          console.log(111, res)
        })

当然,这里还有另外一种方式让我们可以在全局直接使用axios

  • 首先,安装axios, vue-axios:
npm install --save axios vue-axios
  • 然后在 main.js 里引入 axios,然后使用 vueAxios进行全局挂载
import vueAxios from 'vue-axios'
Vue.use(vueAxios, axios);

使用方式:

Vue.axios.get(api).then((response) => {
  console.log(response.data)
})
 
this.axios.get(api).then((response) => {
  console.log(response.data)
})
 
this.$http.get(api).then((response) => {
  console.log(response.data)
})

在这里插入图片描述

跨域问题:
在vue或者react项目中,我们大多使用代理的方式来解决跨域问题

  • 首先,找到 config文件下的 index.js 文件,在proxyTable下加上以下代码
proxyTable: {
      '/apis': {
        // 测试环境
        target: 'http://192.168.0.113',  // 后端接口域名
        changeOrigin: true,  //是否跨域
        pathRewrite: {
            '^/apis': ''   //需要rewrite重写的,
        }
      }
    },

在这里插入图片描述
*tips:因为我们修改了配置文件,所以在这一步之后需要我们重启项目才可生效

接下来就是发送请求,可能我们对于上面刚加的代码不是很理解,比如说:
在这里插入图片描述
这个 '^/apis ’ 是什么意思?
我们仔细一看,他就像是一个正则表达式:^ 符号代表以什么开头,这里就是以 /apis 开头,当我们使用axios发送请求时:
在这里插入图片描述
他就会检测到这里的请求 api 为: ‘/apis/login’ ,是以 /apis 开头的路径,那他就会将我们的请求前缀替换成为 ‘http://192.168.0.113’

http://localhost:8888/apis/login
等同于:
http://192.168.0.113/login

路由拦截

我们的项目中很多页面是需要进行登录才能浏览的,但是如果我们每进入一个页面都去判断他是否有登录(Token),明显不是一个明智之举,所以我们需要一个公共的方法去处理这件事情。

以 vue-router 为例,我们知道 vue-router 具有两个全局守卫:

全局前置守卫:

router.beforeEach((to, from, next) => {
  // ...
})

to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。

全局后置钩子:
router.afterEach((to, from) => {
  // ...
})

在这里,我们正是要用到 router.beforeEach 这个钩子函数
让我们来打印康康 to 这个参数有什么东西:
在这里插入图片描述
其中里面有一个 mata 的对象,他代表了什么呐?
在这里插入图片描述
没错,正是它。我们给路径为 ‘/’ 的这个路由配置一个参数requireAuth

// 需要登录才能进入的页面可以增加一个meta属性
meta: { 
	requireAuth: true
},

这样,我们就可以 to 这个参数里找到他了
在这里插入图片描述
相信讲到这里,你一定有了自己的实现思路了

// 判断是否需要登录权限 以及是否登录
router.beforeEach((to, from, next) => {
  console.log('to', to)
  const { meta } = to || {}
  const { requireAuth } = meta || {}
  // 即将进入的页面是否需要登录
  if(requireAuth) {
    // 判断是否有Token(一般用户登录之后,后端会返回给我们用户的Token, 我们将其存放下来,具体存			 到哪里,存储多长时间,根据你的需要来,这里我是存储在 localStorage 里)
    if(localStorage.getItem('NOV_TOKEN')) {
    // 记住,一定要调用next函数,router才能进行下去
      next()
    } else {
        //  需要登录却没有登录(无Token),重定向到 登录页
        next({
          path: '/login',
        })
    }
  } else {
  // 不需要登录,直接往下进行
    next()
  }
 })

在这里插入图片描述
请求拦截

上面,我们对路由进行了拦截,但是有时候虽然我们登录过,将Token已经存储在 localStorage 里了,但这也不能完全保证安全,因为 token 一般是具有时效的,登录(Token)失效后也需要重新登录。但我们怎么知道Token是否失效了呐?这就需要后端告诉我们了。当我们进入一个需要登录的页面,需要发送请求获取某些数据时,后端会先判断此人是否有登录,登录是否失效,如果失效,后端会返回给我们401登录失效code码。当我们接收到 401 code 码时,就需要重定向到登录页,引导用户进行登录。
那这些,就属于请求拦截的范畴了。
axios的官方文档给出了两个拦截方式:

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });

所以我们就可以在这里进行响应拦截了

// 请求响应拦截处理
axios.interceptors.response.use(response => {
  const { data: { code, message }  } = response
  if ( code === '200') {
    return response;
  } else if (code === '401') {
    router.push('/login')
  } else {
    ElementUI.Message.error({
      message: message || '请求失败',
      type: 'warning'
    });
  }
}, function (error) {
  return Promise.reject(error)
})

在这里插入图片描述
关于这些axios请求,我们都是直接在main.js里进行相关处理的。当然,我们也可以对axios在进行一次封装,把请求相关的东西都放到里面去,这样代码更具有可读性和维护性。
首先,我们在src文件夹下面新建一个utils工具文件
在这里插入图片描述
在里面新建一个request.js文件,关于请求的信息我们就放到里面进行处理。

import axios from "axios";
import { Message, MessageBox } from "element-ui";
import store from "../store";
import { getToken, removeToken} from "./auth";
import { IDPHelper } from "./idp";

// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 15000,
  validateStatus: (status) => {
    return true;
  }
});

// request拦截器
service.interceptors.request.use(
  config => {
    if (store.getters.token) {
      // config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
      config.headers["token"] = getToken();
    }
    return config;
  },
  error => {
    // Do something with request error
    console.log(error); // for debug
    Promise.reject(error);
  }
);

// respone拦截器
service.interceptors.response.use(
  response => {
    const status = response.status;
    if (status >= 200 && status < 300) {
      return response.data;
    }

    switch (status) {
      case 401:
          MessageBox.confirm(
            "你已被登出,请重新登录",
            "确定登出",
            {
              confirmButtonText: "重新登录",
              cancelButtonText: "取消",
              type: "warning"
            }
          ).then(() => {
            removeToken();
           router.push('/login')
          });
          break;
      case 403:
          MessageBox.confirm(
            "你无权访问该页面,请申请权限再行访问",
            "系统安全",
            {
              confirmButtonText: "回退",
              cancelButtonText: "继续留下",
              type: "warning"
            }
          ).then(() => {
            window.history & window.history.back()
          });
          break;
      case 404:
      case 408:
        Message({
          message: "请求资源不存在!",
          type: "error",
          duration: 3 * 1000
        });
        break;
      case 500:
      case 502:
      case 504:
      default:
        Message({
          message: "服务器繁忙,请稍后重试!",
          type: "error",
          duration: 3 * 1000
        });
        break;
    }
    return Promise.reject("error");
  },
  error => {
    console.log("err" + error); // for debug
    Message({
      message: error.message,
      type: "error",
      duration: 3 * 1000
    });
    return Promise.reject(error);
  }
);

export default service;

使用:

在这里插入图片描述

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值