SpringMVC

SpringMVC

一、MVC模型

1、MVC介绍

MVC全称Model View Controller,是一种设计创建Web应用程序的模式。这三个单词分别代表Web应用程序的三个部分:

  • Model(模型):指数据模型,用于存储数据及处理用户请求的业务逻辑在Web应用中,JavaBean对象,业务模型等都属于Model。
  • View(视图):用于展示模型中的数据,一般为jsp或html文件。
  • Controller(控制器):是应用程序中处理用户交互的部分。接收视图提出的请求,将数据交给模型,并把处理后的结果交给视图显示。(接收请求,返回响应,不进行处理起到一个视图和数据的交换)

2、SpringMVC

SpringMVC是一个基于MVC模型的轻量级Web框架,是Spring框架的一个模块,和Spring可以直接整合使用。SpringMVC代替了Servlet技术,它通过一套注解,让一个简单Java类成为处理请求的控制器,而无须实现任何接口

二、SpringMVC入门

1、入门案例

  • 使用maven创建web项目,补齐包结构。
  • 引入相关依赖和tomcat插件
<dependencies>
  <!-- spring核心模块 -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.11.RELEASE</version>
  </dependency>

  <!-- springWeb模块-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.11.RELEASE</version>
  </dependency>

  <!-- springMvc-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.11.RELEASE</version>
  </dependency>

  <!-- Jsp -->
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.1</version>
    <scope>provided</scope>
  </dependency>

  <!-- servlet -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.3</version>
    <!-- 在编译期有效运行期无效,因为tomcat插件中有该包,不是就会产生包冲突-->
    <scope>provided</scope>
  </dependency>

  <!-- junit-->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>

</dependencies>

<build>
  <plugins>
    <!-- tomcat插件 -->
    <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <version>2.1</version>
      <configuration>
        <port>8080</port>
        <path>/</path>
        <uriEncoding>UTF-8</uriEncoding>
        <server>tomcat7</server>
      </configuration>
    </plugin>
  </plugins>
</build>
  • 在web.xml中配置前端控制器DispatcherServlet
<display-name>Archetype Created Web Application</display-name>

<!-- SpringMVC前端控制器,本质就是一个servlet,接听所有请求,在内容启动时就会加载 -->
<servlet>
  <servlet-name>dispatcherServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 在加载web容器时加载spring配置文件-->
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>dispatcherServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>
  • 在spring的配置文件中添加 包扫描 和 SpringMVC注解支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 扫描包 -->
    <context:component-scan base-package="com.dianfei"></context:component-scan>

    <!-- 开启器SpringMvc注解支持 -->
    <mvc:annotation-driven></mvc:annotation-driven>

</beans>
  • 编写控制器
@Controller
public class MyController1 {
    // 该控制器的访问路径为/c1/hello1
    @RequestMapping("/c1/hello1")
    public void helloMVC(){
        System.out.println("hello SpringMVC");
    }
}
  • 使用tomcat插件启动项目,访问本机web

2、SpringMVC执行流

SpringMVC执行流程

SpringMVC组件

  • DispatcherServlet:前端控制器,接收所有请求,调用其他组件。
  • HandlerMapping:处理器映射器,根据配置找到方法的执行链。
  • HandlerAdapter:处理器适配器,根据方法类型找到对应的处理器
  • ViewResolver:视图解析器,找到指定视图。

组件的工作流程

  1. 客户端将请求发送给前端控制器。
  2. 前端控制器将请求处理器映射器,处理器映射器根据路径找到方法的执行链,返回前端控制器
  3. 前端控制器拿到方法的执行链发送给处理器适配器,处理器适配器根据方法的类型找到对应的控制器
  4. 控制器执行方法,将结果返回前端控制器。
  5. 前端控制器拿到返回的数据发送给视图解析器,视图解析器找到视图文件文件。
  6. 视图渲染数据并将结果显示到客户端。

三、请求参数获取

在Servlet中我们通过reques.getParameter(name)获取请求参数。该方式存在两个问题:

  • 请求参数比较多时会出现代码冗余
  • 与容器紧耦合

而SpringMVC支持参数注入的方式用于获取请求数据,将请求参数直接封装到方法的参数当中。

如果请求的name和参数名一致SpringMVC就会自动封装参数。

1、简单数据类型

  • 控制器方法

    // 获取简单数据类型
    @RequestMapping("c1/param")
    public void param1(String sId,String name){
        System.out.println(sId);
        System.out.println(name);
    }
    
  • 访问方法时,请求参数名和方法名参数名相同即可完成自动封装

    http://localhost:8080/c1/param?sId=001&name=dianfei

2、对象类型

  • 编写实体类

    public class Student {
        private String sId;
        private String name;
    }
    
  • 控制器方法

    // 获取对象数据类型
    @RequestMapping("c1/paramO")
    public void objectParam(Student student){
        System.out.println(student);
    }
    
  • 请求格式:属性名=值

    http://localhost:8080/c1/param?sId=001&name=dianfei

3、获取关联对象

  • 编写实体类

    public class Student {
        private String sId;
        private String name;
        private Address address;
    }
    public class Address {
        private String info;
        private String dept;
    }
    
  • 编写控制器方法

    // 获取关联对象数据类型
    @RequestMapping("c1/paramOR")
    public void reObjectParam(Student student){
        System.out.println(student);
    }
    
  • 访问请求格式 对象名.属性 = 值

    http://localhost:8080/c1/paramOR?sId=001&name=dianfei&address.info=dd&address.dept=d

4、集合类型

简单数据类型集合
//参数前必须添加@RequestParam

// 绑定简单数据类型List参数,
@RequestMapping("c1/paramL")
public void listParam(@RequestParam List<String> student){
    System.out.println(students);
}
//格式http://localhost:8080/c1/paramM?student=1&student=2


