• Spring Boot+Vue3前后端分离实战wiki知识库系统之前后端交互整合


    集成HTTP库axios

    在这里插入图片描述

    集成HTTP库axios

    我们定位到web的目录,使用命令安装axios:
    在这里插入图片描述
    指定版本号:
    在这里插入图片描述
    我们定位到Home.vue,我们要在这个页面通过axios用电子书列表功能。
    在这里插入图片描述
    导入axios,在setuo中使用:
    在这里插入图片描述
    启动后端和前端,发现报错:
    在这里插入图片描述
    在这里插入图片描述
    为了解决跨域问题,我们后端需要增加一个配置类:

    @Configuration
    public class CorsConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOriginPatterns("*")
                    .allowedHeaders(CorsConfiguration.ALL)
                    .allowedMethods(CorsConfiguration.ALL)
                    .allowCredentials(true)
                    .maxAge(3600); // 1小时内不需要再预检(发OPTIONS请求)
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述
    在这里插入图片描述
    添加完配置类后我们的前端就没有报错了。在这里插入图片描述

    Vue3数据绑定显示列表数据


    在这里插入图片描述

    Vue2代码结构示例

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    但是到Vue3生命周期方法被setup包了。例如我们刚刚提到的onMounted,如果我们要在Vue3中使用,我们要先从vue中导入,并且写在setup中:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    先会打印出setup后才是onMounted:
    在这里插入图片描述

    使用Vue3 ref实现数据绑定

    导入:在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    使用Vue3 reactive实现数据绑定

    同样使用需要导入:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    返回数据要用到toRef,同样需要导入:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    电子书列表界面展示

    在这里插入图片描述

    找Ant Design Vue现成的组件

    在这里插入图片描述
    在这里插入图片描述

    将列表数据按组件样式显示到界面上

    导入图标库:
    在这里插入图片描述

    在这里插入图片描述
    全局使用图标:在这里插入图片描述
    展示数据:
    在这里插入图片描述
    实现效果:
    在这里插入图片描述
    实现栅格列表:
    在这里插入图片描述
    在这里插入图片描述
    但是我们后端的service代码是写死的,只能根据传进来的name,进行查询,我们得修改为动态sql,name不为空我们才通过name查找:
    在这里插入图片描述

    这时候我们就可以请求所有的数据了:
    在这里插入图片描述
    我们下面来修改图标
    在这里插入图片描述
    在这里插入图片描述
    效果:
    在这里插入图片描述
    修改代码:
    在这里插入图片描述
    实现效果:
    在这里插入图片描述

    Vue CLI多环境配置

    增加开发和生产环境配置文件

    在web目录下
    新增开发环境配置文件:
    在这里插入图片描述
    在这里插入图片描述
    新增生产环境配置文件:
    在这里插入图片描述
    在这里插入图片描述

    修改编译和启动支持多环境

    修改package.json:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    我们可以在启动参数后面添加端口号修改端口(默认是8080):
    在这里插入图片描述

    修改axios请求地址支持多环境


    axios全局配置,写在main.ts中:

    在这里插入图片描述
    这时候我们请求的时候就可以不用写域名了:
    在这里插入图片描述

    使用Axios拦截器打印前端日志

    配置axios拦截器打印前端日志

    在我们的网络请求中,如果我们想知道每个请求返回的数据,那我们每个请求都需要写打印函数,所以axios就提供了一个拦截器,可以让我们把请求参数跟返回参数统一打印出来。同样也是在main.ts中配置:
    在这里插入图片描述
    所以在我们自己的代码中就不需要再去打印日志了。
    在这里插入图片描述

    SpringBoot过滤器的使用

    配置过滤器,打印接口耗时

    接口耗时在我们应用监控里是一个非常重要的监控点,可以看出应用的处理能力。新建包filter:
    在这里插入图片描述

    // @Component
    // public class LogFilter implements Filter {
    //
    //     private static final Logger LOG = LoggerFactory.getLogger(LogFilter.class);
    //
    //     @Override
    //     public void init(FilterConfig filterConfig) throws ServletException {
    //
    //     }
    //
    //     @Override
    //     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    //         // 打印请求信息
    //         HttpServletRequest request = (HttpServletRequest) servletRequest;
    //         LOG.info("------------- LogFilter 开始 -------------");
    //         LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
    //         LOG.info("远程地址: {}", request.getRemoteAddr());
    //
    //         long startTime = System.currentTimeMillis();
    //         filterChain.doFilter(servletRequest, servletResponse);
    //         LOG.info("------------- LogFilter 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
    //     }
    // }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    选择一个接口测试:
    在这里插入图片描述

    SpringBoot拦截器的使用

    配置拦截器,打印接口耗时

    新建一个包interceptor:
    在这里插入图片描述

    package com.jiawa.wiki.interceptor;
    
    import com.alibaba.fastjson.JSON;
    import com.jiawa.wiki.resp.UserLoginResp;
    import com.jiawa.wiki.util.LoginUserContext;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * 拦截器:Spring框架特有的,常用于登录校验,权限校验,请求日志打印
     */
    @Component
    public class LoginInterceptor implements HandlerInterceptor {
    
        private static final Logger LOG = LoggerFactory.getLogger(LoginInterceptor.class);
    
        @Resource
        private RedisTemplate redisTemplate;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 打印请求信息
            LOG.info("------------- LoginInterceptor 开始 -------------");
            long startTime = System.currentTimeMillis();
            request.setAttribute("requestStartTime", startTime);
    
            // OPTIONS请求不做校验,
            // 前后端分离的架构, 前端会发一个OPTIONS请求先做预检, 对预检请求不做校验
            if(request.getMethod().toUpperCase().equals("OPTIONS")){
                return true;
            }
    
            String path = request.getRequestURL().toString();
            LOG.info("接口登录拦截:,path:{}", path);
    
            //获取header的token参数
            String token = request.getHeader("token");
            LOG.info("登录校验开始,token:{}", token);
            if (token == null || token.isEmpty()) {
                LOG.info( "token为空,请求被拦截" );
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                return false;
            }
            Object object = redisTemplate.opsForValue().get(token);
            if (object == null) {
                LOG.warn( "token无效,请求被拦截" );
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                return false;
            } else {
                LOG.info("已登录:{}", object);
                LoginUserContext.setUser(JSON.parseObject((String) object, UserLoginResp.class));
                return true;
            }
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            long startTime = (Long) request.getAttribute("requestStartTime");
            LOG.info("------------- LoginInterceptor 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    //        LOG.info("LogInterceptor 结束");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    拦截器还需要增加一个全局的配置类:
    我们在config包下增加:
    在这里插入图片描述
    在这里插入图片描述

    SpringBootAOP的使用

    配置AOP,打印接口耗时、请求参数、返回参数

    新增一个包aspects
    在这里插入图片描述
    增加AOP的依赖:
    在这里插入图片描述

    package com.jiawa.wiki.aspect;
    
    import com.alibaba.fastjson.JSONObject;
    import com.alibaba.fastjson.support.spring.PropertyPreFilters;
    import com.jiawa.wiki.util.RequestContext;
    import com.jiawa.wiki.util.SnowFlake;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.slf4j.MDC;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.annotation.Resource;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    
    @Aspect
    @Component
    public class LogAspect {
    
        private final static Logger LOG = LoggerFactory.getLogger(LogAspect.class);
    
        /** 定义一个切点 */
        @Pointcut("execution(public * com.jiawa.*.controller..*Controller.*(..))")
        public void controllerPointcut() {}
    
        @Resource
        private SnowFlake snowFlake;
    
        @Before("controllerPointcut()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
    
            // 增加日志流水号
            MDC.put("LOG_ID", String.valueOf(snowFlake.nextId()));
    
            // 开始打印请求日志
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
    
            // 打印请求信息
            LOG.info("------------- 开始 -------------");
            LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
            LOG.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);
            LOG.info("远程地址: {}", request.getRemoteAddr());
    
            RequestContext.setRemoteAddr(getRemoteIp(request));
    
            // 打印请求参数
            Object[] args = joinPoint.getArgs();
    		// LOG.info("请求参数: {}", JSONObject.toJSONString(args));
    
    		Object[] arguments  = new Object[args.length];
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof ServletRequest
                        || args[i] instanceof ServletResponse
                        || args[i] instanceof MultipartFile) {
                    continue;
                }
                arguments[i] = args[i];
            }
            // 排除字段,敏感字段或太长的字段不显示
            String[] excludeProperties = {"password", "file"};
            PropertyPreFilters filters = new PropertyPreFilters();
            PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
            excludefilter.addExcludes(excludeProperties);
            LOG.info("请求参数: {}", JSONObject.toJSONString(arguments, excludefilter));
        }
    
        @Around("controllerPointcut()")
        public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            long startTime = System.currentTimeMillis();
            Object result = proceedingJoinPoint.proceed();
            // 排除字段,敏感字段或太长的字段不显示
            String[] excludeProperties = {"password", "file"};
            PropertyPreFilters filters = new PropertyPreFilters();
            PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
            excludefilter.addExcludes(excludeProperties);
            LOG.info("返回结果: {}", JSONObject.toJSONString(result, excludefilter));
            LOG.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
            return result;
        }
    
        /**
         * 使用nginx做反向代理,需要用该方法才能取到真实的远程IP
         * @param request
         * @return
         */
        public String getRemoteIp(HttpServletRequest request) {
            String ip = request.getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            return ip;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    总结

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    刘韧:接近聪明人最容易变聪明
    RK3399平台开发系列讲解(PCI/PCI-E)5.51、PCIE EP模式软件架构
    国内如何购买Google Colab会员
    Android面试题——高级开发面试题二
    网络安全(黑客)自学
    [JS真好玩] 嘘!我改了掘金源代码!1行代码,让表格支持page_size切换,从每页10条变为20条!
    iOS 16 中 SwiftUI 4.0 轻松实现导航栏标题可编辑
    支持百万并发的服务器测试
    低代码平台,业务开发的“银弹”
    HashMap原理
  • 原文地址:https://blog.csdn.net/Lbsssss/article/details/127659889