SpringMVC-day02

SpringMVC-day02

01_基于SSM的三层架构

目标

  • 了解基于SSM框架的三层架构技术体系

路径

  1. SSM框架的三层架构技术体系

SSM框架的三层架构技术体系

在三层架构开发时会使用到SSM框架:

  • 表现层:SpringMVC
  • 业务层:Spring
  • 数据访问层:Mybatis

在使用SSM框架搭建三层架构时:

  • Spring ,管理全项目
    • IoC(父容器):管理除web层以外的bean
    • AOP :一般用在业务层(声明式事务放在业务层)
  • SpringMVC ,管理web层
    • 基于Spring
      • IoC(子容器):管理web层的bean
        • 父子容器:子容器可以访问父容器中的bean (反之不行)
      • AOP:统一异常处理器、拦截器
    • 封装web
      • 三大组件:Servlet(DispatcherServlet 前端控制器)、Filter(CharacterEncodeingFilter 乱码过滤器)、Listener
      • 请求、响应:请求参数映射、响应数据自动转换json格式
  • Mybatis ,管理dao层
    • 和数据库交互

小结

在三层架构体系中:

  • SpringMVC,属于表现层 (使用自己独立的IOC容器)
    • 接收前端请求
    • 处理请求(交给业务层完成)
    • 给前端响应
  • Spring,属于业务层(使用Spring的IOC容器)
    • 处理业务逻辑
  • Mybatis,属于持久层(由Spring的IOC容器管理)
    • 和数据库交互

02_SSM框架整合流程

目标

  • 了解SSM框架整合的流程

路径

  1. SSM框架整合的流程

SSM框架整合的流程

SSM框架整合流程步骤:

  1. 创建项目工程
  2. 创建包(分包):config、dao、service、controller、domain
  3. 搭建Spring框架环境
    • 导入Spring相关依赖
    • 编写SpingConfig配置类
  4. Spring整合Mybatis框架(业务层和持久层整合)
    • 导入坐标:MySQL、Mybatis、druid、Spring-jdbc、mybatis-spring
    • 编写配置类:
      • JdbcConfig // 连接池、事务管理器
      • MybatisConfig
      • 修改SpringConfig
    • 编写dao层代码
    • 编写service层代码
  5. Spring整合SpringMVC框架(业务层和表现层整合)
    • 导入坐标:Servlet、Jackson、Springmvc
    • 编写配置类
      • ServletConfig //Web容器配置类
      • SpringmvcConfig //SpringMVC配置类
    • 编写web层代码(Controller)

项目工程结构目录:

image-20220506230413504

03_SSM整合-搭建Spring环境

目标

  • 能够搭建Spring框架环境

路径

  1. 导入坐标
  2. 编写Spring配置类

导入坐标

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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itheima</groupId> <artifactId>springmvc_day02-ssm</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <!-- 依赖管理 --> <dependencies> <!-- SpringMVC(底层依赖Spring)--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies> </project> 

image-20220506230208139

编写Spring配置类

SpringConfig:

/* Spring IoC容器(父容器) 1. 管理除Controller包之外的所有bean 2. spring的组件扫描 : 扫描controller包之外的其他包 1). 方案一: 一个个配置,就是不配置controller包 2). 方案二: 全扫描,排除controller包 ComponentScan I. value : 要扫描的包 II. excludeFilters : 排除不扫描 Controller和 RestController注解 a. type : 要排除的类型 (注解) b. classes : 要排除的类 */ /*@ComponentScan(value = "com.itheima", excludeFilters = { @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = {Controller.class, RestController.class} ) } )*/ //Spring配置类 (添加到IoC容器中) @Configuration @ComponentScan({"com.itheima.service"}) //扫描service层包 public class SpringConfig { } 

04_SSM整合-Spring整合Mybatis

目标

  • 能够使用Spring整合Mybatis,实现对数据的CRUD操作

路径

  1. 导入坐标
  2. 编写配置类
  3. 编写dao层
  4. 编写service层
  5. 测试service层功能

数据库:

create database ssm_db; use ssm_db; create table tbl_book( id int primary key auto_increment, type varchar(20), name varchar(50), description varchar(255) ); INSERT INTO `tbl_book` VALUES ('1', '编程', 'Spring实战', 'spring入门经典教程'); INSERT INTO `tbl_book` VALUES ('2', '编程', 'springmvc实战', 'springmvc经典教程'); INSERT INTO `tbl_book` VALUES ('3', '编程', 'mybatis入门', '手把手教你操作数据库'); INSERT INTO `tbl_book` VALUES ('4', '市场营销', '直播就该这么做', '李佳琦,薇娅推荐'); INSERT INTO `tbl_book` VALUES ('5', '市场营销', '直播带货', '一本教你如何玩转直播的书'); 

导入坐标

 <!-- Spring整合Mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!-- Spring JDBC : 声明式事务 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!-- Spring整合Junit --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!-- MySQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- Mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!-- druid连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.1</version> </dependency> <!-- Junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> 

编写配置类

db.properties

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm_db jdbc.username=root jdbc.password=itheima 

JdbcConfig:连接池配置、事务管理器

//配置:连接池、事务管理器 public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; //设置连接池 @Bean public DataSource dataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } //设置事务管理器 (声明式事务) @Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ //PlatformTransactionManager是DataSourceTransactionManager的父接口 DataSourceTransactionManager dstm = new DataSourceTransactionManager(); dstm.setDataSource(dataSource); return dstm; } } 