// 获取Map简单数据类型
@RequestMapping("c1/paramM")
public void mapParam(@RequestParam Map<String,String> student){
    System.out.println(student);
}
//格式http://localhost:8080/c1/paramM?key1=1&key2=3
对象数据类型集合

SpringMVC不支持将参数封装为对象类型的集合,但可以将对象类型的集合封装到对象中。

  • List

    • 实体类

      public class Student {
          private String sId;
          private String name;
          private List<Address> address;//地址集合
      }
      
    • 控制器方法

      // 获取List对象数据类型
      @RequestMapping("c1/paramLO")
      public void objectListParam(Student  student){
          System.out.println(student);
      }
      
    • 请求格式

      http://localhost:8080/c1/paramLO?sId=001&name=dianfei&address[0].info=dd&address[0].dept=w&address[1].info=cc&address[1].dept=e

  • Map

    • 是一样的用法只是请求格式变成了address[key].dept=value

http://localhost:8080/c1/paramMO?sId=001&name=dianfei&address[one].info=dd&address[one].dept=w&address[two].info=cc&address[two].dept=e

对象参数名和表单name对应(

如果请求的name和参数名一致SpringMVC就会自动封装参数。

例如请求参数格式是:sId=001&name=dianfei&address.info=dd&address.dept=d

表单写法,get方式和post方式都一样的

<!-- 
sId=001&name=dianfei&address.info=dd&address.dept=d
 -->
<form method="post" action="/c1/paramOR">
    学号:<input name="sId">
    姓名:<input name="name">
    地址:<input name="address.info">
    备注:<input name="address.dept">
    <input type="submit">
</form>

5、获取原生Servlet

SpringMVC支持使用Servlet原生对象,在方法参数中定义Servlet的原生对象即可直接使用。

// 使用原生servlet对象
@RequestMapping("c1/paramServlet")
public void getServlet(HttpServletRequest request, HttpServletResponse response, HttpSession session){
    System.out.println(request.getParameter("name"));
    System.out.println(response.getCharacterEncoding());
    System.out.println(session.getId());
}
  • 一般情况下,不会获取它的原生对象,SpringMVC都有对Servlet原生对象的方法的替代,SpringMVC提供的使用起来更方便,就没必要获取原生的Servlet了

6、自定义参数类型转换器

自定义类型转换器需要实现Converter接口

前端传来的参数全部为字符串类型,SpringMVC使用自带的转换器将字符串参数转为需要的类型。

但是某些情况,无法将字符串转为需要的类型,如:

@RequestMapping("c1/paramDate")
public void dateParam(Date birthday){
    System.out.println(birthday);
}

由于日期数据有很多格式,SpringMVC没办法把所有格式的字符串转换成日期类型。比如参数为2022-01-01时,SpringMVC就无法解析参数。此时需要自定义类型转换器。

  • 定义类型转换器类,实现Converter接口

    public class DateConverter implements Converter<String,Date> {
        /**
         * 转换方法
         * @param source 转换前
         * @return 转换后
         */
        @Override
        public Date convert(String source) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date date = null;
            try {
                date = sdf.parse(source);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return date;
        }
    }
    
  • 注册类型转换器对象

<!-- 配置转换工厂 -->
<bean id="converterFactory" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <!-- 转换器集合 -->
    <property name="converters">
        <set>
            <!-- 自定义转换器 -->
            <bean class="com.dianfei.converter.DateConverter"></bean>
        </set>
    </property>
</bean>

<!-- 使用转换器工厂 -->
<mvc:annotation-driven conversion-service="converterFactory"></mvc:annotation-driven>

7、编码过滤器

在传递参数时,tomcat8以上能处理get请求中文乱码,但不能处理post请求乱码。

编写。

<!-- SpringMVC中提供的字符编码过滤器,放在所有过滤器的最上方-->
<!-- 字符编码过滤器 -->
<filter>
  <filter-name>encFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>utf-8</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>encFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

四、响应处理

1、配置视图解析器

SpringMVC默认情况下会在控制器执行完成后跳转到视图页面,是通过视图解析器找到相应的视图,如果没有配置就会导致找不到视图。

在SpringMVC中提供了13个视图解析器,用于支持不同的视图技术。InternalResourceViewResolver是SpringMVC的默认视图解析器,用来解析JSP视图

<!-- 视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 视图前缀 -->
    <property name="prefix" value="/"></property>
    <!-- 视图后缀 -->
    <property name="suffix" value=".jsp"></property>
</bean>

2、控制器方法的返回值

spring控制器方法的返回值可以为void、String、ModelAndView类型

我们可以通过控制器方法的返回值设置跳转的视图。

  • void

    • 此时会跳转到名字是 前缀+方法路径名+后缀 的jsp页面
    //路径是helloMVC,方法执行后会跳转到/helloMVC.jsp
    @RequestMapping("/helloMVC")
    public void helloMVC(){
        System.out.println("hello SpringMVC");
    }
    
  • String

    • 此时会跳转到名字是 前缀+返回值+后缀 的jsp页面
    //返回值为String
    @RequestMapping("/c2/helloMVC2")
    public String helloMVC2(){
        System.out.println("hello SpringMVC");
        // 方法执行完后会跳转到 /index.jsp
        return "index";
    }
    
  • ModelAndView

    • 这是SpringMVC提供的对象,该对象可以向request域设置数据并指定跳转的页面。该对象包含Model对象和View对象。
      • Model:向request域中设置数据。
      • View:指定跳转的页面。
    // 返回值为ModelAndView
    @RequestMapping("/c2/mav")
    public ModelAndView mav(){
        // 1.创建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();
        // 2.获取model对象,本质是一个Map
        Map<String, Object> model = modelAndView.getModel();
        // 3.使用model向请求域中存放数据
        model.put("name","dianfei");
        // 4.使用View对象设置跳转的路径为dianfei.jsp
        modelAndView.setViewName("dianfei");
        return modelAndView;
    }
    

3、request域设置数据

SpringMVC可以使用HttpServletRequest、Map、Model、ModelMap向request域设置数据

当控制器返回值为ModelAndView时我们可以向request域设置数据,我们还有以下方法可以向request域设置数据

  • 使用原生态的HttpServletRquest
@RequestMapping("/c2/request1")
public String setRequest1(HttpServletRequest request){
    request.setAttribute("name","dainfei1");
    return "dianfei";
}
  • 使用Model、ModelMap
    • SpringMVC提供了Model接口和ModelMap类,控制器方法添加这两个类型的参数,使用该参数设置数据,该数据就会存到request域中。
@RequestMapping("/c2/request2")
public String setRequest2(Model mode, ModelMap modelMap){
    //使用Model将数据存放到request域中
    //mode.addAttribute("name","dianfei2");

    //使用ModelMap将数据存放到request域中
    modelMap.addAttribute("name","dianfei3");
    return "dianfei";
}
  • 使用map集合
    • Model接口底层就是一个Map集合,我们可以给控制器方法设置 Map类型的参数,向Map中添加键值对,数据也会存到request域 中。
@RequestMapping("/c2/request3")
public String setRequest3(Map map){
    map.put("name","dianfei4");
    return "dianfei";
}

4、向seession域中添加数据

Session作用域表示在当前会话中有效。在SpringMVC中对于 Session作用域传值,只能使用HttpSession对象来实现。

@RequestMapping("/c2/session")
public String setSession(HttpSession session){
    session.setAttribute("address","重庆");
    return "dianfei";
}

五、SpringMVC注解

1、@RequestMapping

  • 作用:给控制器方法设置请求路径
  • 位置:方法或类上方。用于类上,表示类中的所有控制器方法都是以该地址为父路径。
  • 属性:
    • vlaue/path:请求路径
    • method:指定请求方式
    • params:规定必须发送的请求参数
    • headers:规定请求必须包含的请求头
@Controller
@RequestMapping("/c3")
public class Controller3 {
    /*
        请求路径:/c3/student
        请求方式:支持get和post方式
        请求时必须带有name参数
        请求时必须带有User-agent请求头
     */
    @RequestMapping(path = "/student",method = {RequestMethod.POST,RequestMethod.GET},
            params = "name",headers = "User-agent")
    public String objectParam(String name){
        System.out.println(name);
        return "dianfei";
    }
}

2、@RequestParam

  • 作用;在控制器方法中获取请求参数

  • 位置:方法参数前

  • 属性:

    • name:指定请求参数名称
    • dafaultValue:为参数设置默认值
    • required:设置是否是必须传入的参数(默认是true必须传入,设置默认值也不会报错)
    /*
        定义请求参数名为username,不是必须传入参数,默认值为dianfei
     */
    @RequestMapping("/annotation2")
    public String annotation2(@RequestParam(name = "username",required = false,defaultValue = "dianfei") String name){
        System.out.println(name);
        return "dianfei";
    }
    

3、RequestHeader、@CookieValue

  • @RequestHeader
    • 作用:在控制器方法中获取请求头数据
    • 位置:方法参数前
  • @CookieValue
    • 作用:在控制器方法中获取Cookie数据
    • 位置:方法参数前
@RequestMapping("/annotation3")
public String annotation3(@RequestHeader("User-Agent") String userAgent, @CookieValue("JSESSIONID") String jSessionId){
    System.out.println(userAgent);
    System.out.println(jSessionId);
    return "dianfei";
}

4、@SessionAttributes

  • 作用:将Model模型中的数据存放到session域中
  • 位置:类上方
@Controller
@RequestMapping("/c3")
//将模型中的name数据放到session中保存
@SessionAttributes("name")
public class Controller4 {
    @RequestMapping("/annotation4")
    public String annotation4(Model model){
        model.addAttribute("name", "dainfei");
        return "dianfei";
    }
}

5、@ModelAttribute

  • 作用:设置指定方法在控制器其他方法前执行,从Model模型中获取数据给参数赋值。
  • 位置:方法上方(前置方法)、方法参数前(从Model模型中获取数据)
@Controller
@RequestMapping("/c3")
//将模型中的name数据放到session中保存
@SessionAttributes("name")
public class Controller4 {

    //在前置方法中添加数据
    @ModelAttribute
    public void before(Model model){
        System.out.println("前置方法");
        model.addAttribute("name","dianfei");
    }

    @RequestMapping("/annotation4")
    public String annotation4(@ModelAttribute("name") String name){
        System.out.println(name);
        return "dianfei";
    }
}

六、RESTful风格

RESTful风格是一种URL路径的设计风格。在RESTful风格的URL路径中,网络上的任意数据都可以看成一个资源,它可以是一段文本、一张图片,也可以是一个Java对象。而每个资源都会占据一个网络路径,无论对该资源进行增删改查,访问的路径是一致的。

传统URL:需要不同功能使用不同路径

  • 查找id为1的学生

    http://localhost:8080/student/findById?id=1

  • 删除id为1的学生

    http://localhost:8080/student/deleteById?id=1

RESTful风格URL: 不用功能使用同一路径(通过请求方式,判断进行的是什么操作)

  • 查找id为1的学生:

    http://localhost:8080/student/1 GET请求方法

  • 删除id为1的学生:

    http://localhost:8080/student/1 DELETE请求方式

我们了解的请求方式,GET请求和POST请求,而RESTful风格的URL一共有四种请求方式:

  • GET请求:查询操作
  • POST请求:新增请求
  • DELETE请求:删除操作
  • PUT请求:修改操作

RESTful风格的优点: 结构清晰、符合标准、易于理解、扩展方便。

1、Postman使用

默认情况下浏览器是无法发送DELETE请求和PUT请求的,我们可以使用Postman工具发送这些请求。

  • 双击安装包安装Postman即可,会自动安装,安装好了需要登录 x 了就行了
  • 创建连接工程

  • 添加请求

2、@PathVariable

  • 作用:在RESTful风格的URL中获取占位符的值
  • 位置:方法参数前
  • 属性:
    • value:获取那个占位符的值作为参数值,如果占位符和参数名相同,可以省略属性。
@Controller
@RequestMapping("/student")
public class StudentController {

    //添加学生
    @RequestMapping(value = "/{id}",method = RequestMethod.POST)
    public String insertStudent(@PathVariable("id") int id, Student student){
        System.out.println("添加id为" + id + "的学生");
        System.out.println(student);
        return "dianfei";
    }
    //查询学生
    @RequestMapping(value = "/{id}",method = RequestMethod.GET)
    public String findByIdStudent(@PathVariable("id") int id){
        System.out.println("查询id为" + id + "的学生");
        return "dianfei";
    }
    //修改学生
    @RequestMapping(value = "/{id}",method = RequestMethod.PUT)
    public String updateStudent(@PathVariable("id") int id,Student student){
        System.out.println("修改id为" + id + "的学生");
        System.out.println(student);
        return "dianfei";
    }
    //删除学生
    @RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
    public String deleteStudent(@PathVariable("id") int id){
        System.out.println("删除id为" + id + "的学生");
        return "dianfei";
    }
}

访问方式:

  • 新增学生:POST http://localhost:8080/student/1?id=1&name=dianfei
  • 查询学生:GET http://localhost:8080/student/1?id=1
  • 修改学生:PUT http://localhost:8080/student/1?id=1&name=dianfei
  • 删除学生:DELETE http://localhost:8080/student/1?id=1

3、@PostMapping、@GetMapping、@PutMapping、@DeleteMapping

  • 作用:简化设置请求方式的@RequestMapping写法。
  • 位置:方法上方。
//现在写法
//添加学生
@PostMapping("/{id}")
public String insertStudent(@PathVariable("id") int id, Student student){
    System.out.println("添加id为" + id + "的学生");
    System.out.println(student);
    return "dianfei";
}


//以前写法
//添加学生
    @RequestMapping(value = "/{id}",method = RequestMethod.POST)
    public String insertStudent(@PathVariable("id") int id, Student student){
        System.out.println("添加id为" + id + "的学生");
        System.out.println(student);
        return "dianfei";
    }

4、浏览器发送请求方式

由于浏览器form表单只支持GET与POST请求,而DELETE、PUT请 求并不支持,SpringMVC有一个过滤器 Hidde nHttpMethodFilter,可以将浏览器的POST请求改为指定的请求方式,发送给的控制器方法。

  • 在web.xml中配置过滤器
<!-- 请求方式过滤器 -->
<filter>
  <filter-name>httpMethodFilter</filter-name>
  <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>httpMethodFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
  • 控制器方法
@Controller
@RequestMapping("/student")
public class StudentController2 {
    //删除学生
    @DeleteMapping("/delete")
    public String deleteStudent(){
        System.out.println("删除方法");
        return "dianfei";
    }

    //修改学生
    @PutMapping("/put")
    public String testPut(){
        System.out.println("修改方法");
        return "dianfei";
    }
}
  • 在jsp中编写表单
<html>
<head>
    <title>DELETE、PUT提交</title>
</head>
<body>
    <%-- 提交DELETE、PUT请求,表单必须提交方式为post --%>
    <%-- DELETE提交方式 --%>
    <form action="/student/delete" method="post">
        <input type="hidden" name="_method" value="DELETE">
        <input type="submit" value="删除">
    </form>

    <%-- PUT提交方式 --%>
    <form action="/student/put" method="post">
        <input type="hidden" name="_method" value="PUT">
        <input type="submit" value="修改">
    </form>
</body>
</html>

5、jSON和JAVA对象的相互转换

@ResponseBody

作用:方法返回的对象转换为JSON格式,并将JSON数据直接写入到输出流中,使用此注解后不会再经过视图解析器。使用该注解可以处理Ajax请求。

  • 依赖导入,SpringMVC会将Result对象转为JSON格式写入输出流,而SpringMVC默认使用的JSON转换器jackson,需要添加jackson依赖
<!-- jackson -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.9.0</version>
</dependency>
  • jsp页面,发送Ajax请求
<html>
<head>
    <title>ajax请求</title>
    <script src="/js/jquery-3.6.0.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                var name = $("#name").val();
                var sex = $("#sex").val();
                $.get("/c4/addStudent",{"name":name,"sex":sex},function (data) {
                    console.log(data);
                });
            })
        })
    </script>
