SpringBoot+Vue入门

目录

SpringBoot+Vue

1.SpringBoot快速上手

1.1SpringBoot介绍

Spring Boot是由Pivotal团队提供的基于Spring的全新框架,旨在简化Spring应用的初始搭建和开发过程(Spring+SpringMVC+MyBatis简称SSM)。

Spring Boot是所有基于Spring开发项目的起点。

Spring Boot就是尽可能地简化应用开发的门槛,让应用开发、测试、部署变得更加简单。

1.1.1SpringBoot特点

遵循“约定优于配置”的原则,只需要很少的配置或使用默认的配置。

能够使用内嵌的Tomcat、Jetty服务器,不需要部署war文件。

提供定制化的启动器Starters,简化Maven配置,开箱即用。纯Java配置,没有代码生成,也不需要XML配置。

提供了生产级的服务监控方案,如安全监控、应用监控健康检测等。

1.2快速创建SpringBoot应用

    image-20230102105145348

      image-20230102105631312

      1.3开发环境热部署

      1.4系统配置

      2.web开发基础

      2.1Web入门

      1. Spring Boot将传统Web开发的mvc、json、tomcat:等框架整合,提供了spring-boot-starter-web组件,简化了Web应用配置
      2. 创建SpringBoot项目勾选Spring Web选项后,会自动将spring-boot-starterweb组件加入到项目中。
      3. spring-boot-starter-web启动器主要包括web、webmvc、json、tomcat等基础依赖组件,作用是提供Web开发场景所需的所有底层依赖。
      4. webmvc为Web开发的基础框架,json为JSON数据解析组件,tomcat为自带的容器依赖。

      image-20230102113758358

      2.1.1控制器

      1. Spring Boot提供了@Controller和@RestController两种注解来标识此类负责接收和处理HTTP请求。
      2. 如果请求的是页面和数据,使用@Controller注解即可;如果只是请求数据,则可以使用@RestController注解。

      image-20230102113925548

      2.1.2@Controller用法

      示例中返回了hello页面和name的数据,在前端页面中可以通过${name}参数获取后台返回的数据并显示。

      @Controller通常与Thymeleaf模板引擎结合使用。

      image-20230102114431633

      2.2.3@ RestController的用法

      默认情况下,@RestController注解会将返回的对象数据转换为JSON格式。

      image-20230102114829095

      2.2路由映射

      1. RequestMapping注解主要负责URL的路由映射。它可以添加在Controller类或者具体的方法上。

      2. 如果添加在Controller类上,则这个Controller中的所有路由映射都将会加上此映射规则,如果添加在方法上,则只对当前方法生效。

      3. RequestMapping注解包含很多属性参数来定义HIP的有X块羽则。常用的属性参数如下:

        1. value:请求URL的路径,支持URL模板、正则表达式

        2. method: HTTP请求方法。若不设置此参数,任何方法均可请求

          1. HTTP请求Method有GET、POST、PUT、DELETE等方式。HTTP支持的全部Method。
          2. @RequestMapping注解提供了method参数指定请求的Method类型,包括RequestMethod.GET、RequestMethod.POST、RequestMethod.DELETE、RequestMethod.PUT等值,分别对应HTTP请求的Method
          3. Method匹配也可以使用@GetMapping、@PostMapping等注解代替。
            @GetMapping=@RequestMapping(value = “/getData” ,method = RequestMethod.GET)

          image-20230102192925860

        3. consumes:请求的媒体类型(Content-Type), 如application/json

        4. produces:响应的媒体类型

        5. params,headers:请求的参数及请求头的值

      4. @RequestMapping的value属性用于匹配URL映射,value支持简单表达式RequestMapping(“/user”)

      5. @RequestMapping支持使用通配符匹配URL,用于统一映射某些URL规则类似的请求:@RequestMapping(“/getJson/.json”),当在浏览器中请求/getJson/a.json或者/getJson/b.json时都会匹配到后台的Json方法

      6. @RequestMapping的通配符匹配非常简单实用,支持”*”、“?”、“**”,等通配符

      7. 符号“*”匹配任意字符,符号“**”匹配任意路径,符号“?”匹配单个字符。

      8. 有通配符的优先级低于没有通配符的,比如/user/add.json比/user/.json优先匹配。

      9. 有“**”通配符的优先级低于有“”通配符的。

      2.3参数传递

      package com.example.helloworld;
      
      import com.example.helloworld.entity.User;
      import org.springframework.web.bind.annotation.*;
      
      @RestController
      public class HelloController {
      /*
      *https://www.baidu.com:80/.../.... https为协议  www.baidu.com为域名(com后面的80为端口,当为80端口时可以省略)域名后面的为路径(上述/hello就是路径)
      * 本地域名为localhost,端口为8080!!
      *  */
      //    @GetMapping("/hello")
      //   @GetMapping("/hello")=@RequestMapping(value = "/hello",method = RequestMethod.GET)  第二个参数为HTTP的访问方法
      //   浏览器将会发送Get请求访问这个方法,括号里面为访问的链接地址
      //   http://localhost:8088/hello?nickname=zhangsan&phone=123456       ?后面为要传的参数  nickname为参数名,zhangsan为值  多个参数时参数之间用&连接
          @RequestMapping(value = "hello",method = RequestMethod.GET)
          public String hello(String phone,String nickname){
              //方法中的参数为浏览器端要传入的参数,参数名称与发送请求时的名称必须一致。(这些参数在发送请求可以不传)
              System.out.println(nickname);
              System.out.println(phone);
              return "你好"+nickname+"phone:"+phone;
          }
      
          @RequestMapping(value = "helloTest1",method = RequestMethod.GET)
          public String helloTest1(@RequestParam(value = "phone",required = false) String p, @RequestParam(value = "nickname") String n){
              //方法中参数名称与发送请求时的名称不一致。http://localhost:8088/helloTest1?nickname=zhangsan
              //请求时参数nickname与方法形参n映射,请求时参数phone与方法形参p映射
              //在这种写法中,请求时phone可以不传,nickname必须传,因为required默认为true
              System.out.println(n);
              System.out.println(p);
              return "你好"+n+"phone:"+p;
          }
      
          @RequestMapping(value = "helloTest2",method = RequestMethod.GET)
          public String helloTest2(User user){
              //user类有两个私有属性username和password并实现了他们的get、set方法以及user类的toString方法
              //发送请求时参数名与user类属性名保持一致就可以自动封装成user类(传参可缺省)
              //http://localhost:8088/helloTest2?username=zhangsan&password=******
              System.out.println(user.getUsername());
              System.out.println(user.getPassword());
              return user.toString();
          }
      
          @RequestMapping(value = "helloTest3",method = RequestMethod.POST)
          //由于method为POST,而浏览端只能发送GET请求,需要借助Apipost软件来发送POST请求
          public String  helloTest3(String phone,String nickname){
              System.out.println(nickname);
              System.out.println(phone);
              return "你好"+nickname+"phone:"+phone;
          }
          
          @RequestMapping(value = "helloTest4",method = RequestMethod.POST)
          //由于method为POST,而浏览端只能发送GET请求,需要借助Apipost软件来发送POST请求
          //若要用Body+json形式发送POST请求,需要在方法形参前加上@RequestBody注解
          public String  helloTest4(@RequestBody User user){
              System.out.println(user);
              System.out.println(user);
              return user.toString();
          }
      }
      

      Apipost发送post请求的方法

      ①采用Body与x-www-form-urlencoded的形式

      image-20230102220950296

      ②采用Query的形式

      image-20230102221256376

      ③采用Body与json的形式

      image-20230102222632879

      2.4数据响应

      3.web开发进阶

      3.1静态资源访问

      1. 使用IDEA创建Spring Boot项目,会默认创建出classpath:/static/目录,静态资源(图片、css、js等)一般放在这个目录下即可。
      2. 如果默认的静态资源过滤策略不能满足开发需求,也可以自定义静态资源过滤策略。
      3. 在application.properties中直接定义过滤规则和静态资源位置:

      image-20230103101115366

      • 过滤规则为/static/**,静态资源位置为classpath:/static

      3.2文件上传

      3.2.1文件上传原理

      • 表单的enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。

      • 当表单的enctype=“application/x-www-form-urlencoded”(默认)时,form表单中的数据格式为: key=value&key=value

      • 当表单的enctype=”multipart/form-data”时,其传输数据形式如下

      3.2.2SpringBoot实现文件上传

      • Spring Boot工程嵌入的tomcat限制了请求的文件大小。每个文件的配置最大为1Mb,单次请求的文件的总数不能大于10Mb。

      • 要更改这个默认值需要在配置文件(如application.properties)中加入两个配置

      #设置上传的每个文件最大为10MB 默认1MB
      spring.servlet.multipart.max-file-size=10MB
      #设置单次上传所有文件总共不能超过50MB 默认10MB
      spring.servlet.multipart.max-request-size=50MB
      
      package com.example.helloworld;
      
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.RestController;
      import org.springframework.web.multipart.MultipartFile;
      
      import javax.servlet.http.HttpServletRequest;
      import java.io.File;
      import java.io.IOException;
      
      //文件上传
      @RestController
      public class FileUploadController {
          @PostMapping(value = "/upload")//发送请求时表单类型必须是form-data类型,注意参数名保持一致
          public String up(String nickname, MultipartFile photo, HttpServletRequest request) throws IOException{
              //HttpServletRequest参数在发送请求时不用管,系统会自动传
              System.out.println(nickname);
              //获取图片原始名称
              System.out.println(photo.getOriginalFilename());
              //获取文件类型
              System.out.println(photo.getContentType());
              //getServletContext().getRealPath获取Tomcat运行时服务器路径
              //在使用getServletContext.getRealPath()时,传入的参数是从当前servlet部署在tomcat中的文件夹算起的相对路径,可以自定义
              // 要以"/" 开头,否则会找不到路径,导致NullPointerException
              String path=request.getServletContext().getRealPath("/upload/");
              System.out.println(path);
              saveFile(photo,path);
              return "Upload Success!";
          }
      
          public void saveFile(MultipartFile photo, String path) throws IOException{
              File dir = new File(path);
              //判断存储的目录是否存在,如果不存在则创建
              if(!dir.exists()){
                  //创建目录
                  dir.mkdir();
              }
              //getOriginalFilename()获取文件名称。存储文件时需存储路径+文件名
              File file=new File(path+photo.getOriginalFilename());
              //transferTo(file)自动将该文件存储到file路径
              photo.transferTo(file);
          }
      }
      /*
      最终输出:
      zhangsan
      QQ图片20201117170834.jpg
      image/jpeg
      C:\Users\zhy\AppData\Local\Temp\tomcat-docbase.8088.8922082603726198350\upload\
      注意:本地服务器地址tomcat-docbase.8088.8922082603726198350每次运行都会变,部署到云端后服务地址就不改变了
       */
      
      

      用Apipost发送post请求

      image-20230103112750941

      3.3拦截器

      拦截器在Web系统中非常常见,对于某些全局统一的操作,我们可以把它提取到拦截器中实现。总结起来,拦截器大致有以下几种使用场景:

      1. 权限检查:如登录检测,进入处理程序检测是否登录,如果没有,则直接返回登录页面。
      2. 性能监控:有时系统在某段时间莫名其妙很慢,可以通过拦截器在进入处理程序之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间
      3. 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有提取Locale、Theme信息等,只要是多个处理程序都需要的,即可使用拦截器实现。

      Spring Boot定义了HandlerInterceptor接口来实现自定义拦截器的功能

      Handlerlnterceptor接口定义了preHandle、postHandle、afterCompletion三种方法,通过重写这三种方法实现请求前、请求后等操作

      image-20230103172525156

      3.3.1拦截器的使用

      ①首先自定义一个java类实现HandlerInterceptor

      package com.example.helloworld.Interceptor;
      
      import org.springframework.web.servlet.HandlerInterceptor;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      
      //拦截器 发送的请求先经过拦截器,当preHandle返回true时,才会将请求发给后端
      //如有需要也可以重写postHandle和afterCompletion方法
      public class LoginInterceptor implements HandlerInterceptor {
          @Override
          //在请求处理之前进行调用(controller方法调用之前)
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              if(条件){
                  System.out.println("通过");
                  return true;
              }else{
                  System.out.println("不通过");
                  return false;
              }
          }
      }
      

      ②注册拦截器

      • addPathPatterns方法定义拦截的地址
      • excludePathPatterns定义排除某些地址不被拦截
      • 添加的一个拦截器没有addPathPattern任何一个url则默认拦截所有请求
      • 如果没有excludePathPatterns任何一个请求,则默认不放过任何一个请求。
      package com.example.helloworld.config;
      
      import com.example.helloworld.Interceptor.LoginInterceptor;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
      import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
      
      @Configuration//加上这个注解SpringBoot才会自动读取这个类,类里面对拦截器的配置才会生效
      public class WebConfig implements WebMvcConfigurer {
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              //在这里对拦截器进行配置,不配置,拦截器无法使用
              //参数为对 服务器配置路径/ueser/下的所有路径进行拦截.   当参数为空时,对所有路径都进行拦截
              registry.addInterceptor(new LoginInterceptor()).addPathPatterns();
          }
      }
      

      4.RESTful服务+Swagger

      4.1RESTful介绍

      • RESTful是目前流行的互联网软件服务架构设计风格。
      • REST (Representational State Transfer,表述性状态转移)一词是由RoyThomas Fielding在2000年的博士论文中提出的,它定义了互联网软件服务的架构原则,如果一个架构符合REST原则,则称之为RESTful架构。
      • REST并不是一个标准,它更像一组客户端和服务端交互时的架构理念和设计原则,基于这种架构理念和设计原则的Web API更加简洁,更有层次。

      4.1.1RESTful特点

      • 每一个URI(链接)代表一种资源
      • 客户端使用GET、POST、PUT、DELETE四种表示操作方式的动词对服务端资源进行操作:GET用于获取资源,POST用于新建资源(也可以用于更新资源),PUT用于更新资源,DELETE用于删除资源。
      • 通过操作资源的表现形式来实现服务端请求操作。资源的表现形式是JSON或者HTML。
      • 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都包含必需的信息。

      4.1.2RESTful APl

      符合RESTful规范的Web API需要具备如下两个关键特性:

      • 安全性:安全的方法被期望不会产生任何副作用,当我们使用GET操作获取资源时,不会引起资源本身的改变,也不会引起服务器状态的改变。
      • 幂等性:幂等的方法保证了重复进行一个请求和一次请求的效果相同(并不是指响应总是相同的,而是指服务器上资源的状态从第一次请求后就不再改变了),在数学上幂等性是指N次变换和一次变换相同。

      4.1.3HTTP Method

      • HTTP提供了POST、GET、PUT、DELETE等操作类型对某个Web资源进行Create、Read、Update和Delete操作。
      • 一个HTTP请求除了利用URI标志目标资源之外,还需要通过HTTP Method指定针对该资源的操作类型,一些常见的HTTP方法及其在RESTful风格下的使用:
      • image-20230103182904564

      4.1.4SpringBoot实现RESTful APl

      Spring Boot提供的spring-boot-starter-web组件完全支持开发RESTful API,提供了与REST操作方式(GET、POST、PUT、DELETE)对应的注解。

      • @GetMapping:处理GET请求,获取资源。
      • @PostMapping:处理POST请求,新增资源。
      • @PutMapping:处理PUT请求,更新全部资源。
      • @DeleteMapping:处理DELETE请求,删除资源。
      • @PatchMapping:处理PATCH请求,用于部分更新资源。
      • 也可以使用@RequestMapping

      在RESTful架构中,每个网址代表一种资源,所以URI中建议不要包含动词,只包含名词即可,而且所用的名词往往与数据库的表格名对应。

      用户管理模块API示例:

      image-20230104091646207

      package com.example.helloworld.Controller;
      
      import com.example.helloworld.entity.User;
      import org.springframework.web.bind.annotation.*;
      
      //RESTful 风格:路径中没有动词。
      // GetMapping只用于获取数据。PostMapping只用于添加数据。PutMapping只用于更新数据。DeleteMapping只用于删除数据。
      @RestController
      //局部配置Cors。仅为此控制器配置Cors,由于在CorsConfig中已经全局配置了,故此不需要局部配置
      //@CrossOrigin
      public class UserController {
      
          @GetMapping(value = "/user/{id}")
          //如果没有@PathVariable,方法只能获取到路径上的?后面的东西,没办法获取到{}中的。 在浏览器输入请求时不写{}
          public String getUserById(@PathVariable int id){
              return "根据ID获取用户信息  "+id;
          }
      
          @GetMapping(value = "/user/message")
          public String getUserMessage(){
              return "All User Message";
          }
      
          @PostMapping(value = "/user")//要传的数据放到请求体中
          public String save(User user){ return "添加用户"; }
      
          @PutMapping("/user")//要传的数据放到请求体中
          public String update(User user){ return "更新用户"; }
      
          @DeleteMapping("/user/{id}")
          public String deleteById(@PathVariable int id){ return "删除用户:"+id; }
      }
      
      

      4.2构建RESTful应用接口

      4.3使用Swagger生成Web API文档

      Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风,格的Web服务,是非常流行的API表达工具。

      Swagger能够自动生成完善的RESTful API文档,,同时并根据后台代码的修改同步更新,同时提供完整的测试页面来调试API.

      在Spring Boot项目中集成Swagger同样非常简单

      ①在项目pom.xml中引入springfox-swagger2和springfox-swagger-ui依赖即可。

      <!-- 添加swagger2相关功能 用于自动生成API文档 -->
      <dependency>
          <groupId>io.springfox</groupId>
          <artifactId>springfox-swagger2</artifactId>
          <version>2.9.2</version>
      </dependency>
      <!--  添加swagger-ui相关功能 -->
      <dependency>
          <groupId>io.springfox</groupId>
          <artifactId>springfox-swagger-ui</artifactId>
          <version>2.9.2</version>
      </dependency>
      

      ②Spring Boot 2.6.X后与Swagger有版本冲突问题,需要在application.properties中加入以下配置:

      spring.mvc.pathmatch.matching-strategy=ant_path_matcher
      

      ③创建Swagger配置类

      package com.example.helloworld.config;
      
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import springfox.documentation.builders.ApiInfoBuilder;
      import springfox.documentation.builders.PathSelectors;
      import springfox.documentation.builders.RequestHandlerSelectors;
      import springfox.documentation.service.ApiInfo;
      import springfox.documentation.spi.DocumentationType;
      import springfox.documentation.spring.web.plugins.Docket;
      import springfox.documentation.swagger2.annotations.EnableSwagger2;
      
      //启动项目访问http://127.0.0.1:8080/swagger-ui.html   即可打开自动生成的可视化测试页面
      @Configuration//告诉Spring容器这个类是一个配置类
      @EnableSwagger2//启动Swagger2
      public class Swagger2Config {
          //配置Swagger2相关的bean
          @Bean
          public Docket creatRestApi(){
              return new Docket(DocumentationType.SWAGGER_2)
                      .apiInfo(apiInfo())
                      .select()
                      .apis(RequestHandlerSelectors.basePackage("com"))//com包下所有的API都交给Swagger2扫描、管理
                      .paths(PathSelectors.any()).build();
          }
      
          //API文档页面显示信息
          private ApiInfo apiInfo() {
              return new ApiInfoBuilder()
                      .title("演示项目API")//标题
                      .description("演示项目")//描述
                      .version("1.0")//版本
                      .build();
          }
      
      }
      
      

      ④浏览器访问http://localhost:端口号/swagger-ui.html即可访问Swagger生成的API文档

      4.3.1Swagger常用注解

      Swagger提供了一系列注解来描述接口信息,包括接口说明、请求方法、请求参数、返回信息等

      image-20230104104604002

      举例

          @RequestMapping(value = "helloTest1",method = RequestMethod.GET)
          @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "电话", required = false, dataType = "String"),@ApiImplicitParam(name = "nickname", value = "姓名", required = true, dataType = "String")})
          @ApiOperation("获取用户")
          public String helloTest1(@RequestParam(value = "phone",required = false) String p, @RequestParam(value = "nickname") String n){
              System.out.println(n);
              System.out.println(p);
              return "你好"+n+"phone:"+p;
          }
      

      image-20230104110420056

      5.MybatisPlus快速上手

      5.1ORM介绍

      ORM (Object Relational Mapping,对象关系映射)是为了解决面向对象与关系数据库存在的互不匹配现象的一种技术。

      ORM通过使用描述对象和数据库之间映射的元数据将程序中的对象自动持久化到关系数据库中。

      ORM框架的本质是简化编程中操作数据库的编码。

      image-20230104111623259

      5.2MyBatis-Plus介绍

      MyBatis是一款优秀的数据持久层ORM框架,被广泛地应用于应用系统。

      MyBatis能够非常灵活地实现动态SQL,可以使用XML或注解来配置和映射原生信息,能够轻松地将Java的POJO (Plain Ordinary Java Object,普通的Java对象)与数据库中的表和字段进行映射关联。

      MyBatis-Plus是一个MyBatis 的增强工具,在MyBatis的基础上做了增强,简化了开发。

      5.3MyBatis-Plus CRUD操作

      5.3.1SpringBoot与MyBatis-Plus整合

      5.3.1.1用Spring Initializr新建SpringBoot项目

      image-20230107164809840

      Developer Tools选项卡中选择Spring Boot DevTools热部署工具和Lombok工具

      img

      web选项卡中选择Spring Web

      image-20230107165815420

      在SQL选项卡中选择MySQL Driver

      image-20230107165844911

      5.3.1.2加入依赖

      在pom.xml中加入依赖

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <!--注意修改一下版本号2.6.4-->
              <version>2.6.4</version>
      <!--        <version>2.7.2</version>-->
      <!--        <version>3.0.1</version>-->
              <relativePath/> <!-- lookup parent from repository -->
          </parent>
          <groupId>com.example</groupId>
          <artifactId>mpdemo</artifactId>
          <version>0.0.1-SNAPSHOT</version>
          <name>mpdemo</name>
          <description>Demo project for Spring Boot</description>
          <properties>
              <java.version>17</java.version>
          </properties>
          <dependencies>
      <!--test-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
              </dependency>
      <!--热部署-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-devtools</artifactId>
                  <scope>runtime</scope>
                  <optional>true</optional>
              </dependency>
      
              <!-- MyBatisP1us依赖-->
              <dependency>
                  <groupId>com.baomidou</groupId>
                  <artifactId>mybatis-plus-boot-starter</artifactId>
                  <version>3.5.1</version>
              </dependency>
              <!--mysql驱动依赖-->
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.28</version>
                  <scope>runtime</scope>
              </dependency>
      
              <!-- 数据连接池 druid-->
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>druid-spring-boot-starter</artifactId>
                  <version>1.1.20</version>
              </dependency>
      
      <!--        lombok-->
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <optional>true</optional>
              </dependency>
      
      <!--        web-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
      
          </dependencies>
      
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-maven-plugin</artifactId>
                  </plugin>
              </plugins>
          </build>
      
      </project>
      
      
      5.3.1.3全局配置

      在Resource文件夹下新建File(yml文件)配置数据库相关信息,并将自动生成的properties文件删除

      spring:
        datasource:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/mydb?serverTimezone=GMT%2B8
          username: root
          password: 123
      mybatis-plus:
        configuration:
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      
      5.3.1.4新建与数据库对应的实体类

      一般新建一个entity包用来存放与数据库对应的实体类

      package com.example.mpdemo.entity;
      
      import lombok.Data;
      
      /**
       * @author zhy
       * @create 2023-01-04 17:42
       */
      //与数据库中的表做映射。类中的属性要和表中的字段对应
      //@Data//有了该注解后,该类的get、set、toString、equals等方法不用再写,由lombok自动生成
      public class User {
          private int id;
          private String username;
          private String password;
          private String birthday;
      
          public int getId() {
              return id;
          }
      
          public void setId(int id) {
              this.id = id;
          }
      
          public String getUsername() {
              return username;
          }
      
          public void setUsername(String username) {
              this.username = username;
          }
      
          public String getPassword() {
              return password;
          }
      
          public void setPassword(String password) {
              this.password = password;
          }
      
          public String getBirthday() {
              return birthday;
          }
      
          public void setBirthday(String birthday) {
              this.birthday = birthday;
          }
      
          @Override
          public String toString() {
              return "User{" +
                      "id=" + id +
                      ", username='" + username + '\'' +
                      ", password='" + password + '\'' +
                      ", birthday='" + birthday + '\'' +
                      '}';
          }
      }
      
      
      5.3.1.5创建操作数据库的接口

      一般新建一个mapper包,该包下存放操作数据库的接口

      package com.example.mpdemo.mapper;
      
      import com.baomidou.mybatisplus.core.mapper.BaseMapper;
      import com.example.mpdemo.entity.User;
      import org.apache.ibatis.annotations.Mapper;
      import org.apache.ibatis.annotations.Select;
      import org.mybatis.spring.annotation.MapperScan;
      import org.springframework.stereotype.Service;
      
      import java.util.List;
      
      /**
       * 操作数据库的接口命名一般为:要操作的表名+Mapper。
       * 该接口不用自己实例化,实例化过程由SpringBoot+MyBatis完成。但是使用之前需要Spring来注入实例
       * @author zhy
       * @create 2023-01-04 17:38
       */
      public interface UserMapper  {
          //查询所有用户
          @Select("select * from user")
          List<User> find();
      }
      
      //也可以直接继承BaseMapper(该类实现了对数据库增删改查的所有操作,可以直接调用)
      //public interface UserMapper  extends BaseMapper<User>{
      //
      //}
      
      5.3.1.6新建Controller

      一般新建controller包用来存放控制器

      package com.example.mpdemo.controller;
      
      import com.example.mpdemo.entity.User;
      import com.example.mpdemo.mapper.UserMapper;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      import javax.annotation.Resource;
      import java.util.List;
      
      /**
       * @author zhy
       * @create 2023-01-04 17:34
       */
      @RestController
      public class UserController {
      
          @Resource//Spring自动注入UserMapper实例
          private UserMapper userMapper;
      
          @GetMapping("/users")
          public Object query(){
              return userMapper.find();
          }
      }
      
      5.3.1.7在主类前添加@MapperScan注解
      @SpringBootApplication
      @MapperScan("com.example.mpdemo.mapper")
      //指定扫描那个包,与数据库映射的类一般放在mapper包。注意不要自己写路径,右键mapper包→Copy→Copy Reference
      public class MpdemoApplication {
          public static void main(String[] args) {
              SpringApplication.run(MpdemoApplication.class, args);
          }
      }
      
      Mybatis CRUD注解

      image-20230104164812467

      CRUD操作

      image-20230107171539079

      5.4多表查询及分页查询

      实现复杂关系映射,可以使用@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置。(以上配置均有mybatis提供)

      image-20230107203437684

      5.4.1多表查询

      实现通过用户查询所有用户及其订单信息,以及通过订单查询所有订单及用户信息的多表查询

      5.4.1.1创建实体类User和Order
      //与数据库中的表做映射。类中的属性要和表中的字段对应
      //@Data//有了该注解后,该类的get、set、toString、equals等方法不用再写,由lombok自动生成
      //标名需要和类名保持一致(忽略大小写)。如果两者不一样需要在类前面加@TableName("表名")
      @ApiModel("用户对象")
      public class User {
          @ApiModelProperty("用户id")
          private int id;
          //当类中属性与表中对应的字段名不一致时加注解@TableField("对应字段名")
          @ApiModelProperty("用户姓名")
          private String username;
          @ApiModelProperty("用户密码")
          private String password;
          @ApiModelProperty("用户生日")
          private String birthday;
      
          @TableField(exist = false)
          //若类中属性对应的表中并没有对应的字段名加注解@TableField(exist = false)
          //描述用户所有订单
          private List<Order> orders;
          //实现其get、set、toString方法
      }
      
      public class Order {
          private int id;
          private String ordertime;
          private int total;
          private int uid;
          @TableField(exist = false)
          private User user;
          //实现其get、set、toString方法
      }
      
      5.4.1.2创建操作数据库的接口
      public interface UserMapper  {
      
          @Select("select * from user where id=#{id}")
          User selectById(int id);
      
          @Select("select * from user")
          //column的值为数据库中的字段名,property的值为类中的属性名,最终会将查询结果赋值给property
          //javaType为查询到结果的类型,在调用另一个mapper中的方法时必须写
          //@Many表示会查询到多个结果
          @Results(value = {
                  @Result(column = "id",property = "id"),
                  @Result(column = "username",property = "username"),
                  @Result(column = "password",property = "password"),
                  @Result(column = "id",property = "orders",javaType = List.class,
                  many = @Many(select = "com.example.mpdemo.mapper.OrderMapper.selectByUid"))
          })
          List<User> selectAllUserAndOrders();
      }
      
      public interface OrderMapper extends BaseMapper<Order> {
      
          //数据库表名最好不要写成order,因为他是关键字
          @Select("select * from orders where uid = #{uid}")
          List<Order> selectByUid(int uid);
      
          //
          @Select("select * from orders")
          @Results({
                  @Result(column = "id",property = "id"),
                  @Result(column = "ordertime",property = "ordertime"),
                  @Result(column = "total",property = "total"),
                  @Result(column = "uid",property = "uid"),
                  @Result(column = "uid",property = "user",javaType = User.class,
                          one = @One(select = "com.example.mpdemo.mapper.UserMapper.selectById"))
          })
          List<Order> selectAllOrdersAndUser();
      }
      
      5.4.1.3创建Controller
      @RestController
      @Api(tags = {"用户管理控制器"})
      public class UserController {
      
          @Resource//Spring自动注入UserMapper实例
          private UserMapper userMapper;
      
          @GetMapping("/user/findAll")
          @ApiOperation("查询所有用户及订单信息")
          List<User> findAll(){
              return userMapper.selectAllUserAndOrders();
          }
      }
      
      @RestController
      @Api(tags = {"订单管理控制器"})
      public class OrderController {
          @Resource
          private OrderMapper mapper;
      
          @ApiOperation("查询所有订单及其客户")
          @GetMapping("/mapper/findAll")
          public Object findAll(){
              return mapper.selectAllOrdersAndUser();
          }
      }
      

      5.4.2分页查询

      5.1.2.1使用mybatisplus的分页插件

      创建配置类(配置类一般放在config包下)

      @Configuration
      public class MyBatisPlusConfig {
          @Bean
          public MybatisPlusInterceptor paginationInterceptor(){
              MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
              PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
              interceptor.addInnerInterceptor(paginationInterceptor);
              return interceptor;
          }
      }
      

      常见错误

      HTTP状态码

      HTTP状态码就是服务向用户返回的状态码和提示信息,客户端的每一次请求,服务都必须给出回应,回应包括HTTP状态码和数据两部分。
      HTTP定义了40个标准状态码,可用于传达客户端请求的结果。状态码分为以下5个类别:

      • 1xx:信息,通信传输协议级信息
      • 2xx:成功,表示客户端的请求已成功接受
      • 3xx:重定向,表示客户端必须执行一些其他操作才能完成其请求
      • 4xx:客户端错误,此类错误状态码指向客户端
      • 5xx:服务器错误,服务器负责这写错误状态码

      RESTful API中使用HTTP状态码来表示请求执行结果的状态,适用于REST API设计的代码以及对应的HTTP方法。

      image-20230104091250171

      4开头的错误一般都是后端的问题

      **404:**访问路径不对、后端代码修改后没有重启

      **405:**发送请求方式不正确

      **500:**后端程序出现运行时异常

      6.Vue框架快速上手

      6.1前端环境准备

      编码工具:VSCode

      依赖管理:NPM

      项目构建: VueCli

      6.2Vue框架介绍

      Vue (读音/vju:/,类似于view)是一套用于构建用户界面的渐进式框架。

      Vue.js提供了MVVM数据绑定和一个可组合的组件系统,具有简单、灵活的API。

      • MVVM是Model-View-ViewModel的缩写,它是一种基于前端开发的架构模式,其核心是提供对View和ViewModel的双向数据绑定。
      • Vue提供了MVVM风格的双向数据绑定,核心是MVVM中的VM,也就是ViewModel,ViewModel负责连接View和Model,
        保证视图和数据的一致性。
      • image-20230108171729112

      其目标是通过尽可能简单的API实现响应式的数据绑定和可组合的视图组件。

      6.3Vue快速入门

      导入vue.js的script 脚本文件

      在页面中声明一个将要被vue所控制的DOM区域,既MVVM中的View

      创建vm实例对象(vue 实例对象)

      7.Vue组件化开发

      7.1NPM使用

      7.1.1NPM的介绍

      NPM(Node Package Manager)是一个NodeJS包管理和分发工具。

      • Node.js是一个基于Chrome V8引擎的JavaScript
      • Node中包含了NPM包管理工具。
      • 下载地址: https://nodejs.org/zh-cn/

      NPM以其优秀的依赖管理机制和庞大的用户群体,目前已经发展成为整个JS领域的依赖管理工具

      NPM最常见的用法就是用于安装和更新依赖。要使用NPM,首先要安装Noge工具。

      7.1.2NPM的使用

      先安装Node.js

      image-20230108215444212

      7.2Vue CLI使用

      7.2.1Vue CLI介绍

      Vue CLI是Vue官方提供的构建工具,通常称为脚手架。

      用于快速搭建一个带有热重载(在代码修改后不必刷新页面即可呈现修改后的效果)及构建生产版本等功能的单页面应用。

      Vue CLI基于webpack构建,也可以通过项目内的配置文件进行配置。

      7.2.2Vue CLI的安装和使用

      安装: npm install -g @vue/cli

      • 在要建项目的目录下输入cmd打开命令提示窗口
      • 输入npm install -g @vue/cli

      利用Vue CLI新建Vue项目

      • 在上述目录下的cmd窗口中输入vue create 项目名称
      • image-20230108221128204
      • 选择第三个然后回车。因为前两个带有eslint规范,学习阶段会各种报错
      • image-20230108221302224
      • 将Linter/Formatter取消,然后回车。空格键为选中/取消
      • image-20230108221436704
      • 选择Vue3还是Vue2,然后回车。这里选择Vue3
      • image-20230108221703845
      • 选择将依赖信息记录到哪里,然后回车。这里选择package.json
      • image-20230108221836892
      • 是否要将上述配置信息存一个快照(以后可利用此快照快速配置)然后回车。这里选择N
      • 创建成功。在该路径下会创建一个文件夹,名字为刚刚设置的项目名称

      package.json记录了项目的详细信息:项目名称、版本、是否为私有、依赖等等

      • 启动Vue项目:运行serve命令
        • 在项目父目录下进入cmd→进入刚刚新建的项目(hello):cd hello→npm run serve
        • 直接在VScode中运行:查看→终端→npm run serve
        • Ctrl+c关闭本地服务器

      src目录是我们以后写代码的目录

      1. main.js:程序的入口文件。在这里创建了Vue的VM对象

      7.3组件化开发

      组件(Component)是Vue.js最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。

      Vue的组件系统允许我们使用小型、独立和通常可复用的组件构建大型应用。

      7.3.1组件的构成

      Vue 中规定组件的后缀名是.vue

      每个.vue组件都由3部分构成,分别是

      • template,组件的模板结构,可以包含HTML标签及其他的组件
      • script,组件的JavaScript代码
      • style,组件的样式

      7.3.2自定义组件的方法

      1. 一般在component目录下创建.vue文件来自定义组件
      2. 在要使用的该自定义组件的vue文件的script标签中加:import 组件名 from 路径
      3. 在script标签下的components中注册此标签
      <script>
      import HelloWorld from './components/HelloWorld.vue'
      export default {
        components: {
          HelloWorld
        }
      }
      </script>
      

      8.第三方组件element-ui

      8.1组件间的传值

      组件可以由内部的Data提供数据,也可以由父组件通过prop的方式传值。

      <!--Movie.vue-->
      <template>
          <div>
              <h1>{{ title }}</h1>
              <h3>{{ rating }}</h3>
              <button @click="fun">点击收藏!</button>
          </div>
      </template>
      
      <script>
      
      export default{
          name:"Movie",
          // 由父组件将title和rating数据传过来
          props:["title","rating"],
          date:function(){
              return{
              }
          },
          methods:{
              fun(){
                  alert("收藏成功")
              }
          }
      }
      </script>
      
      <!--Appe.vue-->
      <template>
        <div id="app">
          <Movie v-for="movie in movies" :key="movie.id" :title="movie.title" :rating="movie.rating"></Movie>
        </div>
      </template>
      
      <script>
      import Movie from './components/Movie.vue'
      
      export default {
        name: 'App',
        components: {
          Movie
        },
        //组件内部由data提供数据
        data:function(){
          return{
            movies:[
              {id:1,title:"金刚狼1",rating:8.7},
              {id:2,title:"金刚狼2",rating:8.6},
              {id:3,title:"金刚狼3",rating:8.5}
            ]
          }
        }
      }
      </script>
      
      <style>
      #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      </style>
      

      兄弟组件之间可以通过Vuex等统一数据源提供数据共享

      8.2element-ui介绍

      Element是国内饿了么公司提供的一套开源前端框架,简洁优雅,提供了Vue.React、Angular等多个版本。

      文档地址: https://element.eleme.cn/#/zh-CN/

      安装:npm i element-ui

      • install简写为i
      • 安装后在package.json中记录,下载到node_modules路径下
        • 因此在共享项目时无需发送node_modules文件夹,有package.json即可,在终端输入npm install,vscode就会找package.json去下载相关依赖等

      引入 Element:

      • 在main.js中加上

      • import ElementUI from 'element-ui';
        import 'element-ui/lib/theme-chalk/index.css';
        Vue.use(ElementUI);
        
        //main.js
        import Vue from 'vue'
        import App from './App.vue'
        import ElementUI from 'element-ui';
        import 'element-ui/lib/theme-chalk/index.css';
        
        Vue.config.productionTip = false
        Vue.use(ElementUI);//全局注册
        
        new Vue({
          render: h => h(App),
        }).$mount('#app')
        

      8.3组件的使用

      8.4图标的使用

      8.4.1第三方图标库

      由于Element UI提供的字体图符较少,一般会采用其他图表库,如著名的FontAwesome

      Font Awesome提供了675个可缩放的矢量图标,可以使用CSS所提供的所有特性对它们进行更改,包括大小、颜色、阴影或者其他任何支持的效果。

      文档地址: http://fontawesome.dashgame.com/

      安装: npm install font-awesome

      使用时在package.json中引入: import ‘font-awesome/css/font-awesome.min.css’

      9.Axios网络请求

      9.1Axios的使用

      9.1.1Axios简介

      在实际项目开发中,前端页面所需要的数据往往需要从服务器端获取,这必然涉及与服务器的通信。

      Axios是一个基于promise 网络请求库,作用于node.js和浏览器中。

      Axios在浏览器端使用XMLHttpRequests发送网络请求,并能自动完成JSON数据的转换。

      安装: npm install axios

      • 然后可以在main.js中import,也可以在那个组件中使用就在哪个组件中import,路径可以直接写’axios’即可

      地址: https://www.axios-http.cn/

      9.1.2发送get请求

      9.2与Vue整合

      在实际项目开发中,几乎每个组件中都会用到axios 发起数据请求。此时会遇到如下两个问题:
      每个组件中都需要导入axios
      每次发请求都需要填写完整的请求路径可以通过全局配置的方式解决上述问题:

      image-20230113200618373

      9.3跨域

      为了保证浏览器的安全,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源,称为同源策略,同源策略是浏览器安全的基石

      同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能

      所谓同源(即指在同一个域)就是两个页面具有相同的协议( protocol),主机(host)和端口号(port)

      当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域,此时无法读取非同源网页的Cookie,无法向非同源地址发送AJAX请求

      9.3.1跨域问题的解决

      CORS (Cross-Origin Resource Sharing)是由W3C制定的一种跨域资源共享技术标准,其目的就是为了解决前端的跨域请求。

      CORS可以在不破坏即有规则的情况下,通过后端服务器实现CORS接口,从而实现跨域通信。

      CORS将请求分为两类:简单请求和非简单请求,分别对跨域通信提供了支持。

      • 简单请求:
        • 请求方法:GET、POST、HEAD
        • 除了以下的请求头字段之外,没有自定义的请求头:
          • Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type
          • Content-Type的值只有以下三种:text/plain、multipart/form-data、application/x-www-form-urlencoded
        • 对于简单请求,CORS的策略是请求时在请求头中增加一个Origin字段(告诉服务器发送请求的来自那个端口)
        • image-20230113183952993
        • 服务器收到请求后,根据该字段判断是否允许该请求访问,如果允许,则在HTTP头信息中添加Access-Control-Allow-Origin字段。
        • image-20230113184026246
      • 非简单请求
        • 对于非简单请求的跨源请求,浏览器会在真实请求发出前增加一次OPTION请求,称为预检请求(preflight request)
        • 预检请求将真实请求的信息,包括请求方法、自定义头字段、源信息添加到HTTP头信息字段中,询问服务器是否允许这样的操作。
        • 例如一个Get请求
        • image-20230113184101704
        • Access-Control-Request-Method表示请求使用的HTTP方法,Access-Control-Request-Headers包含请求的自定义头字段
        • 服务器收到请求时,需要分别对Origin、Access-Control-Request-Method.Access-Control-Request-Headers进行验证,验证通过后,会在返回HTTP头信息中添加:
          image-20230113184307351
        • Access-Control-Allow-Methods、Access-Control-Allow-Headers:真实请求允许的方法、允许使用的字段
          Access-Control-Allow-Credentials:是否允许用户发送、处理cookie
          Access-Control-Max-Age:预检请求的有效期,单位为秒,有效期内不会重复发送预检请求。
        • 当预检请求通过后,浏览器才会发送真实请求到服务器。这样就实现了跨域资源的请求访问。

      9.3.2SpringBoot配置CORS

      一般在config包下新建一个配置类来配置CORS

      package com.example.mpdemo.config;
      
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.CorsRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
      
      /**
       * @author zhy
       * @create 2023-01-13 18:50
       */
      @Configuration
      public class CorsConfig implements WebMvcConfigurer {
          @Override
          public void addCorsMappings(CorsRegistry registry) {
              registry.addMapping ( "/**")//允许跨域访问的路径
                      .allowedOriginPatterns ("*")//允许跨域访问的源
                      .allowedMethods ( "POST","GET","PUT","OPTIONS","DELETE")//允许请求方法
                      .maxAge (168000)//预检间隔时间
                      .allowedHeaders ("*")//允许头部设置
                      .allowCredentials (true);//是否发送cookie
      
          }
      }
      

      10前端路由VueRouter

      10.1VueRouter安装与使用

      Vue路由vue-router是官方的路由插件,能够轻松的管理SPA项目中组件的切换。

      Vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来

      10.1.1VueRouter的安装

      vue-router目前有3.X的版本和4.X的版本,vue-router 3.x只能结合vue2进行使用,vue-router 4.x只能结合vue3进行使用

      安装:npm install vue-router@4或npm install vue-router@3 (默认安装最新的)

      10.1.2VueRouter的使用

      创建路由组件

      在项目中定义Discover.vue、Friends.vue、My.vue三个组件,将来要使用vue-router来控制它们的展示与切换:

      <!--以下是三个vue文件-->
      
      <!-- Discover.vue -->
      <template>
          <div>
              <h1>发现音乐</h1>
          </div>
      </template>
      
      <!-- Friends.vue -->
      <template>
          <div>
              <h1>关注</h1>
          </div>
      </template>
      
      <!-- My.vue -->
      <template>
          <div>
              <h1>我的音乐</h1>
          </div>
      </template>
      
      声明路由链接和占位标签

      可以使用标签来声明路由链接,并使用标签来声明路由占位符。示例代码如下:

      App.vue:

      <template>
        <div id="app">
          <!-- 声明路由链接 -->
          <router-link to="/discover">发现音乐</router-link>
          <router-link to="/friends">关注</router-link>
          <router-link to="/my">我的音乐</router-link>
          <!-- 声明路由占位标签 -->
          <router-view></router-view>
        </div>
      </template>
      
      创建路由模块

      在项目中创建index.js路由模块,加入以下代码:

      import VueRouter from "vue-router";
      import Vue from "vue";
      import Discover from '../components/Discover.vue'
      import Friends from '../components/Friends.vue'
      import My from '../components/My.vue'
      
      Vue.use(VueRouter)
      
      const router=new VueRouter({
          routes:[
              {path:'/',redirect:"/discover"},//redirect为重定向,使得初始化页面也是有Discover组件的页面
              {path:'/discover',component:Discover},
              {path:'/friends',component:Friends},
              {path:'/my',component:My}
          ]
      })
      
      // 将router对象导出
      export default router
      
      挂载路由模块

      在main.js中导入并挂载router

      import Vue from 'vue'
      import App from './App.vue'
      import router from './router'
      
      Vue.config.productionTip = false
      
      new Vue({
        // 前面是属性名,后面是值
        render: h => h(App),
        router:router
      }).$mount('#app')
      
      vue-router进阶
      路由重定向

      路由重定向指的是:用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面。通过路由规则的redirect属性,指定一个

      新的路由地址,可以很方便地设置路由的重定向:

      const router=new VueRouter({
          //指定hash属性与组件的对应关系
          routes:[
              //当用户访问'/'时,跳转到'/discover'
              {path:'/',redirect:"/discover"},//redirect为重定向,使得初始化页面也是有Discover组件的页面
              {path:'/discover',component:Discover},
              {path:'/friends',component:Friends},
              {path:'/my',component:My}
          ]
      })
      
      嵌套路由

      以Discover为例子,创建toplist和playlist两个子路由。

      首先在components目录下创建TopList.vue和Play.vue两个组件

      在Discover.vue组件中,声明toplist和 playlist的子路由链接以及子路由占位符。示例代码如下:

      <!-- Discover.vue -->
      <template>
          <div>
              <h1>发现音乐</h1>
              <router-link to="/discover/toplist">推荐</router-link>
              <router-link to="/discover/playlist">歌单</router-link>
              <router-view></router-view>
          </div>
      </template>
      

      在src/router/index.js路由模块中,导入需要的组件,并使用children属性声明子路由规则:

      import VueRouter from "vue-router";
      import Vue from "vue";
      import Discover from '../components/Discover.vue'
      import Friends from '../components/Friends.vue'
      import My from '../components/My.vue'
      import TopList from '../components/TopList.vue'
      import PlayList from '../components/PlayList.vue'
      
      Vue.use(VueRouter)
      
      const router=new VueRouter({
          //指定hash属性与组件的对应关系
          routes:[
              //当用户访问'/'时,跳转到'/discover'
              {path:'/',redirect:"/discover"},//redirect为重定向,使得初始化页面也是有Discover组件的页面
              {
                  path:'/discover',
                  component:Discover,
                  children:[
                      {path:"toplist",component:TopList},
                      {path:"playlist",component:PlayList}
                  ]
              },
              {path:'/friends',component:Friends},
              {path:'/my',component:My}
          ]
      })
      
      // 将router对象导出
      export default router
      
      动态路由

      思考:有如下3个路由链接:

      <router-link to="/product/1">商品1</router-link>
      <router-link to="/product/2">商品2</router-link>
      <router-link to="/product/3">商品3</router-link>
      
      const router =new vueRouter({
          //指定hash属性与组件的对应关系
          routes:[
              {path: '/product/1', component: Product },
              {path: '/product/2', component: Product },
              {path: '/product/3', component: Product },
          ]
      })
      
      

      上述方式复用性非常差。
      动态路由指的是:把Hash地址中可变的部分定义为参数项,从而提高路由规则的复用性。在vue-router中使用英文的冒号(:)来定义路由的参数项。示例代码如下:

      {path: '/product/:id', component:Product }
      

      通过动态路由匹配的方式渲染出来的组件中,可以使用$route.params对象访问到动态匹配的参数值,比如在商品详情组件的内部,根据id值,请求不同的商品数据。

      <!--Product.vue-->
      <template>
          <div>
              <h3>商品{{ $route.params.id }}</h3>
          </div>
      </template>
      

      为了简化路由参数的获取形式,vue-router允许在路由规则中开启props传参。示例代码如下:

      <!--Product.vue-->
      <template>
          <div>
              <h3>商品{{ id }}</h3>
          </div>
      </template>
      
      <script>
      export default{
          props:["id"]
      }
      </script>
      
      import VueRouter from "vue-router";
      import Vue from "vue";
      import Discover from '../components/Discover.vue'
      import Friends from '../components/Friends.vue'
      import My from '../components/My.vue'
      import TopList from '../components/TopList.vue'
      import PlayList from '../components/PlayList.vue'
      import Product from '../components/Product.vue'
      
      Vue.use(VueRouter)
      
      const router=new VueRouter({
          //指定hash属性与组件的对应关系
          routes:[
              //当用户访问'/'时,跳转到'/discover'
              {path:'/',redirect:"/discover"},//redirect为重定向,使得初始化页面也是有Discover组件的页面
              {
                  path:'/discover',
                  component:Discover,
                  children:[
                      {path:"toplist",component:TopList},
                      {path:"playlist",component:PlayList}
                  ]
              },
              {path:'/friends',component:Friends},
              {
                  path:'/my',
                  component:My,
                  children:[
                      //:说明后面是动态获取的路由,props:true表示将动态获取的路由作为参数传给组件
                      {path:"product/:id",component:Product,props:true},
                  ]
              }
          ]
      })
      
      // 将router对象导出
      export default router
      
      编程式导航
      声明式导航 编程式导航
      router.push(…)

      除了使用创建a标签来定义导航链接,我们还可以借助router的实例方法,通过编写代码来实现

      想要导航到不同的URL,则使用router.push方法。这个方法会向history栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的URL。

      当你点击时,这个方法会在内部调用,所以说,点击等同于调用router.push(…),

      <!-- My.vue -->
      <template>
          <div>
              <h1>我的音乐</h1>
              <router-link to="/my/product/1">商品1</router-link>
              <router-link to="/my/product/2">商品2</router-link>
              <router-link to="/my/product/3">商品3</router-link>
              <button @click="gotoProduct(2)">跳转到商品2</button>
              <router-view></router-view>
          </div>
      </template>
      
      <script>
      export default{
          methods:{
              gotoProduct:function(id){
                  this.$router.push("/my/product/${id}")
              }
          }
      }
      </script>
      
      导航守卫

      导航守卫可以控制路由的访问权限。示意图如下:

      全局导航守卫会拦截每个路由规则,从而对每个路由进行访问权限的控制。

      你可以使用router.beforeEach 注册一个全局前置守卫:

      image-20230115140151821

      • to:即将要进入的目标

      • from:当前导航正要离开的路由

      • 在守卫方法中如果声明了next形参,则必须调用next()函数,否则不允许用户访问任何一个路由!

        • 直接放行:next()
        • 强制其停留在当前页面:next(false)
        • 强制其跳转到登录页面:next(‘/login’)

      11状态管理Vuex

      11.1Vuex介绍

      对于组件化开发来说,大型应用的状态往往跨越多个组件。在多层嵌套的父子组件之间传递状态已经十分麻烦,而Vue更是没有为兄弟组件提供直接共享数据的办法。

      基于这个问题,许多框架提供了解决方案——使用全局的状态管理器,将所有分散的共享数据交由状态管理器保管,Vue也不例外。

      Vuex是一个专为Vue.js应用程序开发的状态管理库,采用集中式存储管理应用的所有组件的状态。

      简单的说,Vuex用于管理分散在Vue各个组件中的数据

      11.1.1状态管理

      每一个Vuex应用的核心都是一个store,与普通的全局对象不同的是,基于Vue数据与视图绑定的特点,当store中的状态发生变化时,
      与之绑定的视图也会被重新渲染。

      store中的状态不允许被直接修改,改变store中的状态的唯一途径就是显式地提交(commit) mutation,这可以让我们方便地跟踪每一个状态的变化。在大型复杂应用中,如果无法有效地跟踪到状态的变化,将会对理解和维护代码带来极大的困扰。

      Vuex中有5个重要的概念: State、Getter、Mutation.Action、Module。

      image-20230115142516992

      • State用于维护所有应用层的状态,并确保应用只有唯一的数据源(下图以Vuex4为例)

        • image-20230115142757986
      • Mutation提供修改State状态的方法。

        • 在组件中,可以直接使用store.commit来提交mutatiopn
        • 也可以先用mapMutation辅助函数将其映射下来
      • Action类似Mutation,不同在于:

      Action不能直接修改状态,只能通过提交mutation来修改,Action可以包含异步操作

      11.2Vuex安装与使用

      Vuex有3和4两个版本,Vuex3对应Vue2,Vuex4对应Vue3

      安装: npm install vuex@3或npm install vuex@4

      12MockJS

      12.1mockjs介绍

      Mock.js是一款前端开发中拦截Ajax请求再生成随机数据响应的工具,可以用来模拟服务器响应.

      优点是非常简单方便,无侵入性,基本覆盖常用的接口数据类型…

      支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜色等。

      安装: npm install mockjs

      12.2mockjs基本使用

      在项目中创建mock目录,新建index.js文件

      组件中调用mock.js中模拟的数据接口,这时返回的response就是mock.js中用Mock.mock( ‘url’ ,data)中设置的data

      Mock.mock( rurl?, rtype?, templatelfunction(options))

      • rurl,表示需要拦截的URL,可以是URL字符串或URL 正则
      • rtype,表示需要拦截的Ajax请求类型。例如GET、POST、PUT、DELETE等。
      • template,表示数据模板,可以是对象或字符串
      • function,表示用于生成响应数据的函数。
      • 设置延时请求到数据

      12.3数据生成规则

      12.3.1数据模板DTD

      mock的语法规范包含两层规范:

      • 数据模板(DTD)
      • 数据占位符(DPD)

      12.3.2基本语法

      数据模板中的每个属性由3部分构成:属性名name、生成规则rule、属性值value:

      'name|rule' : value
      

      属性名和生成规则之间用竖线|分隔,属性名是自定义的,生成规则是可选的,属性值可以是固定值也可以是一个占位符(@)生成随机的值

      生成规则有7种格式:

      'name|min-max' : value
      'name|count': value
      'name|min-max.dmin-dmax' : value
      'name|min-max.dcount': value
      'name|count.dmin-dmax' : value
      'name|count.dcount' : value
      'name|+step' : value
      
      • 生成规则的含义需要依赖属性值的类型才能确定。
      • 属性值中可以含有@占位符。
      • 属性值还指定了最终值的初始值和类型。

      12.3.3生成规则和示例

      12.3.1.1属性值是字符串String
      //通过重复string生成一个字符串,重复次数大于等于min,小于等于max。
      'name|min-max' : string
      //通过重复string生成一个字符串,重复次数等于count。
      'name|count' : string
      
      var data = Mock.mock({
          'name1|1-3' : 'a',//重复生成1到3个a(随机)
          'name2|2' : 'b' //生成bb
      })
      
      12.3.1.2属性值是数字Number
      //属性值自动加1,初始值为number。
      'name|+1': number
      //生成一个大于等于 min、小于等于max的整数,属性值 number只是用来确定类型。
      'name|min-max' : number
      //生成一个浮点数,整数部分大于等于min、小于等于 max,小数部分保留 dmin到 dmax位。
      'name|min-max.dmin-dmax' : number
      
      Mock.mock({
      	'number1|1-100.1-10' : 1,
          'number2|123.1-10' : 1,
          'number3|123.3': 1,
          'number4|123.10' : 1.123
      })
      //结果:
      {
          "number1": 12.92,
          "number2": 123.51,
          "number3": 123.777,
          "number4": 123.1231091814
      }
      
      var data = Mock .mock({
          'name1|+1':4,
          //生成4,如果循环每次加1
          'name2|1-7 ':2,
          //生成一个数字,1到7之间
          'name3|1-4.5-8':1
          ///生成一个小数,整数部分1到4,小数部分5到8位,数字1只是为了确定类型
      })
      
      12.3.1.3.属性值是布尔型Boolean
      //随机生成一个布尔值,值为 true 的概率是1/2,值为 fa1se的概率同样是1/2。
      'name|1': boo1ean
      //随机生成一个布尔值,值为 value 的概率是 min / (min + max),值为 !value 的概率是 max / (min + max)
      'name|min-mak' : value
      
      var data = Mock . mock({
          'name|1 ' :true,
          //生成一个布尔值,各一半
          'name1|1-3' :true
          //1/4是true,3/4是false
      })
      
      12.3.1.4.属性值是对象Object
      //从属性值 object中随机选取count个属性。
      'name|count' : object
      //从属性值 object中随机选取min到max个属性。
      'name|min-max' : object
      
      var obj = {
      	a:1,
          b:2,
          c:3,
          d:4
      }
      var data = Mock .mock({
          'name|1-3 ' :obj,
          //随机从obj中寻找1到3个属性,新对象
          'name|2 ' :obj
          //随机从onj中找到两个属性,新对象
      })
      
      12.3.1.5.属性值是数组Array
      //从属性值array中随机选取1个元素,作为最终值。
      'name|1' : array
      //从属性值array中顺序选取1个元素,作为最终值。
      'name|+1': array
      //通过重复属性值 array 生成一个新数组,重复次数大于等于 min,小于等于max。
      'name|min-max ' : array
      //通过重复属性值array生成一个新数组,重复次数为count。
      'name|count' : array
      
      Mock .mock ({
      	//通过重复属性值array生成一个新数组,重复次数为1-3次。
          "favorite_games |1-3": [3,5 ,4,6,23,28,42,45],
      });
      
      var arr = [1,2,3];var data = Mock.mock({
          'name1|1' :arr,
          //从数组里随机取出1个值
          'name2|2 ' :arr,
          //数组重复count次,这里count为2
          'name3|1-3 ' :arr,
          //数组重复1到3次
      }
      
      12.3.1.6.属性值是函数Function
      //执行函数 function,取其返回值作为最终的属性值,函数的上下文为属性 'name’所在的对象。
      'name' : function
      
      var fun = function(x){
      	return x+10;
      }
      var data = Mock .mock(i
          'name' :fun(10)
          //返回函数的返回值20
      })
      

      12.3.2数据占位符DPD

      占位符只是在属性值字符串中占个位置,并不出现在最终的属性值中。占位符的格式为:

      @占位符
      @占位符(参数[,参数])
      

      关于占位符需要知道以下几点

      1. ·用@标识符标识后面的字符串是占位符
      2. ·占位符引用的是Mock .Random中的方法。
      3. 可以通过Mock.Random.extend()来扩展自定义占位符。
      4. ·占位符也可以引用数据模板中的属性。
      5. ·占位符会优先引用数据模板中的属性。
      6. ·占位符支持相对路径和绝对路径。
      //引入mockjs
      import Mock from 'mockjs '//使用mockjs模拟数据
      Mock.mock ( '/api/msdk/proxy/query_common_credit', {
      "ret": 0,
      "data" :
      {
          "mtime" : "@datetime ",//随机生成日期时间
          "score" : "@natura1(1,800)",//随机生成1-800的数字
          "rank" :"anatura7(1,100)",//随机生成1-100的数字
          "stars " : "@natura1(o,5)",//随机生成1-5的数字
          "nickname " : "@cname " ,//随机生成中文名字
      }
      });
      

      13企业级后台集成方案

      13.1vue-element-admin介绍

      vue-element-admin是一个后台前端解决方案,它基于vue和element-ui实现。

      内置了i18国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件。

      可以快速搭建企业级中后台产品原型。

      地址: https://panjiachen.github.io/vue-element-admin-site/zh/guide/

      13.2安装与使用

      见官网

      13.3源码解读

      14跨域认证

      14.1Session认证

      互联网服务离不开用户认证。一般流程是下面这样。

      1. 用户向服务器发送用户名和密码。
      2. 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等。
      3. 服务器向用户返回一个session_id,写入用户的Cookie。
      4. 用户随后的每一次请求,都会通过Cookie,将session_id传回服务器。
      5. 服务器收到session_id,找到前期保存的数据,由此得知用户的身份。

      session认证的方式应用非常普遍,但也存在一些问题,扩展性不好,如果是服务器集群,或者是跨域的服务导向架构,就要求session数据共享,每台服务器都能够读取session,针对此种问题一般有两种方案:

      • 一种解决方案是session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。
      • 一种方案是服务器不再保存session 数据,所有数据都保存在客户端,每次请求都发回服务器。Token认证就是这种方案的一个代表。

      14.2Token认证

      Token是在服务端产生的一串字符串,是客户端访问资源接口(API)时所需要的资源凭证,流程如下:

      1. 客户端使用用户名跟密码请求登录,服务端收到请求,去验证用户名与密码
      2. 验证成功后,服务端会签发一个token 并把这个token发送给
      3. 客户端客户端收到token以后,会把它存储起来,比如放在cookie里或者localStorage里
      4. 客户端每次向服务端请求资源的时候需要带着服务端签发的token
      5. 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求的数据

      Token认证的特点

      • 基于token的用户认证是一种服务端无状态的认证方式,服务端不用存放token 数据。
      • 用解析token 的计算时间换取 session的存储空间,从而减轻服务器的压力,减少频繁的查询数据库
      • token完全由应用管理,所以它可以避开同源策略

      14.3JWT的使用

      JSON Web Token(简称JWT)是一个token的具体实现方式,是目前最流行的跨域认证解决方案。

      JWT的原理是,服务器认证以后,生成一个JSON对象,发回给用户,具体如下:

      {
          "姓名":"张三",
          "角色":"管理员",
          "到期时间": "2018年7月1日0点0分"
      }
      

      用户与服务端通信的时候,都要发回这个JSON对象。服务器完全只靠这个对象认定用户身份。

      为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。

      JWT的由三个部分组成,依次如下:

      1. Header(头部)

        • Header部分是一个JSON对象,描述JWT的元数据

        • {
          	"a1g" : "HS256",
              "typ": "JwT"
          }
          
          • alg属性表示签名的算法(algorithm),默认是HMAC SHA256(写成HS256)
          • typ属性表示这个令牌(token)的类型(type) , JWT令牌统一写为JWT最后,将上面的JSON对象使用Base64URL 算法转成字符串。
      2. Payload(负载)

        • Payload 部分也是一个JSON对象,用来存放实际需要传递的数据。JWT规了7个官方字段,供选用。
          1. iss (issuer):签发人
          2. oexp (expiration time):过期时间
          3. sub (subject):主题
          4. aud (audience):受众
          5. nbf (Not Before):生效时间
          6. iat (lssued At):签发时间
          7. jti (WT ID):编号
        • 注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在个部分。
        • 这个JSON对象也要使用Base64URL算法转成字符串。
      3. Signature(签名)

        • Signature部分是对前两部分的签名,防止数据篡改。

        • 首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户

        • 然后,使用Header里面指定的签名算法(默认是HMAC SHA256),按照下面的公式产生签名。

        • HMACSHA256(
          base64Ur1Encode(header) + "." + base64Ur1Encode(pay1oad),
          secret)
          

      三部分最终组合为完整的字符串,中间使用.分隔,如下:

      Header.Payload.Signature

      image-20230116155352959

      JWT特点

      • 客户端收到服务器返回的JWT,可以储存在Cookie里面,也可以储存在localStorage。
      • 客户端每次与服务器通信,都要带上这个JWT,可以把它放在Cookie里面自动发送,但是这样不能跨域。
      • 更好的做法是放在HTTP请求的头信息Authorization字段里面,单独发送。

      14.3.1JWT的实现

      加入依赖

      <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.1</version>
      </dependency>
      

      生成token

      //7天过期
      private static long expire = 604800;//单位是毫秒
      //32位秘钥
      private static String secret = "*****************" ;//密钥自定义
      //生成token
      public static String generateToken( String username){//参数可以自定义
      Date now = new Date();
      Date expiration = new Date( now.getTime() + 1000*expire);
      return Jwts.builder()
              .setHeaderParam("type", "JWT")//设置Header(头部)  固定的
          	.setSubject(username)//Payload(负载)
              .setIssuedAt (now)//设置生效时间
              .setExpiration(expiration)//设置过期时间
              .signwith(signatureALgorithm.HS512, secret)//第一个参数是枚举类型:生成签名算法,第二个参数是密钥
          	.compact();//合成
      }
      

      解析token

      //解析token
      public static cLaims getclaimsByToken(string token){
      	return Jwts.parser()
      			.setSigningKey(secret)//传入密钥
              	.parseClaimsJws(token)//传入token
              	.getBody();//解析
      }
      

      版权声明:本文为博主作者:又菜又倒霉原创文章,版权归属原作者,如果侵权,请联系我们删除!

      原文链接:https://blog.csdn.net/honor2/article/details/129272215

      共计人评分,平均

      到目前为止还没有投票!成为第一位评论此文章。

      (0)
      社会演员多的头像社会演员多普通用户
      上一篇 2024年1月6日
      下一篇 2024年1月6日

      相关推荐