集成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;
}
}
总结