vue,java处理前后端交互那点事儿

2019-11-20  本文已影响0人  简单coder

为什么会写这篇,主要是对以前的知识点回顾,以及给不清楚的人稍微梳理一下前后端对权限,网络请求公共模块的简单写法

java后端

前后端分离的好处之一就是可以更加的专精所长,但是随之带来的是两端在数据交互方面都需要进行自己的协调

1.跨域问题

No 'Access-Control-Allow-Origin' header is present on the requested resource

跨域问题我认为是需要让后端来做,毕竟做个cors也不是多么麻烦的事儿.
如果前端需要配置:

location / {  
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
} 

如果后端来配置:
就是一部到位的事情,直接配置一个cors,直接解决



注释部分可以增加更细化的配置,不过开发阶段我就没去管那么多

一个小注意点
顺嘴说一句,如果是src图片加载出现跨域问题,就是有的图片地址出现,只需要在index.html添加一个meta,防止别人的资源对你的资源进行refer防盗链劫持

<!--    防止img加载403-->
<meta name="referrer" content="no-referrer" />

原理在这

2.网络请求封装

@Configuration
public class XqInterceptor implements HandlerInterceptor {

    @Autowired
    UserAuthModelMapper userAuthModelMapper;
    @Autowired
    UserModelMapper userModelMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        System.out.println("进入了一次预处理" + request.getRequestURI());

        String url = request.getRequestURI();
        if (url.contains("/login")) {
            return true;
        }
        // 这里检验token是否过期
        {
            String token = request.getHeader("token");
            if (token == null) {
//                throw new XQException(RespEnum.user_token_error);
                write(request, response, Resp.error(RespEnum.user_token_null));
                return false;
            }
            UserAuthModel userAuthModel = userAuthModelMapper.findUserByToken(token);
            if (userAuthModel == null) {
//                throw new XQException(RespEnum.user_token_error);
                write(request, response, Resp.error(RespEnum.user_token_error));
                return false;
            }
            Date updateTime = userAuthModel.getUpdateTime();
            Long expireTimeInterval = updateTime.getTime() + 1000 * 60 * 60 * 24;;
            Long nowInterval = new Date().getTime();
            if (expireTimeInterval < nowInterval) {
                // 已经过期
                userAuthModel.setToken(UUID.randomUUID().toString());
                userAuthModel.setUpdateTime(null);
                userAuthModelMapper.updateByPrimaryKeySelective(userAuthModel);

                write(request, response, Resp.error(RespEnum.user_token_error));
                return false;
            }

            return true;
        }
    }

    private <T> void write(HttpServletRequest request, HttpServletResponse response, T content)
            throws IOException {

        response.setCharacterEncoding("UTF-8");
        String origin = request.getHeader("Origin");
        response.setHeader("Access-Control-Allow-Origin", origin);

//告诉浏览器允许跨域访问的方法
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        //告诉浏览器允许带有Content-Type,header1,header2头的请求访问
        response.addHeader("Access-Control-Allow-Headers", "*");
        //允许带有cookie访问
        response.addHeader("Access-Control-Allow-Credentials", "true");
        String contentStr = new ObjectMapper().writeValueAsString(content);
        response.getWriter().write(contentStr);
    }
}

拦截器抛出的异常在自定义的异常处理器中是捕获不到的,必须要直接对response进行返回
这里的逻辑我并没有进行很细化的编写(其实前端传值的时候,header里面应该添加token和id的,我一开始嫌麻烦没做,所以就只用了个token)

基本都回去封装一个http.js模块做axios的前后置处理
简单地说,就是new一个axios实例,然后对request,和response处理,然后export抛出即可

  1. 第一个注意点:
    这里是一个js文件,有时候我们可能想在这里进行一个toast弹窗之类的操作,在elementui中.这个需要一个vue的实例进行createElement 又或者我们需要一个router进行push什么的操作
    其实,我们可以对main.js进行抛出即可


    然后引入这个mian.js,就可以做任何想要的操作
  2. 第二个注意点:



    request,response的处理,前置处理header,后置处理通用全局错误码,不过我目前写的都还是比较简单地


  3. 第三个:
    使用的话,建议大家可以封装一个api文件夹




    其实这里如果想更少侵入的话,甚至可以将user.js等模块统一起来,然后放大Vue.prototype.$api上,但是那样写的话,编辑器就没有提示,所以我还是做这个引入的操作

  4. 第四个:
    这里最后说一个点,在上图user.js模块中,我测试了很多很多网络请求的传参,最后还是用了qs的stringfy() 才能正常的在java中使用@RequestParam拿到参数,这里面其实坑特别特别多,因为axios用的不是x-www-form-urlencoded格式的参数,用的是body体传参