</head>
<body>
    姓名:<input id="name"><br>
    性别:<input id="sex"><br>
    <input type="button" value="提交" id="btn">
</body>
</html>
  • 由于jsp页面中引入jQuery的js文件,而SpringMVC会拦截所有静态资源,造成jquery.js失效,需要在SpringMVC核心配置文件中放行静态资源。
<!-- 设置不过滤静态资源 -->
<mvc:default-servlet-handler/>
  • 结果实体类,该类会封装一共请求的结果。
public class Result {
    private boolean flag;//请求是否成功
    private String message;//请求提示信息
}
  • 控制器代码
@Controller
@RequestMapping("/c4")
public class StudentController3 {
    @GetMapping("/addStudent")
    @ResponseBody
    public Result addStudent(String name, String sex) {
        System.out.println("姓名:" + name + "性别:" + sex);
        Result result = new Result(false, "添加学生成功");
        return result;
    }
}
@RestController

@ResponseBody只能控制一个方法对象转为JSON字符串,@RestController放在类放控制一个类

@Controller
@RequestMapping("/c4")
@RestController
public class StudentController3 {
    @GetMapping("/addStudent")
    public Result addStudent(String name, String sex) {
        System.out.println("姓名:" + name + "性别:" + sex);
        Result result = new Result(false, "添加学生成功");
        return result;
    }
}
@RequestBody