MybatisConfig

//Mybatis配置类 public class MybatisConfig { @Bean public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource ds){ SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); //设置pojo的包扫描 factoryBean.setTypeAliasesPackage("com.itheima.domain"); //设置连接池 factoryBean.setDataSource(ds); return factoryBean; } @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); //设置dao层的接口扫描 msc.setBasePackage("com.itheima.dao"); return msc; } } 

SpringConfig修改

@Configuration //Spring配置类 (添加到IoC容器中) @ComponentScan({"com.itheima.service"}) //扫描指定包下的类,并添加到IoC容器 @PropertySource("classpath:db.properties") //加载外部配置文件 @Import({JdbcConfig.class, MybatisConfig.class}) //引入配置类 @EnableTransactionManagement //开启事务管理支持 public class SpringConfig { } 

编写dao层

dao层:

public interface BookDao { @Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})") public int save(Book book); @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}") public int update(Book book); @Delete("delete from tbl_book where id = #{id}") public int delete(Integer id); @Select("select id, type, name, description from tbl_book where id = #{id}") public Book getById(Integer id); @Select("select id, type, name, description from tbl_book") public List<Book> getAll(); } 

编写service层

service层:

//接口 @Transactional //给接口的每个方法添加事务 public interface IBookService { /** * 保存 * @param book * @return */ public boolean saveBook(Book book); /** * 修改 * @param book * @return */ public boolean updateBook(Book book); /** * 按id删除 * @param id * @return */ public boolean deleteBookById(Integer id); /** * 按id查询 * @param id * @return */ public Book getBookById(Integer id); /** * 查询全部 * @return */ public List<Book> getAllBook(); } 
//实现类 @Service public class BookServiceImpl implements IBookService { @Autowired private BookDao bookDao; @Override public boolean saveBook(Book book) { int result = bookDao.save(book); if(result>0){ return true; } return false; } @Override public boolean updateBook(Book book) { bookDao.update(book); return true; } @Override public boolean deleteBookById(Integer id) { bookDao.delete(id); return true; } @Override public Book getBookById(Integer id) { Book book = bookDao.getById(id); return book; } @Override public List<Book> getAllBook() { List<Book> bookList = bookDao.getAll(); return bookList; } } 

测试service层功能

测试类:

@RunWith(SpringJUnit4ClassRunner.class)//spring整合junit @ContextConfiguration(classes = SpringConfig.class)//加载注解配置类 public class BookServiceTest { @Autowired IBookService bookService; @Test public void testGetBook() { Book book = bookService.getBookById(1); System.out.println(book); } @Test public void testGetAllBook() { List<Book> bookList = bookService.getAllBook(); for (Book book : bookList) { System.out.println(book); } } @Test public void testSaveBook(){ Book book = new Book(); book.setType("编程"); book.setName("MySQL高级"); book.setDescription("MySQL数据库的高级应用开发"); boolean result = bookService.saveBook(book); System.out.println(result); } } 

05_SSM整合-Spring整合SpringMVC

目标

  • 能够使用Spring整合SpringMVC,实现前端数据交互

路径

  1. 导入坐标
  2. 编写配置类
  3. 编写Controller类
  4. 使用postman测试

导入坐标

 <!-- Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- Jackson : 实现json格式和javabean之间的数据转换 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> 

编写配置类

SpringMVC配置类

@Configuration @ComponentScan("com.itheima.controller") //springmvc的ioc容器只管理web层相关的bean @EnableWebMvc //开启javabean自动转换json public class SpringmvcConfig { } 

Web容器配置类

public class ServletInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { //指定spring配置类加载(根配置) return new Class[]{SpringConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { //指定springmvc配置类加载 return new Class[]{SpringmvcConfig.class}; } @Override protected String[] getServletMappings() { //配置springmvc拦截路径为 / (全路径) return new String[]{"/"}; } //中文乱码过滤器 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter(); encodingFilter.setEncoding("utf-8"); return new Filter[]{encodingFilter}; } } 
  • 配置启动顺序:

    1. Tomcat启动,利用SPI机制加载ServletInitConfig

    2. 在ServletInitConfig中,先加载SpringConfig,然后加载SpringmvcConfig,最后设置SpringMVC中的DispatcherServlet的匹配路径为:/

    3. SpringConfig

      • 1、加载com.itheima.service下的bean(IoC父容器)

      • 2、加载jdbc.properties文件

      • 3、加载mybatis配置类:JdbcConfig、MybatisConfig

        • 将com.itheima.dao下的bean放到父容器
    4. SpringmvcConfig

      • 加载com.itheima.controller下的bean(子容器)

父子容器:子容器可以访问父容器中的bean (反之不行)

编写Controller类

BookController

//RESTful风格 @RestController @RequestMapping("/book") public class BookController { @Autowired private IBookService bookService; @PostMapping public boolean save(@RequestBody Book book) { return bookService.saveBook(book); } @PutMapping public boolean update(@RequestBody Book book) { return bookService.updateBook(book); } @DeleteMapping("/{id}") public boolean delete(@PathVariable Integer id) { return bookService.deleteBookById(id); } @GetMapping("/{id}") public Book getById(@PathVariable Integer id) { return bookService.getBookById(id); } @GetMapping public List<Book> getAll() { return bookService.getAllBook(); } } 

使用postman测试