// 设置post请求头
// instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'

上面这个也不好使的,反正坑是很多的,需要大家注意下

最后把http.js模块丢出来给大家参考下吧,去去糟粕


/**
 * axios封装
 * 请求拦截、响应拦截、错误统一处理
 * https://juejin.im/post/5b55c118f265da0f6f1aa354
 */
import axios from 'axios'
import router from '../router'
import store from '../store'
import cookies from 'js-cookie'
import mainVue from '../main'
/**
 * 提示函数
 * 禁止点击蒙层、显示一秒后关闭
 */
const tip = msg => {
  mainVue.$message(msg);
}

/**
 * 跳转登录页
 * 携带当前页面路由,以期在登录页面完成登录后返回当前页面
 */
const toLogin = () => {
  mainVue.$router.replace({
    path: '/login',
    query: {
      redirect: mainVue.$router.currentRoute.fullPath
    }
  })
}

/**
 * 请求失败后的错误统一处理
 * @param {Number} status 请求失败的状态码
 */
const errorHandle = (code, other) => {
  // 状态码判断
  switch (code) {
    // 401: 未登录状态,跳转登录页
    case 204:
    case 205:
    case 206:
    case 207:
      // 403 token过期
      // 清除token并跳转登录页
      cookies.remove("token")
      tip('登陆过去,请重新登陆')
      setTimeout(() => {
        toLogin()
      }, 1000)
      break
    // store.commit('loginSuccess', null);
    default:
      console.log(other)
  }
}

// 创建axios实例
var instance = axios.create()
// g过期时间
instance.defaults.timeout = 1000 * 12
// 设置post请求头
// instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
/**
 * 请求拦截器
 * 每次请求前,如果存在token则在请求头中携带token
 */
instance.interceptors.request.use(
  config => {
    // 登录流程控制中,根据本地是否存在token判断用户的登录情况
    // 但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token
    // 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码
    // 而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
    // const token = store.state.token
    const token = cookies.get("token")
    token && (config.headers.token = token)
    return config
  },
  error => Promise.error(error))

// 响应拦截器
instance.interceptors.response.use(
  // 请求成功
  res => {
    // console.log(JSON.stringify(res))
    if (res.status === 200) {
      if (res.data.code === 0) {
        return Promise.resolve(res)
      }else {
        // Promise.reject(res)
        errorHandle(res.data.code, res.data.msg)
        return Promise.reject(res)
      }
    } else {
      return Promise.reject(res)
    }
    // res.status === 200 ? Promise.resolve(res) : Promise.reject(res)
  },
  // 请求失败
  error => {
    const { response } = error
    if (response) {
      // 请求已发出,但是不在2xx的范围
      errorHandle(response.data.code, response.data.message)
      return Promise.reject(response)
    } else {
      // 处理断网的情况
      // eg:请求超时或断网时,更新state的network状态
      // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
      // 关于断网组件中的刷新重新获取数据,会在断网组件中说明
      if (!window.navigator.onLine) {
        store.commit('changeNetwork', false)
      } else {
        return Promise.reject(error)
      }
    }
  })

export default instance

总结:

其实东西有很多,但是写出来的很少,前后端的交互花了我很多时间去研究,尤其是前端,毕竟只是随便糊弄了几下就上手做了,毕竟只是做个分离的前后端分离管理,很多东西也不需要特别严格,成长肯定是有的,但是怕自己过段时间不写又给忘了,所以写了一些出来,给大家看看,也给自己留个醒,到时候知道在哪找这些问题

上一篇 下一篇

猜你喜欢

热点阅读