作用:将JSON格式的参数转为JAVA对象

位置:写在方法参数前方

  • Ajax请求发送JSON格式的参数
<html>
<head>
    <title>ajax请求</title>
    <script src="/js/jquery-3.6.0.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                var name = $("#name").val();
                var sex = $("#sex").val();

                var param = JSON.stringify({"name":name,"sex":sex});
                $.ajax({
                    url:"/c4/addStudent2",
                    contentType:"/application/json",
                    type:"post",
                    data:param,
                    success:function () {
                        console.log(data);
                    }
                })
            })
        })
    </script>
</head>
<body>
    姓名:<input id="name"><br>
    性别:<input id="sex"><br>
    <input type="button" value="提交" id="btn">
</body>
</html>
  • 控制器处理数据代码
@PostMapping("/addStudent2")
@ResponseBody //响应时时对象转JSON字符串
//请求时JSON字符串转对象
public Result addStudent2(@RequestBody Student student) {
    System.out.println(student);
    Result result = new Result(false, "添加学生成功");
    return result;
}

6、静态资源映射

当在DispatcherServlet(前端控制器)的中配置拦截 “/” 时,除了jsp文件不会拦截以外,其他所有的请求都会经过前端控制器进行匹配。此时静态资源例如css、js、jpg等就会被前端控制器拦截,导致不能访问,出现404问题。想要正常映射静态资源共有三种方案:

  • 配置静态资源筛查器

    • 会在Spring容器中创建一个资源检查器,它对进入 DispatcherServlet的URL进行筛查,如果不是静态资源,才由 DispatcherServlet处理。
    <!-- 设置不过滤静态资源 -->
    <mvc:default-servlet-handler/>
    
  • 配置静态资源映射器

    • 配置的路径就不会被过滤了
    <!-- 配置静态资源映射器 -->
    <!-- mapping:配置请求的URL location:资源路径-->
    <mvc:resources mapping="/js" location="/js"></mvc:resources>
    
  • 配置默认Servlet处理静态资源

    • 在web.xml可以配置默认Servlet处理静态资源,该Servlet由tomcat 提供,它会直接访问静态资源不进行其他操作。这样就避免了使用 DispatcherServlet对静态资源的拦截。
    <servlet-mapping>
      <servlet-name>default</servlet-name>
      <url-pattern>*.jpg</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
      <servlet-name>default</servlet-name>
      <url-pattern>*.js</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
      <servlet-name>default</servlet-name>
      <url-pattern>*.css</url-pattern>
    </servlet-mapping>
    