  • 修改

    image-20220507120056572

  • 查询

    image-20220507120232667

06_SSM整合-封装表现层数据

目标

  • 能够使用封装的方式优化表现层响应的数据

路径

  1. 表现层存在的问题
  2. 改造表现层:对响应的数据进行统一封装

表现层存在的问题

现在后端程序响应的数据格式是五花八门,这种情况不利于前端解析,同时也加大了前后端交互的难度

1638285744660

解决方案:对响应的数据进行统一的封装 (自定义交互协议)

1638285619665

改造表现层:对响应的数据进行统一封装

添加:响应结果类(Result)、状态码类(Code)

1638286030207

响应结果类:设置统一数据返回结果

  • 说明:Result类中的字段并不是固定的,可以根据需要自行增减
public class Result{ //描述统一格式中的数据 private Object data; //描述统一格式中的编码,用于区分操作,可以简化配置0或1表示成功失败 private Integer code; //描述统一格式中的消息,可选属性 private String msg; public Result() { } //可用于增删改操作的响应结果(查询失败也可以用) public Result(Integer code,String msg) { this.msg = msg; this.code = code; } //可用于查询成功 public Result(Integer code, Object data, String msg) { this.data = data; this.code = code; this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } 
Result responseResult = new Result(响应状态码, 响应的数据, 响应状态消息); 

问题:在Result类中有指定响应状态码,那么响应状态码应该怎么定义呢?

  • 可以参照之前学习http协议时有接触过的状态码。如:200、404、500

自定义状态码:设置统一数据返回结果编码

//状态码 public class Code { public static final Integer SAVE_OK = 20011; //保存成功 public static final Integer DELETE_OK = 20021;//删除成功 public static final Integer UPDATE_OK = 20031;//修改成功 public static final Integer GET_OK = 20041;//查询成功 public static final Integer SAVE_ERR = 50010;//保存失败 public static final Integer DELETE_ERR = 50020;//删除失败 public static final Integer UPDATE_ERR = 50030;//修改失败 public static final Integer GET_ERR = 50040;//查询失败 } 
  • 说明:Code类的常量设计也不是固定的,可以根据需要自行增减
    • 例如:将查询再进行细分为GET_OK,GET_ALL_OK,GET_PAGE_OK

修改BookController类:

@RestController @RequestMapping("/book") public class BookController { @Autowired private IBookService bookService; @PostMapping public Result save(@RequestBody Book book) { boolean result = bookService.saveBook(book); Integer code = result ? Code.SAVE_OK : Code.SAVE_ERR; String message = result ? "保存成功" : "保存失败"; return new Result(code, result, message); } @PutMapping public Result update(@RequestBody Book book) { boolean result = bookService.updateBook(book); Integer code = result ? Code.UPDATE_OK : Code.UPDATE_ERR; String message = result ? "修改成功" : "修改失败"; return new Result(code, result, message); } @DeleteMapping("/{id}") public Result delete(@PathVariable Integer id) { boolean result = bookService.deleteBookById(id); Integer code = result ? Code.DELETE_OK : Code.DELETE_ERR; String message = result ? "删除成功" : "删除失败"; return new Result(code, result, message); } @GetMapping("/{id}") public Result getById(@PathVariable Integer id) { Book book = bookService.getBookById(id); Integer code = book != null ? Code.GET_OK : Code.GET_ERR; String message = book != null ? "查询用户成功" : "查询用户失败"; return new Result(code, book, message); } @GetMapping public Result getAll() { List<Book> bookList = bookService.getAllBook(); Integer code = bookList != null && bookList.size() != 0 ? Code.GET_OK : Code.GET_ERR; String message = bookList != null && bookList.size() != 0 ? "查询全部成功" : "查询全部失败"; return new Result(code, bookList, message); } } 

使用postman测试:

  • 修改

    image-20220507132846251

  • 查询

    image-20220507132954724

  • 查询失败

    image-20220507133159799

07_SpringMVC异常处理器

目标

  • 了解SpringMVC异常处理器

路径

  1. 三层架构下的异常现象
  2. SpringMVC的异常处理器

三层架构下的异常现象

在三层架构下异常现象出现的位置与诱因:

  • 框架内部抛出的异常:因使用不合规导致
  • 数据层抛出的异常:因外部服务器故障导致(如:服务器访问超时)
  • 业务层抛出的异常:因业务逻辑书写错误导致(如:遍历过程中出现索引异常)
  • 表现层抛出的异常:因数据收集、校验等规则导致(如:不匹配的数据类型导致异常)
  • 工具类抛出的异常:因工具类书写不严谨不够健壮导致(如:要释放的连接长期未释放)

思考:各层都可能出现异常,那么异常处理代码应该写在哪一层?

之前:学习Java时,可以把异常都抛出到main方法中,在main方法中进行统一处理。 现在:三层架构体系下,把异常都抛出到表现层(controller)进行处理 

思考:在表现层处理异常时,每个方法都可能会存在异常,如果在每个方法中都添加异常处理代码,代码书写量巨大且意义不强,如何解决?

AOP思想 SpringMVC参照AOP思想,设计了可以统一处理项目中发生异常的处理器 -- 切面 = 切入点 + 通知 -- 切入点: controller层所有方法 -- 通知: 异常通知 

SpringMVC的异常处理器

AOP思想:

  • 切面 = 切入点 + 通知
  • 切入点 : controller的方法
  • 通知 : 异常通知

SpringMVC的异常处理器可以集中的、统一的处理项目中出现的异常

  • 为SpringMVC控制器类做增强
//@RestControllerAdvice = @ResponseBody + @ControllerAdvice //此注解自带@ResponseBody注解与@ControllerAdvice注解,具备对应的功能 @RestControllerAdvice public class ProjectExceptionAdvice { /* 专门处理Exception类型异常的通知 1. 此方法在controller层代码抛出异常之后 2. 并且此异常类型为Exception,那么此方法就会运行 3. 而且参数ex就是所发生的异常 */ @ExceptionHandler(Exception.class) public Result doException(Exception ex){ ex.printStackTrace(); return new Result(666,null); } } 

08_项目异常处理方案

目标

  • 能够在SSM项目中添加自定义异常处理

路径

  1. 项目中异常的分类
  2. 改造SSM项目:添加异常处理

项目中异常的分类

异常出现的可能情况:

  1. 代码写错了:程序员自身原因
  2. 用户出错了:程序有一部分数据是由用户输入的
  3. 系统的影响:项目所依赖的整个环境(tomcat,mysql)

项目中异常分类:

  1. 业务异常(BusinessException) 用户行为产生的异常
  2. 系统异常(SystemException) 项目运行过程中可预计且无法避免的异常(数据库崩溃)
  3. 其他异常(Exception) 编程人员未预期到的异常

项目中异常处理方案

  1. 业务异常(BusinessException)
    • 发送对应消息传递给用户,提醒规范操作
    • 记录日志
  2. 系统异常(SystemException)
    • 发送固定消息传递给用户,安抚用户
    • 发送特定消息给运维人员,提醒维护
    • 记录日志
  3. 其他异常(Exception)
    • 发送固定消息传递给用户,安抚用户
    • 发送特定消息给编程人员,提醒维护(纳入预期范围内)
    • 记录日志

SpringMVC中异常处理示例

添加:自定义异常类、异常通知类

image-20220507172653976

自定义异常处理器: 用于封装异常信息,对异常进行分类

//业务异常处理器 public class BusinessException extends RuntimeException{ private Integer code; public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public BusinessException(Integer code, String message) { super(message); this.code = code; } public BusinessException(Integer code, String message, Throwable cause) { super(message, cause); this.code = code; } } 
//系统异常处理器 public class SystemException extends RuntimeException{ private Integer code; public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public SystemException(Integer code, String message) { super(message); this.code = code; } public SystemException(Integer code, String message, Throwable cause) { super(message, cause); this.code = code; } } 

自定义异常状态码:

public class Code { public static final Integer SAVE_OK = 20011; //保存成功 public static final Integer DELETE_OK = 20021;//删除成功 public static final Integer UPDATE_OK = 20031;//修改成功 public static final Integer GET_OK = 20041;//查询成功 public static final Integer SAVE_ERR = 50010;//保存失败 public static final Integer DELETE_ERR = 50020;//删除失败 public static final Integer UPDATE_ERR = 50030;//修改失败 public static final Integer GET_ERR = 50040;//查询失败 public static final Integer SYSTEM_ERR = 50001;//系统异常 public static final Integer SYSTEM_TIMEOUT_ERR = 50002;//系统访问超时 public static final Integer SYSTEM_UNKNOW_ERR = 59999;//服务器异常 public static final Integer BUSINESS_ERR = 60001; //业务异常 } 

异常通知:

@RestControllerAdvice public class ProjectExceptionAdvice { //专门处理业务功能导致的异常 @ExceptionHandler(BusinessException.class) public Result doBusinessException(BusinessException ex){ //发送对应消息传递给用户,提醒规范操作 return new Result(ex.getCode(),ex.getMessage()); } //专门处理系统异常 @ExceptionHandler(SystemException.class) public Result doSystemException(SystemException ex){ //发送固定消息传递给用户,安抚用户 //发送特定消息给运维人员,提醒维护 //记录日志 return new Result(ex.getCode(),ex.getMessage()); } //处理其他未知异常 @ExceptionHandler(Exception.class) public Result doException(Exception ex){ //发送固定消息传递给用户,安抚用户 //发送特定消息给编程人员,提醒维护 //记录日志 return new Result(Code.SYSTEM_UNKNOW_ERR,"服务器正在维护,请稍后访问"); } } 

修改业务类(模拟异常发生)

@Service public class BookServiceImpl implements IBookService { @Autowired private BookDao bookDao;//由于idea不能很好的识别spring整合myabtis的配置,所以可能报错,但实际运行没有问题 @Override public boolean saveBook(Book book) { int result = bookDao.save(book); if(result>0){ return true; } return false; } @Override public boolean updateBook(Book book) { bookDao.update(book); return true; } @Override public boolean deleteBookById(Integer id) { bookDao.delete(id); return true; } @Override public Book getBookById(Integer id) { //业务逻辑判断:传递参数的合法性(用户录入) if (id < 0) { //业务异常: 用户造成的 throw new BusinessException(Code.BUSINESS_ERR, "用户id不能为负数"); } Book book = null; try { //数据库可能有问题抛出异常 book = bookDao.getById(id); } catch (Exception e) { throw new SystemException(Code.SYSTEM_ERR, "当前访问人数较多,请稍后访问"); } //模拟未知异常 int i = 1 / 0; return book; } @Override public List<Book> getAllBook() { List<Book> bookList = bookDao.getAll(); return bookList; } } 

使用postman测试:

image-20220507191355044

09_SpringMVC拦截器

目标

