第四章 前后端相互整合

## 1.集成HTTP库Axios
⭐web项目下安装Axios
```
 npm install axios --save
```
⭐在Home.vue中，通过axios调用电子书列表接口（先把axios引用进来）
```diff
<script lang="ts">
// @ts-nocheck
import { defineComponent,onMounted,ref } from 'vue';
+ import axios from 'axios';

export default defineComponent({
  name: 'Home',

+   setup(){
+    console.log("setup");
    const ebooks = ref();

    onMounted( () =>{
      console.log("onMounted");
+      axios.get("http://localhost:8080/ebook/list?name=Spring").then((response) => {
        const data = response.data;
        console.log(data.content);
        ebooks.value = data.content;
      });
    })
    return{
       ebooks
    }
  }
});
</script>
```
⭐解决跨域问题：在config包下加一个配置类CorsConfig
## 2.Vue3数据绑定显示列表数据
⭐数据绑定是Vue的核心功能，前端拿到后端的数据之后，要把它显示到页面上
⭐初始化的逻辑都写到onMounted方法里，setup就放一些参数定义，方法定义（setup相当于一个入口）
⭐返回的数据：使用{{xxx}}来获取变量（加标签 pre）
```vue
<pre>
        {{ ebooks }}
        {{ ebooks2 }}
</pre>
```
⭐ref：响应式数据 实时更新
⭐data对应的就是统一相应反应结果，content对应的是电子书列表（内容）
```diff
setup(){
    console.log("setup");
+    const ebooks = ref();
-    const ebooks1 = reactive({books:[]});

    onMounted( () =>{
      console.log("onMounted");
      axios.get("http://localhost:8080/ebook/list?name=Spring").then((response) => {
        console.log(response)
+        const data = response.data;
        console.log(data.content);
+        ebooks.value = data.content;
-        ebooks1.books = data.content;
      });
    });
    return{
+       ebooks,
-       ebooks2:toRef(ebooks1,"books")
    }
  }
```
## 3.电子书列表界面展示
①找组件
②将列表数据按组件样式显示到界面上
步骤：
⭐Ant Design Vue 网页中找列表及图标组件，复制代码到Home.vue中
⭐Icon组件需要单独安装：
```vue
 npm install @ant-design/icons-vue --save
```
⭐在main.ts中把所有的图标库导入进来
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/a2cfaa87-61d5-9893-33f5-ac307610658b.png)
⭐ebookService层查询名字改为动态的sql：
```java 
if (!ObjectUtils.isEmpty(req.getName())){
            criteria.andNameLike("%"+req.getName()+"%");
        }
```
## 4.Vue Cli多环境配置
一个项目会发布到很多不同的环境，会针对不同的环境做一些配置。
#### ①增加开发和生产配置文件
web下新建文件:.env.dev 开发环境   .env.prod 生产环境
(.env开头,后面是环境的名称  文件中有环境变量名字及自定义参数
自定义参数以VUE-APP-XXX开头 开发环境和生产环境的参数要一致)  
.env.dev 开发环境
```vue
NODE_ENV=development
VUE_APP_SERVER=http://localhost:8080
```
.env.prod 生产环境
```vue
NODE_ENV=production
VUE_APP_SERVER=http://server.imooc.com
```
#### ②修改编译和启动支持多环境
加上多环境可以读取生产环境和开发环境的变量
执行server命令的时候就会读里面的变量
在package.json文件中
```vue
    "serve-dev": "vue-cli-service serve --mode dev",
    "serve-prod": "vue-cli-service serve --mode prod",
    "build-dev": "vue-cli-service build --mode dev",
    "build-prod": "vue-cli-service build --mode prod",
```
main.ts启动文件中：
使用process.env.xxx读环境变量:配置文件的名称
```vue
console.log('环境:',process.env.NODE_ENV);
console.log('服务端:',process.env.VUE_APP_SERVER);
```
使用--port 修改启动端口(8081)
```vue
"serve-dev": "vue-cli-service serve --mode dev --port 8081",
```
#### ③修改axios请求地址支持多环境
全局配置main.ts文件中
```vue
import axios from 'axios';

axios.defaults.baseURL = process.env.VUE_APP_SERVER;
```
Home.vue中：
```vue
axios.get( "/ebook/list").
```
## 5.使用Axios拦截器打印前端日志
#### 配置axios拦截器打印请求参数和返回参数
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3899383/c250f14b-f7c6-1387-cfe2-8feb9a087263.png)

main.ts文件中写拦截器代码（前端拦截器）：
```vue
/**
 * axios拦截器
 */
axios.interceptors.request.use(function (config) {
    console.log('请求参数：', config);
    return config;
}, error => {
    return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
    console.log('返回结果：', response);
    return response;
}, error => {
    console.log('返回错误：', error);
    return Promise.reject(error);
});
```
## 6.SpringBoot过滤器打印前端日志（后端过滤器）
#### 配置过滤器，打印接口耗时
⭐Component注解：添加此注解，Springboot就会去扫描，容器就会拿到这个过滤器。
wiki下新建过滤器包filter-新建类写过滤器代码
```java
@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);
    }
}
```
## 7.SpringBoot拦截器的使用
wiki下新建拦截器包interceptor-新建类写拦截器代码
```java
/**
 * 拦截器：Spring框架特有的，常用于登录校验，权限校验，请求日志打印 /login
 */
@Component
public class LogInterceptor implements HandlerInterceptor {

    private static final Logger LOG = LoggerFactory.getLogger(LogInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 打印请求信息
        LOG.info("------------- LogInterceptor 开始 -------------");
        LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
        LOG.info("远程地址: {}", request.getRemoteAddr());

        long startTime = System.currentTimeMillis();
        request.setAttribute("requestStartTime", startTime);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        long startTime = (Long) request.getAttribute("requestStartTime");
        LOG.info("------------- LogInterceptor 结束 耗时：{} ms -------------", System.currentTimeMillis() - startTime);
    }
}
```
⭐拦截器还需要添加一个配置类
wiki-config下新建-SpringMvcConfig
```java
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {

    @Resource
    LogInterceptor logInterceptor;

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logInterceptor)
                .addPathPatterns("/**").excludePathPatterns("/login");
    }
}
```
## 8.SpringBoot AOP的使用
#### 配置AOP 打印接口耗时 请求参数 返回参数
wiki下新建包aspect-新建AOP类
```java
@Aspect
@Component
public class LogAspect {

    private final static Logger LOG = LoggerFactory.getLogger(LogAspect.class);

    /** 定义一个切点 */
    @Pointcut("execution(public * com.jiawa.*.controller..*Controller.*(..))")
    public void controllerPointcut() {}

    @Before("controllerPointcut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {

        // 开始打印请求日志
        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());

        // 打印请求参数
        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;
    }
}
```
使用AOP需要在pom文件中增加新的依赖：
```java 
<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.70</version>
</dependency>
```