六、文件的上传与下载

1、使用JAVAEE原生方式上传

  • 修改web.xml版本将默认的版本2.3改为3.1,因为3.1默认开启了el表达式

    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
    http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             id="WebApp_ID" version="3.1">
    
  • 导入依赖,这里比之前其实就多了一个文件上传依赖

    <dependencies>
        <!-- spring核心模块 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.11.RELEASE</version>
        </dependency>
    
        <!-- springWeb模块-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.11.RELEASE</version>
        </dependency>
    
        <!-- springMvc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.11.RELEASE</version>
        </dependency>
    
        <!-- Jsp -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
    
        <!-- servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.3</version>
            <!-- 在编译期有效运行期无效,因为tomcat插件中有该包,不是就会产生包冲突-->
            <scope>provided</scope>
        </dependency>
    
        <!-- junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    
        <!-- jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.0</version>
        </dependency>
    
        <!-- 文件上传 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>
    
  • 编写上传表单

    <html>
    <head>
        <title>上传</title>
    </head>
    <body>
    <h3>文件上传</h3>
    <%-- 上传表单必须是post方式
    enctype属性为multipart/form-data,意思是不对表单数据进行字符编码,就使用默认的字节输入--%>
    <form action="/fileUpload" method="post" enctype="multipart/form-data">
        <%-- 文件选择控件,类型是file,必须要有name属性--%>
        <input type="file" name="upload"><br>
        <input type="submit" value="上传">
    </form>
    </body>
    </html>
    
  • 编写控制器接收上传请求,控制器进行三部操作:

    • 创建文件夹,存放上传文件
    • 分析请求体,找到上传文件数据
    • 将文件数据写入文件夹
    public String upload(HttpServletRequest request) throws Exception {
        //创建文件夹
        // 1.设置上传文件的真实目录
        String realPath = request.getSession().getServletContext().getRealPath("upload");
        // 2.检测目录是否存在,不存在则创建
        File file = new File(realPath);
        if(!file.exists()){
            file.mkdirs();
        }
    
        // 分析请求体,找到上传文件数据
        // 1.创建磁盘文件工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 2.创建上传数据分析器对象
        ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
        // 3.利用分析器对象解析请求体,返回所有数据项
        List<FileItem> fileItems = servletFileUpload.parseRequest(request);
        // 4.遍历所有数据,找到文件项(非表单项)
        for (FileItem fileItem : fileItems) {
            
            // 将文件写入磁盘
            // 1.获取文件名
            String name = fileItem.getName();
            // 2.将文件写入磁盘
            fileItem.write(new File(file,name));
            // 3.删除内存中的临时文件
            fileItem.delete();
        }
        return "index";
    }
    

