vue配置代理,处理后端跨域

106 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

前段项目下,处理跨域是一个很正常的问题;在vue脚手架下面处理就更为常见了;那么要怎么去处理这个很常见的问题呢?

1.在src下面新建api/index.js,然后将对应的js文件复制进去,并且放到原型里面

// 配置API接口地址
import Vue from 'vue'
// import { Toast } from 'vant';
// import store from '../store/index';

// Vue.use(Toast);

// 打包时要注释
// var root ='/api'


// 引用axios
var axios = require('axios');
// 自定义判断元素类型JS
function toType(obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
// 参数过滤函数
function filterNull(o) {
  for (var key in o) {
    if (o[key] === null) {
      delete o[key]
    }
    if (toType(o[key]) === 'string') {
      o[key] = o[key].trim()
    } else if (toType(o[key]) === 'object') {
      o[key] = filterNull(o[key])
    } else if (toType(o[key]) === 'array') {
      o[key] = filterNull(o[key])
    }
  }
  return o
}

/**
  * 提示函数
  * 禁止点击蒙层、显示一秒后关闭
  */
 const tip = msg => {
  Toast({
      message: msg,
      duration: 1000,
      forbidClick: true
  });
}
/**
  * 跳转登录页
  * 携带当前页面路由,以期在登录页面完成登录后返回当前页面
  */
 const toLogin = () => {
  router.replace({
      path: '/login',
      query: {
          redirect: router.currentRoute.fullPath
      }
  });
}
/** 
 * 请求失败后的错误统一处理 
 * @param {Number} status 请求失败的状态码
 */
const errorHandle = (status, other) => {
  // 状态码判断
  switch (status) {
      // 401: 未登录状态,跳转登录页
      case 401:
          toLogin();
          break;
      // 403 token过期
      // 清除token并跳转登录页
      case 403:
          tip('登录过期,请重新登录');
          // localStorage.removeItem('token');
          // store.commit('loginSuccess', null);
          setTimeout(() => {
              toLogin();
          }, 1000);
          break;
      // 404请求不存在
      case 404:
          tip('请求的资源不存在'); 
          break;
      default:
  
      }}
// 创建axios实例
// var instance = axios.create({    timeout: 1000 * 12});
// 设置post请求头
//axios.defaults.headers.post['Content-Type'] = 'application/json';
/**
  * 请求拦截器
  * 每次请求前,如果存在token则在请求头中携带token
  */
    //axios.interceptors.request.use(
    //config => {
    // 登录流程控制中,根据本地是否存在token判断用户的登录情况
    // 但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token
    // 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码
    // 而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
    //     const token = sessionStorage.token;
    //     token && (config.headers.Authorization = token);
    //     return config;
    // },
    // error => Promise.error(error)
    // )
axios.defaults.timeout = 10000
// 响应拦截器
axios.interceptors.response.use( res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res), 
   // 服务器状态码不是2开头的的情况
   // 这里可以跟你们的后台开发人员协商好统一的错误状态码 
   // 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
   // 下面列举几个常见的操作,其他需求可自行扩展
   // 请求失败
  error => {
    const { response } = error;
    if (response) {
        // 请求已发出,但是不在2xx的范围 
        errorHandle(response.status, response.data.message);
        return Promise.reject(response);
    } else {
        // 处理断网的情况
        // eg:请求超时或断网时,更新state的network状态
        // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
        // 关于断网组件中的刷新重新获取数据,会在断网组件中说明
        // setTimeout(() => {
        //   Toast({
        //     message: "暂无网络,请稍后重试",
        //     duration: 1000,
        //     forbidClick: true
        // });
      // }, 500);
        store.commit('changeNetwork', false);
    }
});

function apiAxios(method, url, params, success, failure){
  if (params) {
    params = filterNull(params)
  }

  // if (process.env.NODE_ENV == 'development'){
  //   axios.defaults.baseURL = 'http://39.96.24.162:10000';
  //  } else if (process.env.NODE_ENV == 'testing'){
  //  axios.defaults.baseURL = 'http://39.96.24.162:10000';
  //  } else if (process.env.NODE_ENV == 'production') {
  //  axios.defaults.baseURL = 'http://59.110.220.161:9000';
  //  }
        axios({
          method: method,
          url: url,
          headers: {
            "Content-Type": "application/json",
            Authorization: localStorage.token
          },
          data: method === 'POST' || method === 'PUT' ? params : null,
          params: method === 'GET' || method === 'DELETE' ? params : null,

// 打包时要注释
          //  baseURL:root,



          withCredentials: false,
        })
        .then(function (res) {
          if (res.status === 200) {
            if (success) {
                  success(res.data);
                  Toast.clear();
             }
          } else {
            if (failure) {
              failure(res.data);
            } else {
              // Toast.loading({
              //   duration:1000,      
              //   forbidClick: true, 
              //   loadingType: 'spinner',
              //   message:JSON.stringify(res.data),
              // });
              // Message({
              //   showClose: true,
              //   message: 'error: ' + JSON.stringify(res.message),
              //   type: 'error',
              //   duration: 2000
              // })
              //  this.$Message.error('error: ' + JSON.stringify(res.data));
              // window.alert('error: ' + JSON.stringify(res.data))
            }
          }
        })
        .catch(function (err) {
          let res = err.response;
          if (err) {
            // loadinginstance.close();
            // Indicator.close();
            // Toast.loading({
            //   duration:1000,      
            //   forbidClick: true, 
            //   loadingType: 'spinner',
            //   message: JSON.stringify(res.message),
            // });
            // Message({
            //   showClose: true,
            //   message: res.data.non_field_errors, // res.data.non_field_errors
            //   type: 'error',
            //   duration: 2000
            // })
            // window.alert('api error, HTTP CODE: ' + res.status)
          }
        })
      }

      // 返回在vue模板中的调用接口
      export default {
        get: function (url, params, success, failure) {
          return apiAxios('GET', url, params, success, failure)
        },
        post: function (url, params, success, failure) {
          return apiAxios('POST', url, params, success, failure)
        },
        put: function (url, params, success, failure) {
          return apiAxios('PUT', url, params, success, failure)
        },
        delete: function (url, params, success, failure) {
          return apiAxios('DELETE', url, params, success, failure)
        }
      }

image.png

2.添加本地代理,处理跨域(注意只在本地有效,打包不走这个,放线上还是会跨域)

image.png

3.打开api文件夹下面的index.js(注意,这两个打包的时候要注释掉)

image.png

image.png

4.然后页面开始调接口 image.png

5.配置测试环境和生产环境(并且在package.json文件配置命令) image.png

image.png

6.然后将文件夹夹放到和后端的数据同一个域下(同一个服务器)测试,看到数据就可以了