  • 了解SpringMVC中拦截器的概念

路径

  1. 拦截器介绍
  2. 拦截器和过滤器的区别
  3. 拦截器的应用场景

拦截器介绍

在前面学习Servlet技术时,有使用过:Filter(过滤器)

而在SpringMVC中有存在类似于Filter的功能:拦截器(Interceptor)。用于对controller进行预处理和后处理。

拦截器:

  • 拦截器是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
  • 作用:
    1. 在指定的controller方法前后执行预先设定的代码
    2. 阻止controlle方法的执行
  • 原理:AOP思想

1638292219389

拦截器和过滤器的区别

官方:

image-20220507212512302

黑马:

  1. 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
  2. 拦截内容不同:Filter对所有访问进行拦截,Interceptor仅针对SpringMVC的访问进行拦截
  3. 执行顺序:先执行过滤器,然后再能执行拦截器

拦截器的应用场景

应用场景:

  1. 权限检查
    • 如登录检测,进入处理器前检测用户是否登录,如果没有登陆直接返回到登录页面。
  2. 性能监控
    • 有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,统计处理器执行使用了多少时间。

10_SpringMVC拦截器的使用

目标

  • 能够使用SpringMVC拦截器增强contorller功能

路径

  1. 拦截器的使用步骤
  2. 拦截器的使用示例

拦截器的使用步骤

要使用SpringMVC拦截器,必须实现HandlerInterceptor接口,重写接口中的方法:

  • preHandle() ,在contorller方法执行前拦截

  • postHandle() ,在contorller方法执行后拦截

  • afterCompletion ,在视图解析器解析完毕后拦截

拦截器使用步骤:

  1. 自定义拦截器(通知)
    • 创建类并实现HandlerInterceptor接口,重写接口中的方法
  2. 配置拦截器的拦截规则(切入点)

拦截器的使用示例

image-20220507222152618

代码示例:

  • 自定义拦截器(通知)
//定义拦截器类,实现HandlerInterceptor接口 @Component //当前类必须受Spring容器控制 public class TimeConsumeInterceptor implements HandlerInterceptor { long beginTime;//开始时间 long endTime;//结束时间 //原始方法调用前增强的功能 /** * @param request 请求对象 * @param response 响应对象 * @param handler 处理器方法的封装(方法对象) * @return true:表示资源放行 、 false:资源禁止访问 * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle... 开始计时"); beginTime = System.currentTimeMillis(); return true;//返回值类型可以拦截控制的执行。 true放行,false终止 } //原始方法调用后执行增强的功能 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle... 结束计时"); endTime = System.currentTimeMillis(); //强制转换为:HandlerMethod (反射中的Method对象再次包装) HandlerMethod handlerMethod = (HandlerMethod) handler; //获取controller方法的名字 String methodName = handlerMethod.getMethod().getName(); System.out.println(methodName+"方法执行,耗时:" + (endTime - beginTime) + "毫秒"); } } 
  • 配置拦截器的拦截规则(切入点)
//拦截器配置类 @Configuration public class SpringmvcSupport extends WebMvcConfigurationSupport { @Autowired private TimeConsumeInterceptor timeConsumeInterceptor; @Override protected void addInterceptors(InterceptorRegistry registry) { //配置拦截器 设定拦截的访问路径,路径可以通过可变参数设置多个 registry.addInterceptor(timeConsumeInterceptor) .addPathPatterns("/book","/book/*"); } } 
//修改SpringmvcConfig配置类: 添加扫描包 @Configuration @ComponentScan({"com.itheima.controller","com.itheima.config"}) //新增扫描包 @EnableWebMvc public class SpringmvcConfig { } 
//输出结果: preHandle... 开始计时 postHandle... 结束计时 getAll方法执行,耗时:7毫秒 

使用标准接口WebMvcConfigurer简化开发 (侵入式编码)

@Configuration //@ComponentScan({"com.itheima.controller","com.itheima.config"}) @ComponentScan("com.itheima.controller") @EnableWebMvc public class SpringmvcConfig implements WebMvcConfigurer { @Autowired private TimeConsumeInterceptor timeConsumeInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { //配置拦截器 设定拦截的访问路径,路径可以通过可变参数设置多个 registry.addInterceptor(timeConsumeInterceptor) .addPathPatterns("/book","/book/*"); } } 

11_SpringMVC拦截器链

目标

  • 了解SpringMVC中的拦截器链

路径

  1. 拦截器链介绍
  2. 拦截器链示例

拦截器链介绍

拦截器链:

  • 当配置多个拦截器时,就形成拦截器链

  • 拦截器链的运行顺序参照拦截器添加顺序为准

  • 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行

实际开发中,尽量避免多拦截器的使用,因为请求拦截器的增多会造成请求链路的增长,那么执行请求的效率也会降低

image-20200427171422781

以上图中3个拦截器为例说明: 规律:当3个拦截器的pre方法都返回true时,则对应的所有post和after方法都会被执行; 规律:当有一个pre方法返回false时,所有的post方法都不会被执行; 规律:after方法只有在对应的pre方法被执行且返回true时才执行; 

拦截器链示例

代码示例:

  • 自定义拦截器(通知)
@Component public class ProjectInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("ProjectInterceptor => preHandle"); return true;//放行:执行controller方法 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); System.out.println("ProjectInterceptor => postHandle"); } } 
  • 配置多个拦截器(切入点)
@Configuration public class SpringmvcSupport extends WebMvcConfigurationSupport { @Autowired private TimeConsumeInterceptor timeConsumeInterceptor; @Autowired private ProjectInterceptor projectInterceptorl; protected void addInterceptors(InterceptorRegistry registry) { //配置多个拦截器 registry.addInterceptor(timeConsumeInterceptor) .addPathPatterns("/book","/book/*"); registry.addInterceptor(projectInterceptorl) .addPathPatterns("/book","/book/*"); } } 
//输出结果: preHandle... 开始计时 ProjectInterceptor => preHandle ProjectInterceptor => postHandle postHandle... 结束计时 getAll方法执行,耗时:6毫秒 

12_在SpringMVC中访问静态资源

目标

  • 能够解决在SpringMVC中访问静态资源时存在的问题

路径

  1. 问题演示
  2. 解决问题

问题演示

向项目工程下,添加web静态资源:

image-20220507194259664

启动Tomcat,访问页面时,会出现404错误(找不到资源):

image-20220507194447254

原理分析:

image-20220507212200275

解决问题

image-20220507201045649

在config包下,新建配置类:SpringmvcSupport

@Configuration public class SpringmvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } } 

修改SpringMVC配置类:

@Configuration @ComponentScan({"com.itheima.controller","com.itheima.config"}) //新增扫描包 @EnableWebMvc //开启javabean自动转换json public class SpringmvcConfig { } 

再次访问books.html页面:

image-20220507200207731

13_前端vue代码实现

目标

  • 能够编写vue代码,实现和后端程序的交互

路径