2、SpringMVC方式上传

  • 配置文件解析器,SpringMVC会把文件解析

    <!-- 文件解析器对象,id名必须是multipartResolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 支持一次上传文件的总内容。单位:字节100M = 100*1024*1024 -->
        <property name="maxUploadSize" value="104857600"/>
        <!-- 设置文件默认编码 -->
        <property name="defaultEncoding" value="utf-8"/>
    </bean>
    
  • jsp表单

    <form action="/fileUpload2" method="post" enctype="multipart/form-data">
        <input type="file" name="file"><br>
        <input type="submit" value="上传">
    </form>
    
  • 控制器代码

    // MultipartFile参数必须和jsp文件空间的name属性一致
    @RequestMapping("/fileUpload2")
    public String upload2(MultipartFile file, HttpServletRequest request) throws Exception {
        //创建文件夹
        // 1.设置上传文件的真实目录
        String realPath = request.getSession().getServletContext().getRealPath("upload");
        // 2.检测目录是否存在,不存在则创建
        File dir = new File(realPath);
        if(!dir.exists()){
            dir.mkdirs();
        }
    
        // 将上传的数据写到文件夹中
        // 1.拿到上传的文件名
        String filename = file.getOriginalFilename();
        filename = UUID.randomUUID() + "_" + filename;
        // 2.创建空文件
        File newFile = new File(dir, filename);
        // 3.将数据写入文件中
        file.transferTo(newFile);
    
        return "index";
    }
    

3、多文件上传

