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请求)
    }

}

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

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);
//     }
// }

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

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 结束");
    }
}

拦截器还需要增加一个全局的配置类:
我们在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;
    }

}

总结

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

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring BootVue3是一种常用的前后分离的技术组合,可以用于构建实战Wiki知识库系统。 首先,采用Spring Boot作为后框架可以快速搭建项目的基本框架和配置,并提供了许多有用的功能和便利的工具。Spring Boot具有自动配置的特点,可以减少开发人员的配置工作,提高开发效率。同时,Spring Boot还提供了丰富的插件和扩展库,可以集成和支持各种数据库、消息队列等常用的后技术。 而Vue3是一种现代的JavaScript框架,用于构建用户界面。相较于Vue2,Vue3在性能和开发体验上都有了显著的提升。Vue3引入了响应式API、组合API等新特性,让开发者能够更容易地编写复杂的交互逻辑和可重用组件。同时,Vue3还优化了虚拟DOM和编译器,提高了渲染性能和项目的整体性能。 在实战Wiki知识库系统中,可以通过前后分离的方式来实现系统的架构。后使用Spring Boot提供数据管理和业务逻辑处理的接口,前使用Vue3进行页面的展示和用户交互。前后通过RESTful API进行通信,实现数据的请求和响应。 在后,可以使用Spring Boot提供的JPA或MyBatis等持久层框架来操作数据库,并使用Spring Security来实现用户认证和权限控制。同时,可以使用Spring的缓存、事务管理等特性来提高系统的性能和安全性。 在前,可以使用Vue3的组件化开发方式构建页面,并使用Vue Router进行页面之间的导航。可以使用Vue3的响应式API和组合API来管理页面的数据和交互逻辑。同时,可以使用Element Plus等常用的UI组件库,提供美观、易用的用户界面。 总而言之,通过使用Spring BootVue3的前后分离技术,可以构建一个功能强大、性能优越的Wiki知识库系统,实现数据管理、用户认证和权限控制等功能,为用户提供高效的知识管理平台。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gujunhe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值