文章目录

  • 前言
  • 什么是Spring Web MVC?
    • 什么是MVC
    • 什么是Spring MVC?
  • Spring Boot 和 Spring MVC 的区别
    • 什么是Spring Boot?
    • 关系和区别
  • Spring MVC 学习
    • 注解介绍
      • 1. @SpringBootApplication
      • 2. @RestController
      • 3. @RequestMapping
        • 3.1 @RequestMapping 使用
        • 3.2 @RequestMapping 能接受 GET 方法还是 POST 方法
      • 4 请求
        • 4.1 传递单个参数
        • 4.2 传递多个参数
        • 4.3 传递对象
        • 4.4 参数重命名
        • 4.5 传递数组
        • 4.6 传递集合
        • 4.6 传递JSON数据
        • 4.7 获取URL中参数
        • 4.8 上传文件
        • 4.9 获取cookie 和 session
        • 4.10 获取 header
      • 5. 响应
        • 5.1 返回静态页面
        • 5.2 返回数据
        • 5.3 返回HTML代码片段
        • 5.4 返回JSON
        • 5.5 设置状态码
        • 5.6 设置header
        • 5.7 设置响应的Content-Type

前言

前面我们了解了什么是Spring,那么今天我将为大家分享一种在日常网站开发中使用非常广泛的框架——Spring Web MVC。

什么是Spring Web MVC?

先来看看官方解释。



Spring Web MVC是Spring Framework提供的Web组件,它是一个MVC设计模式的框架,主要用于开发灵活、松散耦合的Web应用程序。它提供了模型-视图-控制(Model-View-Controller,简称MVC)的体系结构和可以用来开发Web应用程序的组件。

Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架。它使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助简化日常Web开发的。Spring Web MVC也是要简化我们日常Web开发的。

一直提到 MVC 架构模式,那么到底什么是 MVC 呢?

什么是MVC

MVC(Model-View-Controller)架构模型是一种软件设计模式,用于将用户界面、数据模型和控制器分离开来。这种架构模式可以提高系统的可维护性、可扩展性和灵活性。在MVC架构中,模型(Model)负责处理应用程序的数据和业务逻辑,视图(View)负责展示数据给用户,控制器(Controller)负责处理用户请求并协调模型和视图之间的交互。

  • View(视图):指在应⽤程序中专⻔⽤来与浏览器进⾏交互,展⽰数据的资源.
  • Model(模型):是应⽤程序的主体部分,⽤来处理程序中数据逻辑的部分.
  • Controller(控制器):可以理解为⼀个分发器,⽤来决定对于视图发来的请求,需要⽤哪⼀个模型来处理,以及处理完后需要跳回到哪⼀个视图。即⽤来连接视图和模型

MVC架构模式通常应用于桌面应用程序和Web应用程序开发中。在Web应用程序中,控制器通常由Servlet或Controller类来实现,模型通常由Java类来实现,视图则由HTML或JSP页面来实现。通过MVC架构模式,可以使得程序的各个部分更加独立,更易于维护和扩展。

什么是Spring MVC?

MVC 是一种架构设计模式,也是一种思想,而 Spring MVC 则是对 MVC 思想的具体实现,除此之外,Spring MVC 还是一个 Web 架构。

总结来说,Spring MVC 就是一个实现了 MVC 架构模式的 Web 框架。

在前面我们创建 Spring Boot 项目的时候,勾选的 Spring Web 框架其实就是 Spring MVC 框架。


那么这里有 Spring Boot 又有 Spring MVC,那么它们两个之间的区别是什么呢?

Spring Boot 和 Spring MVC 的区别

什么是Spring Boot?

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。Spring Boot是Spring家族的一个子项目,其设计初衷是为了简化Spring配置,从而可以轻松构建独立运行的程序,并极大提高开发效率。

关系和区别

Spring MVC是一个用于构建Web应用程序的框架,它基于MVC设计模式,将业务逻辑、数据和界面显示分离,减少了各组件之间的依赖,提高了代码的可重用性。Spring MVC需要手动配置XML或其他类型的配置文件来管理应用程序的各个组件和依赖关系。