  1. 查询功能

查询功能

books.html

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>SpringMVC案例</title> <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"> <!-- 引入样式 --> <link rel="stylesheet" href="../plugins/elementui/index.css"> <link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css"> <link rel="stylesheet" href="../css/style.css"> </head> <body class="hold-transition"> <div id="app"> <div class="content-header"> <h1>图书管理</h1> </div> <div class="app-container"> <div class="box"> <div class="filter-container"> <el-input placeholder="图书名称" v-model="pagination.queryString" style="width: 200px;" class="filter-item"></el-input> <el-button @click="getAll()" class="dalfBut">查询</el-button> <el-button type="primary" class="butT" @click="handleCreate()">新建</el-button> </div> <el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row> <el-table-column type="index" align="center" label="序号"></el-table-column> <el-table-column prop="type" label="图书类别" align="center"></el-table-column> <el-table-column prop="name" label="图书名称" align="center"></el-table-column> <el-table-column prop="description" label="描述" align="center"></el-table-column> <el-table-column label="操作" align="center"> <template slot-scope="scope"> <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button> <el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button> </template> </el-table-column> </el-table> <!-- 新增标签弹层 --> <div class="add-form"> <el-dialog title="新增图书" :visible.sync="dialogFormVisible"> <el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px"> <el-row> <el-col :span="12"> <el-form-item label="图书类别" prop="type"> <el-input v-model="formData.type"/> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="图书名称" prop="name"> <el-input v-model="formData.name"/> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="描述"> <el-input v-model="formData.description" type="textarea"></el-input> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取消</el-button> <el-button type="primary" @click="handleAdd()">确定</el-button> </div> </el-dialog> </div> <!-- 编辑标签弹层 --> <div class="add-form"> <el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit"> <el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right" label-width="100px"> <el-row> <el-col :span="12"> <el-form-item label="图书类别" prop="type"> <el-input v-model="formData.type"/> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="图书名称" prop="name"> <el-input v-model="formData.name"/> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="描述"> <el-input v-model="formData.description" type="textarea"></el-input> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible4Edit = false">取消</el-button> <el-button type="primary" @click="handleEdit()">确定</el-button> </div> </el-dialog> </div> </div> </div> </div> </body> <!-- 引入组件库 --> <script src="../js/vue.js"></script> <script src="../plugins/elementui/index.js"></script> <script type="text/javascript" src="../js/jquery.min.js"></script> <script src="../js/axios-0.18.0.js"></script> <script> /* * TODO: Vue就两部分内容 * 1. 视图 : html(界面) * 1). 不用脚手架 <body> 2). 脚手架 <template> * 2. 脚本 : js(data数据,methods交互) * <script> * TODO: Vue的理念 * 1). 数据绑定 (通用) * 视图绑定脚本中的data数据 -> data数据改变,视图会随之改变 * 2). 双向数据绑定 (表单 : v-model) * data数据改变,视图会随之改变 * 视图改变,data数据也会随之改变 * TODO: 交互部分,是不需要关注视图,关注data数据 * 发送请求,获取数据,设置到data中 * * 视图 <- data <- 交互 * */ var vue = new Vue({ el: '#app', data: { pagination: {}, dataList: [],//当前页要展示的列表数据 formData: {},//表单数据 dialogFormVisible: false,//控制表单是否可见 dialogFormVisible4Edit: false,//编辑表单是否可见 rules: {//校验规则 type: [{required: true, message: '图书类别为必填项', trigger: 'blur'}], name: [{required: true, message: '图书名称为必填项', trigger: 'blur'}] }, url: "/book" }, //钩子函数,VUE对象初始化完成后自动执行 created() { this.getAll(); }, methods: { //列表 TODO: getAll() { //查询 : 发起一个查询请求,接收数据,设置给dataList axios.get(this.url).then(response => { console.log(response.data); if (response.data.code == 20041) { this.dataList = response.data.data } else { this.$message.error(response.data.msg); } }) }, //重置表单 resetForm() { this.formData = {}; }, //弹出添加窗口 handleCreate() { this.dialogFormVisible = true; this.resetForm(); }, //弹出编辑窗口 handleUpdate(row) { this.formData = row this.dialogFormVisible4Edit = true; }, //TODO:省略.... } }); </script> </html> 

扩展:文件上传

目标

  • 能够使用SpringMVC接收上传的文件

路径

  1. 客户端文件上传要求
  2. SpringMVC的文件上传

客户端文件上传要求

浏览器客户端实现文件上传的要求:

  1. form表单的method属性设置为post方式
    • get的请求参数拼接url中,体现在地址栏,长度是受限
    • post请求参数放在请求体中,长度不受限
  2. form表单的enctype属性设置为multipart/form-data,默认是:x-www-form-urlencoded
  3. form表单中需要一个的文本选择域
<form method="post" enctype="multipart/form-data"> 头像: <input type="file" name="file" /> <!-- 上传文件项 --> <input type="submit" value="提交表单" /> </form> 

SpringMVC的文件上传

代码示例:

  • 前端

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>文件上传</h1> <form action="uploadfile" method="post" enctype="multipart/form-data"> <!-- 普通表单项 --> 用户名: <input type="text" name="username"/> <br> 密码: <input type="text" name="password" /> <br> <!-- 上传文件项 --> 头像: <input type="file" name="myFile" /> <br> <input type="submit" value="提交表单" /> </form> </body> </html> 
  • 后端

    • 导入坐标
    <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.2</version> </dependency> 
    • 配置类:SpringMvcSupport
    @Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { /** * 释放静态资源 * @param registry */ @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); } //配置多媒体解析器,方法名须为:multipartResolver @Bean public CommonsMultipartResolver multipartResolver(){ CommonsMultipartResolver resolver = new CommonsMultipartResolver(); //设置上传文件的总大小,单位是字节 100MB resolver.setMaxUploadSize(1024*1024*100); //设置每个文件上传的大小,单位是字节 10MB resolver.setMaxUploadSizePerFile(1024*1024*10); return resolver; } } 
    • Controller
    @RestController public class UploadController { /** * 注意参数名跟前端name属性一致 * @param username * @param password * @param myFile 本质是个InputStream * springmvc用这个对象封装了前端发送的文件数据 * @return */ @PostMapping("/uploadfile") public String upload(String username, String password, MultipartFile myFile) throws IOException { System.out.println(username + "," + password); System.out.println(myFile); String name = myFile.getName(); System.out.println("属性名:" + name); String originalFilename = myFile.getOriginalFilename(); System.out.println("文件名:" + originalFilename); //随机文件名 String randomName = UUID.randomUUID().toString().replaceAll("-", ""); //切割文件名: 6.jpg =切割=> 6 jpg String[] split = originalFilename.split("\\."); String fileName = "f:/upload/" + randomName + "." + split[1]; //创建文件对象 File destFile = new File(fileName); //将myFile数据写到destFile去 myFile.transferTo(destFile); return "success"; } } 

扩展:使用postman进行图片上传测试

1641309409772

附录:SSM整合案例完整依赖

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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itheima</groupId> <artifactId>springmvc_day02-ssm</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <!-- 依赖管理 --> <dependencies> <!-- SpringMVC(底层依赖Spring)--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!-- Spring整合Mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!-- Spring JDBC : 声明式事务 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!-- Spring整合Junit --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> <!-- MySQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- Mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!-- druid连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.1</version> </dependency> <!-- Junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- Jackson : 实现json格式和javabean之间的数据转换 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> </dependencies> </project> 

附录:books.html完整代码

books.html

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>SpringMVC案例</title> <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"> <!-- 引入样式 --> <link rel="stylesheet" href="../plugins/elementui/index.css"> <link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css"> <link rel="stylesheet" href="../css/style.css"> </head> <body class="hold-transition"> <div id="app"> <div class="content-header"> <h1>图书管理</h1> </div> <div class="app-container"> <div class="box"> <div class="filter-container"> <el-input placeholder="图书名称" v-model="pagination.queryString" style="width: 200px;" class="filter-item"></el-input> <el-button @click="getAll()" class="dalfBut">查询</el-button> <el-button type="primary" class="butT" @click="handleCreate()">新建</el-button> </div> <el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row> <el-table-column type="index" align="center" label="序号"></el-table-column> <el-table-column prop="type" label="图书类别" align="center"></el-table-column> <el-table-column prop="name" label="图书名称" align="center"></el-table-column> <el-table-column prop="description" label="描述" align="center"></el-table-column> <el-table-column label="操作" align="center"> <template slot-scope="scope"> <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button> <el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button> </template> </el-table-column> </el-table> <!-- 新增标签弹层 --> <div class="add-form"> <el-dialog title="新增图书" :visible.sync="dialogFormVisible"> <el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px"> <el-row> <el-col :span="12"> <el-form-item label="图书类别" prop="type"> <el-input v-model="formData.type"/> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="图书名称" prop="name"> <el-input v-model="formData.name"/> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="描述"> <el-input v-model="formData.description" type="textarea"></el-input> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取消</el-button> <el-button type="primary" @click="handleAdd()">确定</el-button> </div> </el-dialog> </div> <!-- 编辑标签弹层 --> <div class="add-form"> <el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit"> <el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right" label-width="100px"> <el-row> <el-col :span="12"> <el-form-item label="图书类别" prop="type"> <el-input v-model="formData.type"/> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="图书名称" prop="name"> <el-input v-model="formData.name"/> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="描述"> <el-input v-model="formData.description" type="textarea"></el-input> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible4Edit = false">取消</el-button> <el-button type="primary" @click="handleEdit()">确定</el-button> </div> </el-dialog> </div> </div> </div> </div> </body> <!-- 引入组件库 --> <script src="../js/vue.js"></script> <script src="../plugins/elementui/index.js"></script> <script type="text/javascript" src="../js/jquery.min.js"></script> <script src="../js/axios-0.18.0.js"></script> <script> /* * TODO: Vue就两部分内容 * 1. 视图 : html(界面) * 1). 不用脚手架 <body> 2). 脚手架 <template> * 2. 脚本 : js(data数据,methods交互) * <script> * TODO: Vue的理念 * 1). 数据绑定 (通用) * 视图绑定脚本中的data数据 -> data数据改变,视图会随之改变 * 2). 双向数据绑定 (表单 : v-model) * data数据改变,视图会随之改变 * 视图改变,data数据也会随之改变 * TODO: 交互部分,是不需要关注视图,关注data数据 * 发送请求,获取数据,设置到data中 * * 视图 <- data <- 交互 * */ var vue = new Vue({ el: '#app', data: { pagination: {}, dataList: [],//当前页要展示的列表数据 formData: {},//表单数据 dialogFormVisible: false,//控制表单是否可见 dialogFormVisible4Edit: false,//编辑表单是否可见 rules: {//校验规则 type: [{required: true, message: '图书类别为必填项', trigger: 'blur'}], name: [{required: true, message: '图书名称为必填项', trigger: 'blur'}] }, url: "/book" }, //钩子函数,VUE对象初始化完成后自动执行 created() { this.getAll(); }, methods: { //列表 TODO: getAll() { //查询 : 发起一个查询请求,接收数据,设置给dataList axios.get(this.url).then(response => { console.log(response.data); if (response.data.code == 20041) { this.dataList = response.data.data } else { this.$message.error(response.data.msg); } }) }, //弹出添加窗口 handleCreate() { this.dialogFormVisible = true; this.resetForm(); }, //重置表单 resetForm() { this.formData = {}; }, //添加 TODO: handleAdd() { //发送ajax请求 axios.post(this.url, this.formData).then((res) => { console.log(res.data); //如果操作成功,关闭弹层,显示数据 if (res.data.code == 20011) { this.dialogFormVisible = false; this.$message.success("添加成功"); } else if (res.data.code == 20010) { this.$message.error("添加失败"); } else { this.$message.error(res.data.msg); } }).finally(() => { this.getAll(); }); }, //弹出编辑窗口 handleUpdate(row) { this.formData = row this.dialogFormVisible4Edit = true; }, //编辑 TODO: handleEdit() { //发送ajax请求 axios.put(this.url, this.formData).then((res) => { //如果操作成功,关闭弹层,显示数据 if (res.data.code == 20031) { this.dialogFormVisible4Edit = false; this.$message.success("修改成功"); } else if (res.data.code == 20030) { this.$message.error("修改失败"); } else { this.$message.error(res.data.msg); } }).finally(() => { this.getAll(); }); }, // 删除 handleDelete(row) { //1.弹出提示框 this.$confirm("此操作永久删除当前数据,是否继续?", "提示", { type: 'info' }).then(() => { //2.做删除业务TODO: //2.做删除业务 axios.delete(this.url + "/" + row.id).then((res) => { if (res.data.code == 20021) { this.$message.success("删除成功"); } else { this.$message.error("删除失败"); } }).finally(() => { this.getAll(); }); }).catch(() => { //3.取消删除 this.$message.info("取消删除操作"); }); } } }) </script> </html> 

原文链接:https://www.cnblogs.com/-turing/p/17206367.html

兔子先生 西安驾培

于灯火阑珊处,于暗香离别时,未曾放弃

相关推荐

抖音矩阵平台有哪些?

  抖音矩阵平台是指一系列用于在抖音平台上进行内容创作、运营和推广的工具和系统。其中,视界引擎短视频运营获客系统作为一款全面的抖音矩阵平台,具有许多优势。 抖音矩阵平台是当今数字营销领域的热门工 ...

怎么看待直播带货的营销形式?

随着电商行业的崛起,越来越多的商家开始使用直播的方式来进行产品推广,那么直播为什么可以推广产品带货呢?直播带货的本质是什么呢?今天,就来跟大家讲解一下。直播之所以可以推广产品,引导观众购买产品是因 ...

快抖矩阵多账号管理系统官方正版

账号源从哪里来?没有那么多抖音账号?怎么赚钱?有什么价值?       首先这是一套多开可招代理的矩阵账号管理系统,第一目的是让你拿来给有很多账号的品牌机构或达人当账号辅助管理工具打理账户 ...