SpringMVC支持一次性上传多个文件,写法如下:

  • 创建JSP表单

    <form action="/fileUpload3" method="post" enctype="multipart/form-data">
        <%-- 多个文件的文件名需要一致不然无法自动映射到文件数组中去--%>
        用户名:<input name="username"><br>
        文件1:<input type="file" name="files"><br>
        文件2:<input type="file" name="files"><br>
        <input type="submit" value="上传">
    </form>
    
  • 控制器方法

    // 处理多个文件上传,参数类型为MultipartFile数组,参数名和JSP文件控件的name属性一致
    @RequestMapping("/fileUpload3")
    public String upload3(MultipartFile files[], String username, HttpServletRequest request) throws Exception {
        System.out.println(username);
        //创建文件夹
        // 1.设置上传文件的真实目录
        String realPath = request.getSession().getServletContext().getRealPath("upload");
        // 2.检测目录是否存在,不存在则创建
        File dir = new File(realPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
    
        // 遍历数组,将上传文件保存到文件夹中
        for (MultipartFile file : files) {
            // 1.获取文件名
            String filename = file.getOriginalFilename();
            // 2.创建新的空文件
            filename = UUID.randomUUID() + "_" + filename;
            File newFile = new File(dir, filename);
            // 3.将上传文件数据传入空文件中
            file.transferTo(newFile);
        }
        return "index";
    }
    

4、异步上传

之前的上传案例,在上传成功后都会跳转页面。而实际开发中,很多情况下上传部进行跳转,而是进行页面的局部刷新,比如:上传头像成功后将头像显示在网页中。这时候就需要使用异步文件上传。

  • jsp编写需要引入JQuery和JQuery表单上传工具jquery.form.js

    <html>
    <head>
        <title>异步上传</title>
        <script src="js/jquery-3.6.0.js"></script>
        <script src="js/jquery.form.js"></script>
    </head>
    <body>
    <h3>文件上传</h3>
    <form id="ajaxFile" enctype="multipart/form-data">
        文件:<input type="file" name="file"><br>
        <%-- 按钮不能是提交按钮否则会刷新 --%>
        <input type="button" value="上传" id="btn">
    </form>
    <img src="/" width="100px" id="img">
    
    <script>
        $(function () {
            $("#btn").click(function () {
                //异步提交表单
                $("#ajaxFile").ajaxSubmit({
                    url:"/fileUpload4",
                    type:"post",
                    success:function (data) {
                        $("#img").attr("src",data);
                    }
                })
            })
        })
    </script>
    </body>
    </html>
    
  • 控制器异步上传请求,将文件保存了,然后把路径返回给jsp,然后jsp显示

    // MultipartFile参数必须和jsp文件空间的name属性一致
    @RequestMapping("/fileUpload4")
    @ResponseBody
    public String upload4(MultipartFile file, HttpServletRequest request) throws Exception {
        //创建文件夹
        // 1.设置上传文件的真实目录
        String realPath = request.getSession().getServletContext().getRealPath("/upload");
        // 2.检测目录是否存在,不存在则创建
        File dir = new File(realPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
    
        // 将上传的数据写到文件夹中
        // 1.拿到上传的文件名
        String filename = file.getOriginalFilename();
        filename = UUID.randomUUID() + "_" + filename;
        // 2.创建空文件
        File newFile = new File(dir, filename);
        // 3.将数据写入文件中
        file.transferTo(newFile);
        
        //将存储后的路径返回
        return "/upload/" + filename;
    }
    

5、跨服上传

由于文件占据磁盘空间较大,在实际开发中往往会将文件上传到其他服务器中,此时需要使用跨服务器上传文件。

  • 本地创建一个tomcat文件服务器,在tomcat的webapps下创建upload目录作为文件上传目录,并对文件服务器做如下修改:(支持跨服上传,端口号)

  • 支持跨服上传,修改tomcat的conf/web.xm文件

    <servlet>    
        <init-param>        
            <param-name>readonly</param-name>
            <param-value>false</param-value>  
        </init-param>
    </servlet>
    
  • 修改端口号,修改tomcat的conf/server.xml

    <Connector port="8081" protocol="HTTP/1.1"
                  connectionTimeout="20000"
    redirectPort="8443" />
    
  • 添加跨服上传依赖

    <!-- 跨服上传 -->
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-core</artifactId>
        <version>1.19.4</version>
    </dependency>
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-client</artifactId>
        <version>1.19.4</version>
    </dependency>
    
  • 编写跨服上传

@RequestMapping("/fileUpload5")
@ResponseBody
public String upload5(MultipartFile file, HttpServletRequest request) throws Exception {
    // 设置跨服上传的服务器路径
    String path = "http://localhost:8081/upload/";

    // 设置上传的文件名
    String filename = file.getOriginalFilename();
    filename = UUID.randomUUID() + "_" + filename;

    // 跨服上传
    // 1.创建客户端对象
    Client client = Client.create();
    // 2.使用客户端对象连接文件服务器
    WebResource resource = client.resource(path + filename);
    // 3.数据上传
    resource.put(file.getBytes());

    //返回文件路路径
    return path + filename;
}

6、文件下载

  • 查询所有可下载的文件
    • 编写控制器方法,查询所有可下载的文件,并跳转到jsp页面显示可下载文件
// 显示文件名
@RequestMapping("/showFile")
public String showFile(HttpServletRequest request, Model model){
    // 1.获取下载文件路径集合。注:跨服务器上传中,网络路径无法获取文件列表。
    String realPath = request.getSession().getServletContext().getRealPath("/upload");
    File file = new File(realPath);
    String[] files = file.list();

    // 2.将路径放到model模型中
    model.addAttribute("files",files);
    model.addAttribute("dir","/upload/");
    return "download";
}

jsp页面

<html>
<head>
    <title>下载</title>
</head>
<body>
    <h3>文件下载</h3>
    <%-- 遍历文件集合 --%>
    <c:forEach items="${files}" var="file">
        <a href="/download?fileName=${file}" >
            <img src="${dir}${file}" width="100px" height="50px">
        </a>
    </c:forEach>
</body>
</html>

jsp页面需要使用c表达式,所以需要导入JSTL包

<!-- JSTL -->
<dependency>
    <groupId>org.apache.taglibs</groupId>
    <artifactId>taglibs-standard-spec</artifactId>
    <version>1.2.5</version>
</dependency>
<dependency>
    <groupId>org.apache.taglibs</groupId>
    <artifactId>taglibs-standard-impl</artifactId>
    <version>1.2.5</version>
</dependency>
  • 编写下载控制器
//文件下载
@RequestMapping("/download")
public void download(HttpServletRequest request, HttpServletResponse response,String fileName) throws IOException {
    // 设置响应头
    response.setHeader("Content-Disposition","attachment;filename="+fileName);

    //获取文件路径
    String realPath = request.getSession().getServletContext().getRealPath("/upload");
    File file = new File(realPath,fileName);

    //获取字符输出流
    ServletOutputStream os = response.getOutputStream();

    //使用输出流写出文件
    //FileUtils 类里面有个把字符流转换为字节流数组
    os.write(FileUtils.readFileToByteArray(file));

    os.flush();
    os.close();

}

七、异常处理

在系统当中, Dao、Service、Controller层代码出现都可能抛出异 常。如果哪里产生异常就在哪里处理,则会降低开发效率。所以一 般情况下我们会让异常向上抛出,最终到达DispatcherServlet中, 此时SpringMVC提供了异常处理器进行异常处理,这样可以提高开发效率。

1、局部和全局异常

局部异常处理@ExceptionHandler

在控制器中定义异常处理方法只能处理该类的异常

@Controller
public class MyController {
    @RequestMapping("/t1")
    public String t1(){
        /*String str = null;
        str.length();*/
        //int i = 1/0;
        int[] arr = new int[1];
        arr[2]=10;
        return "index";
    }

    /**
     * 异常处理方法一
     * @param e 异常对象
     * @param model 模型对象
     * @return
     */
    //添加@ExceptionHandler,表示该方法是处理异常的方法,属性为处理的异常类
    @ExceptionHandler({java.lang.NullPointerException.class,java.lang.ArithmeticException.class})
    public String exceptionHandle1(Exception e, Model model){
        //向模型中添加一次对象
        model.addAttribute("msg",e);
        //跳转页面
        return "error";
    }

    //方法一不处理的交给方法二
    @ExceptionHandler(java.lang.Exception.class)
    public String exception(Exception e, Model model){
        //向模型中添加一次对象
        model.addAttribute("msg",e);
        //跳转页面
        return "error2";
    }
}

全局异常处理@ControllerAdvice

因为局部异常只能处理那一个类的异常不能处理全部的,全局异常在类的上方。

全局异常能处理所有类的异常(开发过程中基本都是使用全局异常)

注:当出现全局异常和局部异常都有时会优先使用局部异常

//全局异常处理器,需要添加@ControllerAdvice
@ControllerAdvice
public class GlobalException {
    //添加@ExceptionHandler,表示该方法是处理异常的方法,属性为处理的异常类
    @ExceptionHandler({java.lang.NullPointerException.class,java.lang.ArithmeticException.class})
    public String exceptionHandle1(Exception e, Model model){
        //向模型中添加一次对象
        model.addAttribute("msg",e);
        //跳转页面
        return "error";
    }

    //方法一不处理的交给方法二
    @ExceptionHandler(java.lang.Exception.class)
    public String exception(Exception e, Model model){
        //向模型中添加一次对象
        model.addAttribute("msg",e);
        //跳转页面
        return "error3";
    }
}

2、自定义异常

我们可以实现HandlerExceptionResolver接口实现resolveException方法进行自定义异常。

//自定义异常处理实现HandlerExceptionResolver接口,并放入spring容器中
@Component
public class MyController3 implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView modelAndView = new ModelAndView();
        //为空指针异常时,跳转到error页面,其他异常跳转到error2页面
        if(e instanceof NullPointerException){
            modelAndView.setViewName("error");
        }else {
            modelAndView.setViewName("error2");
        }

        modelAndView.addObject("msg",e);
        return modelAndView;
    }
}