Spring Boot则是一个更为现代化的框架,它旨在简化Spring应用的初始搭建以及开发过程。通过约定优于配置的原则,Spring Boot可以自动配置应用程序的各种组件和依赖关系,避免了繁琐的手动配置过程。此外,Spring Boot还提供了许多内置的功能和工具,如内嵌的Web服务器、自动化测试、安全控制等,使得开发人员可以更加专注于业务逻辑的实现。

因此,可以说Spring Boot是Spring MVC的一种升级和优化,它通过自动配置和内置的工具简化了开发过程,提高了开发效率。同时,Spring Boot也支持使用Spring MVC等其他Spring组件,可以与它们无缝集成。

简单点来说,就是 Spring MVC 是通过 Spring Boot 添加 Spring MVC 依赖来实现 web 功能的。

虽然 Spring MVC 实现了 MVC 架构模式,但 Spring MVC 也结合了自身的特点,做了一些改变。

Spring MVC 学习

在知道了什么是 Spring MVC 之后,我们就可以来学习 Spring MVC 了,而学习 Spring MVC 关键是学习它的注解,因为在 Spring MVC 中,会使用到非常多的注解。

注解介绍

1. @SpringBootApplication

当我们创建完成一个 Spring MVC 项目之后,就会自动生成一个 “项目名”+Application 的类,并且在这个类中我们就可以看到第一个注解。


@SpringBootApplication 是一个方便的注解,它是 @SpringBootConfiguration 和 @EnableAutoConfiguration 的组合。这个注解让我们快速地开始一个 Spring Boot 项目。

  • @SpringBootConfiguration:该注解表示当前类是一个 Spring Boot 的配置类,它会包含一些基础的 Spring Boot 配置。
  • @EnableAutoConfiguration:该注解用于告诉 Spring Boot,根据当前项目中的类路径以及已经配置的属性,自动地选择和配置需要的 Bean。这大大简化了新项目的设置。

当你在 Spring Boot 项目中使用 @SpringBootApplication 注解时,它会自动配置一个开发环境,以便你可以快速启动并测试你的应用程序。同时,它也提供了一个基础的目录结构,让你可以更容易地管理你的代码。

也就是说 @SpringBootApplication 注解的类是 Spring MVC 项目的启动类,项目启动就是从这个类启动的。

我们先写一个简单的 Spring MVC 代码。

package com.example.springmvcdemo2;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloController {@RequestMapping("/hi")public String hi() {return "你好,Spring MVC";}}

写完这段代码启动之后,控制台会出现下面这样的结果。


出现这样的结果就表示我们的 Spring MVC 项目启动成功。

然后我们就可以使用浏览器访问当前的 Spring MVC 项目,在浏览器搜索栏输入http"//127.0.0.1:8080/hi,为什么要这样输入 URL 呢?前面我们说过,Spring 是在 servlet 的基础上开发的,所以 Spring 使用的也是 tomcat 服务器,tomcat 默认绑定的是 8080 端口和 8005 管理端口,所以访问 Spring 项目也就需要访问我们电脑的 8080 端口,但是前面我们学习servlet的时候,会发现 URL 中除了有/hi之外,还会有一个项目名称在路径中,但是这里访问 Spring MVC 项目的时候为什么不需要在 URL 中添加项目名称呢?前面的 servlet 中,tomcat和项目之间的关系是:一个 tomcat 服务器下面可以有多个项目,而在 Spring MVC 中,tomcat 和项目之间的关系是:一个 Spring MVC 项目下含有一个 tomcat 服务器,也就是一个项目对应一个 tomcat 服务器,换句话说就是:Spring MVC 允许同一时间启动多个 tomcat 服务器,只要保证这多个 tomcat 服务器的端口不冲突就可以,所以我们在访问 Spring MVC 项目的时候就不需要指定项目名称。

然后 /hi 就是我们 @RequestMapping 中的内容。

上面的简单代码中也出现了两个新的注解 @RestController 和 @RequestMapping。我们来看看这两个注解的作用。

2. @RestController

⼀个 Spring MVC 项⽬中,会有很多类,每个类可能有很多的⽅法,Spring程序怎么知道要执⾏哪个⽅法呢” />3. @RequestMapping

在 Spring MVC 中使⽤ @RequestMapping 来实现 URL 路由映射,也就是浏览器连接程序的作⽤。

路由映射:当用户访问⼀个 URL 时, 将⽤⼾的请求对应到程序中某个类的某个⽅法的过程就叫路由映射。

