美文网首页
vue,java处理前后端交互那点事儿

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

作者: 简单coder | 来源:发表于2019-11-20 11:08 被阅读0次

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

    java后端

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

    1.跨域问题

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

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

    • 1.开发阶段一层devServer转发



      注释起来的部分就是,这里还涉及到部分转发,就是访问的时候指定哪个路径下需要转发,路径重写,指定目标,反正也是需要一些的理解

    • 2.发布阶段nginx配置
      一般这一步是到发布的时候,连上公司服务器才能进行,下面这个我没试过,应该是能行的,这个多长时间下就可以了.nginx配置一般都需要学一下,也不说哪些配置优化,就基础的配置,比如搭建,转发什么的.
    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.网络请求封装

    • 1.java后端
      这里的封装其实就是对请求的拦截处理,token过期处理,有几个点得注意下:
      配置拦截器劫持



      拦截器拦截检验

    @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)

    • 2.vue前端
      假设大家基本用的都是axios(其他的框架我也没听过,毕竟只是基础地学了下前端的vue)

    基本都回去封装一个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
    
    

    总结:

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

    相关文章

      网友评论

          本文标题:vue,java处理前后端交互那点事儿

          本文链接:https://www.haomeiwen.com/subject/tipnictx.html