八、拦截器

SpringMVC的拦截器(Interceptor)也是AOP思想的一种实现方式。它与Servlet的过滤器(Filter)功能类似,主要用于拦截用户的 请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。

拦截器和过滤器的区别

  • 拦截器是SpringMVC组件,而过滤器是Servlet组件。
  • 拦截器不依赖Web容器,过滤器依赖Web容器。
  • 拦截器只能对控制器请求起作用,而过滤器则可以对所有的请求起作用。
  • 拦截器可以直接获取IOC容器中的对象,而过滤器就不太方便获取。

1、拦截器的使用

创建拦截器类,该类实现HandlerInterceptor接口,需要重写三个方法:

  • preHandle:请求到达Controller前执行的方法,返回值为true通过拦截器,返回值为false被 拦截器拦截。
  • postHandle:跳转到JSP前执行的方法
  • afterCompletion:跳转到JSP后执行的方法

拦截器类

// 控制器拦截类
public class MyInterceptor implements HandlerInterceptor {
    // 请求到达Controller前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行controller前");
        return true;//返回true通过,false拦截
    }

    // 请求到达jsp前执行,所以往请求域中放数据有效
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("调转到jsp前");
        request.setAttribute("name","dianfei");
    }

    // 请求到达jsp后执行,所以往请求域中放数据无效
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("跳转到jsp后");
        request.setAttribute("age",10);
    }
}

使用前需要在SpringMVC核心配置文件中配置拦截器

<!-- 配置拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 拦截器的作用路径 -->
        <mvc:mapping path="/**"/>
        <!-- 拦截器对象 -->
        <bean class="com.dianfei.interceptor.MyInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

2、全局拦截器配置

  • 全局拦截器可以拦截所有控制器处理的URL,作用等于/**。
  • 全局拦截器不管放在配置文件的任何位置都会最先执行。
<!-- 配置拦截器 -->
<mvc:interceptors>
    <!--全局拦截器配置-->
    <bean class="com.dianfei.interceptor.MyInterceptor"></bean>
</mvc:interceptors>

3、拦截器链与执行流程

如果一个URL能够被多个拦截器所拦截,全局拦截器最先执行,其他拦截器根据配置文件中配置的从上到下执行。

拦截器1:执行controller前
拦截器2:执行controller前

控制器方法

拦截器2:调转到jsp前
拦截器1:调转到jsp前
拦截器2:跳转到jsp后
拦截器1:跳转到jsp后

结论(重):

  • preHandle()顺序执行,postHandle()、afterCompletion()逆序执行。
  • 只要有一个preHandle()拦截,后面的preHandle(),postHandle()都不会执行。
  • 只要有相应的preHandle()放行,afterCompletion()就会执行。

4、拦截器过滤敏感词案例

在系统中,我们需要将所响应中的一些敏感词替换为**,此时可以使用拦截器达到要求。

拦截器代码

public class MyInterSensitiveWordInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 敏感词列表
        String[] sensitiveWords = {"坏蛋","暴力","笨蛋"};
        // 获取Model中全部数据
        Map<String, Object> model = modelAndView.getModel();

        // 遍历Model与敏感词列表匹配替换**
        Set<Map.Entry<String, Object>> entries = model.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue().toString();

            //将model值和敏感词列表遍历比对
            for (String sensitiveWord : sensitiveWords) {
                //如果包含敏感词则替换
                if(value.contains(sensitiveWord)){
                    String newValue = value.replaceAll(sensitiveWord, "**");
                    model.put(key,newValue);
                }
            }
        }
    }
    
}

九、跨域请求

1、同源策略

同源策略是浏览器的一个安全功能。同源,指的是两个URL的协 议,域名,端口相同。浏览器出于安全方面的考虑,不同源的客户 端脚本在没有明确授权的情况下,不能读写对方资源。

那些不受同源策略限制:

  • 页面中的 a 标签跳转,表单提交不会受同源策略限制。
  • 静态资源引入也不会受到同源策略限制,如嵌入到页面中的src路径和href路径

最容易收到同源策略影响的就是Ajax请求。

2、跨域请求

当请求URL的协议、域名、端口三者任意一个与当前页面URL不同是即为跨域。浏览器执行JavaScript脚本时,会检查当前请求是否同源,如果不是同源资源,就部会本执行。

例如

  • 控制器方法:
@RequestMapping("/m3")
@ResponseBody
public String m3(){
    System.out.println("测试跨域请求");
    return "success!";
}
  • Ajax请求,发送异步请求
<html>
<head>
    <title>跨域请求</title>
    <script src="/js/jquery-3.6.0.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                /*$.get("http://localhost:8080/m3",function (data) {
                    console.log(data);
                })*/
                $.get("http://127.0.0.1:8080/m3",function (data) {
                    console.log(data);
                })
            })
        })
    </script>
</head>
<body>
<input type="button"  id="btn" value="异步请求">
</body>
</html>
  • 结果:

    • http://localhost:8080/m3能发送异步请求

    • http://127.0.0.1:8080/m3由于同源无法发送异步请求,报异常:

3、控制器接收跨域请求

SpringMVC提供了注解@CrossOrigin解决跨域问题。用法如下:

@RequestMapping("/m3")
@ResponseBody
//跨域请求需要在控制器中加需要跨域的地址
@CrossOrigin("http://localhost:8080")
public String m3(){
    System.out.println("测试跨域请求");
    return "success!";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值