我们在上面的代码中,当访问了 127.0.0.1:8080 ,也就是本地的 tomcat 服务器之后,后面的 /hi 就表示访问 /h1 路由映射的方法。

那么这个 @RequestMapping 中的 / 可以省略吗?其实是可以的,当你省略了这个 / 的时候,Spring 会为我们自动加上 / ,所以还是建议大家加上这个 /。

package com.example.springmvcdemo2;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloController {@RequestMapping("hi")public String hi() {return "你好,Spring MVC";}}

@RequestMapping 是 Spring MVC 项目中最常用到的注解之一,下面我们来详细学习一下 @RequestMapping 的使用。

3.1 @RequestMapping 使用

@RequestMapping 注解不仅可以使用在方法上,还可以使用在类上。

package com.example.springmvcdemo2;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("sayHi")public class HelloController {@RequestMapping("hi")public String hi() {return "你好,Spring MVC";}}

当 @RequestMapping 修饰了类之后,那么访问的地址就是 类路径 + 方法路径了。

这里访问的时候,没有加上类路径,就无法成功访问。

3.2 @RequestMapping 能接受 GET 方法还是 POST 方法

为了解决这个问题,我们需要使用 postman 来分别构造出 GET 请求和 POST 请求。



通过 postman 构造出 GET 请求和 POST 请求可以看出来,这个 @RequestMapping 默认是支持 GET 方法和 POST 方法的,那么是否有方法可以是它只能接收 GET 或者 POST 请求呢?答案是可以的。我们可以看看 @ReqeustMapping 的源码。

  1. @Target({ElementType.TYPE, ElementType.METHOD}): 这个注解可以用于类级别或方法级别。
  2. @Retention(RetentionPolicy.RUNTIME): 这个注解在运行时有效,即运行时的代码可以访问这个注解的信息。
  3. @Documented: 这是一个标准的JavaDoc注释,用于生成API文档。
  4. @Mapping: 这个注解通常与Spring的@Configuration类一起使用,用于映射处理方法到URL路径。
  5. @Reflective({ControllerMappingReflectiveProcessor.class}): 这个注解指示Spring在运行时解析和处理带有此注解的类和方法。

注解中的各个元素:

  1. name(): 返回一个字符串,表示这个映射的名称。如果没有指定,默认为空字符串。
  2. value(): 返回一个字符串数组,表示需要映射的URL路径。如果没有指定,默认为空数组。这个路径可以包含占位符,例如/users/{id}。
  3. path(): 另一个用于指定URL路径的数组。这个数组的值和value()返回的值是一样的,它们是互为别名的。
  4. method(): 返回一个RequestMethod数组,表示这个映射接受的HTTP方法类型。如果没有指定,默认为空数组,表示接受所有方法类型。
  5. params(): 返回一个字符串数组,表示请求参数。如果请求参数符合数组中的任何一个,那么这个映射就会被触发。
  6. headers(): 返回一个字符串数组,表示请求头。如果请求头符合数组中的任何一个,那么这个映射就会被触发。
  7. consumes(): 返回一个字符串数组,表示这个映射接受的媒体类型。如果没有指定,默认为空数组,表示接受所有媒体类型。
  8. produces(): 返回一个字符串数组,表示这个映射能产生的媒体类型。如果没有指定,默认为空数组,表示能产生所有媒体类型。

@RequestMapping 中 method 默认为接收所有方法的请求,如果我们想指定一种方法,就可以显式的指明请求的方法。

@RequestMapping(value = "sayHi", method = RequestMethod.GET)

当 @RequestMapping 中只有一个参数的时候,这个参数会被认为是 value,如果有多个参数,则需要显式的指明参数的 key,类似value=”sayhi“,method=RequestMethod.GET。

更改完 @RequestMapping 中的参数,只允许接收 GET 请求之后,我们再用 postman 构造 GET 请求和 POST 请求看看结果。



当构造出 POST 方法之后,就显示 405,方法不被允许。

4 请求

访问不同的路径,就是发送不同的请求。在发送请求的时候,可能会传递一些参数,我们后端就需要拿到这些传递的参数来做出相应的业务处理。

4.1 传递单个参数

在 Spring MVC 中,我们可以使用跟传递来的参数相同的名字就可以获取到这个参数,这都是 Spring MVC 底层都帮我们做好了的,其实 Spring MVC 底层为我们做了很多事情,发送来的请求在由 Controller 转发到 Mudel 的时候,一些我们可能需要用到的东西,都是 Spring MVC 底层帮我们处理过的,如果想要使用只需要拿到就可以了。

package com.example.springmvcdemo2;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/request")public class UserController {@RequestMapping("/r1")public String r1(String name) {return "name: " + name;}}

但是如果我们将参数的名字和请求的参数名字不一样的话,那么我们后端这里就不能获取到这个参数。

package com.example.springmvcdemo2;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/request")public class UserController {@RequestMapping("/r1")public String r1(String username) {return "name: " + username;}}

4.2 传递多个参数
@RequestMapping("/r2")public String r2(String name, int age) {return "name: " + name + " age: " + age; }

可以发现,请求中参数的顺序和我们代码获取参数的顺序可以不用保持一致。

如果我们这里的 age 没有传递参数的话,会发生什么呢?


当 int 类型的 age 参数为传递的话,这里会报出一个 500 的错误,也就是我们的服务器,Java代码出现了错误,这时就需要我们去看看代码日志,看看出现了什么问题。


通过前面观察代码可以知道,如果参数未传递的话,会默认返回 null,但是由于我们接收参数使用的是 int 类型来接收这个参数的,int 类型不能够转换 null,所以就会报错,避免这个问题的方法就是使用 int 类型的包装类 Integer,这样就能够处理返回的 null。

@RequestMapping("/r2")public String r2(String name, Integer age) {return "name: " + name + " age: " + age;}


通过上面的一个例子,我们得出一个结论:在接收参数的时候,尽量使用基本数据类型的包装类。

还有一个问题,就是如果我们后端觉得这个参数是 Integer 类型,所以就用 Integer 来接收,但是请求中该参数传递的却是 String 类型,那么这会发生什么问题呢?来看看。


后端获取参数的数据类型需要和请求的参数数据类型之间能够转换,否则就会出现问题,这就需要前端和后端协商好。

4.3 传递对象

如果传递的参数很多的时候,那么方法声明就需要很多的的形参,在后续增加参数的时候,也需要更改方法中的形参 ,所以这样就会显得很麻烦,所以我们就可以把这些参数封装为一个对象,可以在这个对象中一次性把所有可能会用到的参数都给写进去,如果请求传递的参数没有的话,就会被设置为默认值,就算后续需要添加参数的话,也是在我们封装的这个对象中更改。当Spring MVC 接收的参数是对象类型的话,那么Spring MVC 就会根据对象中的属性,然后在请求中找到对应的值,并且将这些属性给赋值,这样就极大的方便了我们程序员的工作。

封装一个 UserInfo 对象。

package com.example.springmvcdemo2;public class UserInfo {private String name;private Integer age;private Integer id;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}@Overridepublic String toString() {return "UserInfo{" +"name='" + name + '\'' +", age=" + age +",token operator">+ id +'}';}}
@RequestMapping("/r3")public String r3(UserInfo user) {return user.toString();}


4.4 参数重命名

虽然在做项目之前,前端和后端肯定协商了请求中参数的名称,但是我后端程序员写的写的感觉这个名字不好,我想换一个可以吗?答案是可以的,这里需要使用到 @RequestParam 注解来实现参数的重命名。

@RequestMapping("/r4")public String r4(@RequestParam("name") String username, @RequestParam("age") Integer userage) {return "username: " + username + " userage: " + userage;}


如果我们加了这个注解之后,但是请求中没有这个参数的时候就会出现问题了。


这是因为该注解默认该参数是必须要传递的,所以就需要设置该默认值为非必传。

@RequestMapping("/r4")public String r4(@RequestParam("name") String username, @RequestParam(value = "age", required = false) Integer userage) {return "username: " + username + " userage: " + userage;}

4.5 传递数组

Spring MVC 可以自动绑定数组参数的赋值。也就是说:Spring MVC 会自动帮我们处理请求参数是数组的情况。

@RequestMapping("/r5")public String r5(String[] arr) {return Arrays.toString(arr);}


4.6 传递集合

和传递数组类似,当请求中的参数有多个相同的名称时,也可以看作是集合,只不过如果将他看作是集合的话,需要使用 @RequestParam 绑定参数关系。因为在默认情况下,请求中有多个参数名称相同的话,Spring MVC 会将它们封装为数组,要想封装为集合的话就需要使用 @RequestParam 来绑定参数关系。

@RequestMapping("/r6")public String r6(@RequestParam("list") List<String> list) {return "size: " + list.size() + " list: " + list;}

4.6 传递JSON数据

JSON数据格式因为其易于阅读和编写、跨平台、跨语言、、轻量级等优势称为网络传输中最常使用的一种数据格式,那么在 Spring MVC 中如何接收到请求中传递来的 JSON 数据格式呢?

因为 JSON 数据格式是键值对的形式,所以我们将需要接收的参数封装到 Java 对象中,然后 Spring MVC 参数中使用 @RequestBody 来修饰这个参数,这样当 Spring MVC 接收到 JSON 数据的时候,就会根据 Java 对象中的属性然后在这些 JSON 数据中查找相同名称的键值对,并且将值赋值给 Java 对象属性。

@RequestMapping("/r7")public String r7(@RequestBody UserInfo user) {return user.toString();}

fiddler 抓包结果。

4.7 获取URL中参数

通过使用 @PathVariable ,可以实现 URL 路径上的数据绑定。在路径上使用 {} 来表示要绑定的数据。

@RequestMapping("/r8/{name}/{age}")public String r8(@PathVariable String name, @PathVariable Integer age) {return "name: " + name + " age: " + age;}


Spring MVC 接收绑定数据的参数的时候,需要保证和请求中传递的参数顺序保持一致,否则就可能会出现问题。


被 @PathVariable 修饰的参数也是必传的,如果想要设置为非必传的话,不仅需要更改 @PathVariable 的默认值,还需要在路径上做出调整。

我们先是只更改 @PathVariable 的默认设置看看能不能成功。

@RequestMapping("/r8/{name}/{age}")public String r8(@PathVariable(value = "name", required = false) String name, @PathVariable Integer age) {return "name: " + name + " age: " + age;}


显然不能成功,所以就还需要在路径上做出修改。

@RequestMapping({"/r8/{name}/{age}", "r8/{age}"})public String r8(@PathVariable(value = "name", required = false) String name, @PathVariable Integer age) {return "name: " + name + " age: " + age;}

4.8 上传文件

Spring MVC 同样可以接收到请求传来的文件,使用 @RequestPart 修饰即可接收文件。

@RequestMapping("/r9")public String r9(@RequestPart MultipartFile file) throws IOException {String fileName = file.getOriginalFilename();file.transferTo(new File("D:/tmp/" + fileName));return "接收到的文件的名称为" + fileName;}

在 Spring MVC 中表示文件这种类型需要使用到 MultipartFile,transferTo()方法是将文件复制到指定的目录下。

postman 这样传递文件。



4.9 获取cookie 和 session

获取cookie

前面我已经为大家介绍了关于 cookie 和 session 相关的知识,如果大家忘记了的话可以去看看,cookie 和 session。

由于 cookie 是由浏览器在发送请求的时候带上的,所以我们就需要得到 HttpServletRequest 类,然后通过这个类来获取到 cookie。

@RequestMapping("/r10")public String r10(HttpServletRequest request) {Cookie[] cookies = request.getCookies();StringBuilder sb = new StringBuilder();for (Cookie cookie : cookies) {sb.append(cookie.getName());sb.append("=");sb.append(cookie.getValue());sb.append("
"
);}return sb.toString();}

写完 Spring MVC 代码之后,我们就需要在 postman 中构造 cookie 了。


点击 add domain 之后,就会出现下面的页面,然后我们点击 add cookie,添加cookie。


这里 username 就表示的是这个 cookie 的 name,而 zhangsan 则表示该 cookie 的 value,Path 定义了Web站点上可以访问该 cookie 的目录,这里 / 表示所有目录。


构造完成之后,在 postman 的这个部分就会显式我们设置好的cookie。


以上是一种获取 cookie 的方式,Spring MVC 还为我们提供了更方便的获取 cookie 的方式。

@RequestMapping("/r11")public String r11(@CookieValue("username") String username) {return "username=" + username;}


但是这个方式一次只能获取到一个 cookie,如果我们需要获取到多个 cookie,就可以使用第一种获取 cookie 的方式。

获取session

咱们的服务器要想获取到 session,就需要依靠请求中 cookie 中携带的 sessionId,然后在服务器这里服务器根据这个获取到的 sessionId,拿到与这个浏览器的 session,所以我们还是需要获取到 HttpServletRequest 对象。

在获取到 session 之前呢,还需要我们手动设置出一个 session,然后再设置一些属性。

@RequestMapping("/setsession")public String setSession(HttpServletRequest request) {HttpSession session = request.getSession(true);session.setAttribute("username", "zhangsan");return "设置session完成";}


当设置完成session之后,我们查看cookie,可以发现,cookie中自动添加进去了一个cookie,这个cookie就记录了sessionId。

设置完成 session 之后,我们就可以获取到这个 session 了。

@RequestMapping("/getsession")public String getSession(HttpServletRequest request) {HttpSession session = request.getSession(false);if (session != null) {return "username: " + (String)session.getAttribute("username");}return "session为null";}

Spring MVC 提供了几个简单的获取 session 的方式。

@RequestMapping("/getsession1")public String getSession1(HttpSession session) {return "username: " + (String)session.getAttribute("username");}


不仅如此,如果该浏览器和服务器是第一次建立连接,不存在 session ,也就是session为null的时候,session.getAtttribute() 不会出现空指针一样,这是因为 Spring MVC 底层帮我们解决了这个问题。

我们把这个sessionId cookie给删除了,然后给服务器发送请求。

另一种 Spring MVC 提供的方式更加简单,可以直接获取到 session 中的属性值。

@RequestMapping("/getsession2")public String getSession2(@SessionAttribute("username") String username) {return "username: " + username;}

4.10 获取 header

首先我们还是使用传统的方式获取到 header:通过 HttpServletRequest 类来获取到我们需要的header。

@RequestMapping("/getheader")public String getHeader(HttpServletRequest request) {String ua = request.getHeader("User-Agent");return "获取到User-Agent:" + ua;}



使用 Spring MVC 提供的 @RequestHeader 注解来获取到 header。

@RequestMapping("/getheader2")public String getHeader1(@RequestHeader("User-Agent") String ua) {return "获取到User-Agent:" + ua;}

5. 响应

前面我们 Spring MVC 返回的响应都是数据类型,其实还可以返回一个静态页面、设置header、设置状态码等操作。

5.1 返回静态页面

我们在这里写一个静态页面。

<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body>hello, spring mvc,这里是html页面</body></html>
package com.example.springmvcdemo2;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;import java.util.Map;@RequestMapping("/response")@RestControllerpublic class ResponseController {@RequestMapping("/index")public Object index() {return "/index.html";}}



通过 postman 构造请求之后用 fiddler 抓包之后可以看到返回的是 text 类型的数据,而不是 html,也就是说我们的 Spring MVC 将“/index.htnl”当成了一个数据。那么这是为什么呢?

这是 @RestController 作用的结果,这个注解会默认将返回的都看作是数据,要想将返回的类型解析成 html 的话,就需要更换这个 @RestController 注解为 @Controller

package com.example.springmvcdemo2;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;import java.util.Map;@RequestMapping("/response")@Controllerpublic class ResponseController {@RequestMapping("/index")public Object index() {return "/index.html";}}

那么这个 @RestController 注解和 @Controller 注解有什么区别呢?我们来看看 @RestController 注解的源码。



通过观察 @RestController 的源码我们可以发现,@RestController 注解可以写为 @Controller + @ResponseBody

  • @Controller:定义一个控制器,Spring 框架启动时加载,把这个对象交给 Spring 管理
  • @ResponseBody:定义返回的数据格式为非视图,返回一个 text/html 信息
5.2 返回数据

返回静态页面,我们使用 @Controller 注解,那么返回数据的话,我们只需要在 @Controller 注解的基础上加上 @ResponseBody 注解就可以了。所以我们的代码其实这样写更好,这样就可以使得一个类中的方法既可以返回视图也可以返回数据。

@RequestMapping("/response")@Controllerpublic class ResponseController {@RequestMapping("/getstatus")@ResponseBodypublic String getStatus() {return "这返回的是一个数据";} }


5.3 返回HTML代码片段

在我们 Spring MVC 中返回的数据中如果存在HTML代码的时候,浏览器也会解析出这段HTML代码。

@RequestMapping("/returnhtml")@ResponseBodypublic String returnHtml() {return "

Hello,HTML~

"
;}

通过 Fiddler 抓取 postman 发送的请求,我们可以发现,响应的数据类型是 text/plain 纯文本类型,而不是 html 类型。


而我们使用浏览器访问的话,使用 Fiddler 抓包可以观察到响应的数据的类型是 html 类型。


这是为什么呢?其实这个取决于发送方能接收的数据类型,抓取 postman 请求的数据包,可以发现 header 中的 accept 为 /,也就是可以接收所有类型的数据。

并且我们的 Spring MVC 代码中的返回类型写的是 String,也就是文本数据类型,所以使用 postman 发送请求的话,响应的数据类型就是 text/plain 类型。

再观察浏览器发送请求的数据包可以发现,accept 不是 /,而是指定了一些类型。

并且浏览器能接收的数据类型中没有 text/plain,所以浏览器就会对返回的数据进行解析,看返回的数据可以解析成能接收的数据类型中的哪一种,因为我们返回的字符串是一个 html 片段,所以就会被解析为 html 类型。

样,我们还可以使用同样的方法返回CSS、JavaScript、json等类型。

5.4 返回JSON

Spring MVC 也可以返回 JSON 数据。

@RequestMapping("/returnjson")@ResponseBodypublic Map<String, String> returnJson() {Map<String, String> map = new HashMap<>();map.put("java", "java value");map.put("python", "python value");return map;}



这里为什么返回的类型是 Map 类型,最后我们抓包抓取到的响应的数据类型还是 JSON 呢?

通过 Fiddler 抓取到的请求数据包中的 Accept 可以知道,当前接收任何类型的响应,也就是说,发送端没有明确的指出接收响应的数据类型,因为我们代码返回的数据类型是 Map,是一个对象,那么我们的 Spring MVC 注解@ResponseBody 会默认使用 Jackson 库对返回的数据类型进行序列化/反序列化,所以就会将我们返回的 Map 类型转换为 JSON 数据类型,而接收端能接收到任何类型的响应,所以接收到的数据类型就是 JSON 数据类型。

5.5 设置状态码

Spring MVC 设置状态码需要使用到 HttpServletResponse 类中的 setStatus() 方法。

@RequestMapping("/returnstatus")@ResponseBodypublic String returnStatus(HttpServletResponse response) {response.setStatus(401);return "设置状态码成功";}

5.6 设置header

Spring MVC 设置状态码需要使用到 HttpServletResponse 类中的 setHeader() 方法。

@RequestMapping("/setheader")@ResponseBodypublic String setHeader(HttpServletResponse response) {response.setHeader("myheader", "666");return "设置header成功";}


5.7 设置响应的Content-Type

Spring MVC 会根据我们返回的数据或者视图自动为我们设置相应的数据类型,当然我们也可以自己设置相应的数据类型。在 Spring MVC 中,设置响应中的数据类型需要更改 @ReqeustMapping 注解中的 produces 参数的值。

  • name():指定注解的名称,默认值为空字符串。
  • value() 和 path():这两个属性是互为别名的,它们指定了请求映射的路径。如果未指定,默认为空数组。
  • method():这个属性指定了请求方法类型,例如 GET、POST、PUT 等。默认为空数组。
  • params():这个属性指定了请求参数,默认为空数组。
  • headers():这个属性指定了请求头信息,默认为空数组。
  • consumes():这个属性指定了请求内容类型,默认为空数组。
  • produces():这个属性指定了响应内容类型,默认为空数组。

我们返回一个由键值对构成的字符串。

@RequestMapping(value = "/returnjson2", produces = "application/json; charset=utf8")@ResponseBodypublic String returnJson2() {return "{\"sucess\":true}";}


如果我们不设置这个 produces 参数的,这个响应的字符串将会解析为文本类型。


Spring MVC 其实会根据你返回值的类型来决定是以什么类型进行响应的,但是对于哪些可以是文本类型又可以被解析为HTML、JavaScrip、CSS……等多种类型的时候就可以通过设置 produces 参数来指定响应的数据类型。