目录
[1. 返回静态页面](#1. 返回静态页面)
[1.1 Spring 默认扫描路径](#1.1 Spring 默认扫描路径)
[1.2 @RestController](#1.2 @RestController)
[1.2.1 @Controller => 返回页面](#1.2.1 @Controller => 返回页面)
[1.2.2 @ResponseBody](#1.2.2 @ResponseBody)
[2. 返回 HTML](#2. 返回 HTML)
[2.1 @RequestMapping](#2.1 @RequestMapping)
[2.1.1 produces(修改响应的 Content-Type)](#2.1.1 produces(修改响应的 Content-Type))
[2.1.2 其他属性](#2.1.2 其他属性)
[3. 返回 JSON](#3. 返回 JSON)
[4. 设置状态码](#4. 设置状态码)
[4.1 HttpServletResponse](#4.1 HttpServletResponse)
[5. 设置 Header](#5. 设置 Header)
引言
前面的几篇博文中, 介绍的都是和请求相关的内容, 通过 Spring 的方法来接收请求中相关信息.
本篇博客介绍响应相关的知识, 通过 Spring 来设置响应中的值, 向客户端返回设置好的响应.
1. 返回静态页面
我们首先使用 html 写一个前端页面, 再通过 Spring 方法, 将这个 html 页面作为响应的内容, 展示给用户.

显然, 方法返回的只是一个字符串, 并非 html 展示的页面.
如何将 html 页面返回给客户端呢?? 这需要修改 类注解.
若方法返回的是一个页面, 则需要将原来的类注解 @RestController 修改为 @Controller.
接下来, 对涉及到的几个注解逐个进行解释.
1.1 Spring 默认扫描路径
我们知道, 每一个路径, 都对应了一个方法. 当服务器收到客户端的请求时, Spring 就会根据请求中的资源路径, 寻找该路径对应的方法, 进而计算响应结果, 最后返回响应.
而 Spring 中有大量的方法存在, 包括使用 Maven 引入的第三方库, 都存在于 Spring 中. 若 Spring 要扫描所有的方法, 一个一个的去进行比对, 确定哪个是资源路径所对应的方法的话, 那工作量是巨大的.
因此, Spring 有一个默认扫描路径: 只扫描启动类所在的目录, 及其子目录.
若请求中资源路径对应的方法, 属于启动类所在目录或子目录时, 会被 Spring 扫描到:

而当请求中资源路径所对应的方法, 不属于启动类所在目录或子目录时, 则 Spring 不会扫描, 客户端就会显示错误:

因此, Spring 也当然不会扫描 Maven 中第三方库中的方法.
有了默认的扫描路径, 大大降低了 Spring 的工作量, 提升了开发效率.
1.2 @RestController
虽然 Spring 有默认的扫描路径, 但是当项目很大时, 这个路径下也是会有很多的方法存在的.
于是, 可以使用 @RestController 对类进行标记, Spring 只需扫描被 @RestController 标记的类.
因此, Spring 的扫描对象为: 默认扫描路径中的类 && 使用 @RestController 进行标记的类
当默认路径下的类没有使用 @RestController 进行标记时, 那么 Spring 也不会进行扫描, 进一步减少了 Spring 的工作量.
@RestController 的特性如下:
- 类注解. 只能对类进行使用
- 存活于运行阶段. (整个项目运行时, 都存在)
- 包含 @Controller 和 @ResponseBody

其中, @Controller 表示返回的是页面. @ResponseBody 表示返回的是数据.
@RestController 也表示返回的是数据.
由于 @RestController = @Controller + @ResponseBody, 因此, 左右两侧的注解效果相同:

1.2.1 @Controller => 返回页面
@Controller 是一个类注解(只能给类使用), 并且使用此注解标记类时, 表示该类中的所有方法(@ResponseBody 标记的方法除外, 见下文), 返回的均是页面.

因此, 我们将类的注释修改为 @Controller, 就可以返回静态页面了:
Spring 会以 resources.static 为该 html 文件的基准路径, 扫描指定路径下的 html 文件, 并返回该 html 构造的页面. 并且, html 文件的路径前要加上 /

不过, 现在后端已经基本不会返回页面了.
java
@RequestMapping("/resp")
@Controller // 表示类中的方法只能返回页面
public class RespController {
@ResponseBody // 该方法可以返回数据
@RequestMapping("/r1")
public String returnPage() {
// 返回页面时, 一定要加上 /
return "/index.html";
}
}
1.2.2 @ResponseBody
@ResponseBody 既是一个类注解, 也是一个方法注解, 表示返回的是数据.

当类注解为 @Controller 时, 类中的方法只能返回页面, 若此时我们想返回数据, 可以对方法使用 @ResponseBody 进行标记, 被标记的方法可以返回数据.

综上, @RestController, @Controller, @ResponseBody 的使用场景如下:
- 一个类中, 既有返回页面的方法, 也有返回数据的方法 ==> 使用 @Controller 对类标记, 对返回数据的方法使用 @ResponseBody 标记.
- 如果一个类中所有的方法, 返回的都是数据 ==> 使用 @RestController 或者 @Controller + @ResponseBody 对类标记
- 如果一个类中所有的方法, 返回的都是页面 ==> 使用 @Controller 对类标记
- 类必须有 @Controller 注解才能被 Spring 扫描到(@RestController 包含了 @Controller)
2. 返回 HTML
默认情况下, 响应中的 Content-Type 都是 test/html, 因此, 浏览器收到数据后, 都会按照 html 的格式来解析, 最终展示在页面上.
因此, 如果我们想要给客户端返回一个 HTML 片段, 那我们无需任何操作, 直接返回 HTML 代码即可.

java
// 返回 HTML 片段
@ResponseBody
@RequestMapping("/r3")
public String returnHTML() {
return "<h1>这是一个一级标题</h1>";
}
2.1 @RequestMapping
@RequestMapping 是一个类注解, 也是一个方法注解, 既可以对类设置路径, 也可以对方法设置路径.

此外, @RequestMapping 还有一些属性.
2.1.1 produces(修改响应的 Content-Type)
但是, 如果我们想要把这个 HTML 代码, 当做一个普通的字符串去返回, 该怎么办呢?
显然, 我们将响应的 Content-Type 类型修改为纯文本格式即可, 即: test/plain
我们可以通过修改 @RequestMapping 中的 produces 属性, 来修改 Content-Type:

这样, 浏览器接收响应后, 就会将 Body 中的内容, 以纯文本的格式来解析, 而不是当做 HTML .
java
// 返回纯文本
@ResponseBody
@RequestMapping(value = "/r4", produces = "text/plain") // 修改 Content-Type 为纯文本格式
public String returnText() {
// 当成普通字符串返回
return "<h1>这是一个一级标题</h1>";
}
2.1.2 其他属性
@RequestMapping 中不仅有 produces 这一属性, 还有其他属性. 通过设置这些属性值, 可以对请求或者响应中的内容做出要求:
- method: 要求请求的方法必须是什么(如:GET, POST, ....), 否则不处理
- params: 要求请求中必须包含哪些参数, 否则不处理
- headers: 要求请求的 header 中必须包含哪些 header 信息, 否则不处理
- consumes: 要求请求的 Content-Type 必须是什么, 否则不处理
- produces: 设置响应的 Content-Type

3. 返回 JSON
Spring 会根据返回数据的类型, 自动对 Content-Type 进行类型的转换.
上文提到, 当返回的数据是字符串时, 默认的 Content-Type 是 text/html; 若想改成其他类型, 还需要手动进行转换.
但是当我们返回的数据是一个对象时(自定义类对象, List 对象, Map 对象, ....), Spring 都会自动将这个对象序列化为 JSON 格式, 并自动将 Content-Type 转换为 application/json:

java
// 返回 JSON
@ResponseBody
@RequestMapping("/r5")
public UserInfo returnJson() {
// 若返回的数据是一个对象
// Spring 会自动将响应中的 Content-Type 转换为 JSON 格式
return new UserInfo("dings", "aaa", 1);
}
4. 设置状态码
4.1 HttpServletResponse
在后端的 Spring 项目中, 我们既可以通过 HttpServletRequest 来获取请求中的信息, 同样也可以通过 HttpServletResponse 来设置响应中的信息.
因此, 我们可以拿到 HttpServletResponse 对象, 进而通过 setStatus 方法修改响应中的状态码:

java
// 设置状态码
@ResponseBody
@RequestMapping("/r6")
public String setStatus(HttpServletResponse response) {
// 手动将响应中的状态码设置为 404
response.setStatus(404);
return "设置状态码成功!!";
}
注意: 状态码不影响页面的展示!!
不管状态码是什么, 都可以返回我们自定义的内容. 至于为什么有些网站的 404 页面是一些提示内容, 因为那个界面也是维护那个网站的程序员自己代码写的.
程序员当然也可以将 404 页面打扮的很漂亮!! 比如 bilibili:

5. 设置 Header
后端可以设置响应中的 header, 通过 header 来向客户端传递一些信息.
仍然是通过 HttpServletResponse 设置响应中的 header:
(header 是以键值对的形式存储数据的)

java
// 设置 header
@ResponseBody
@RequestMapping("/r7")
public String setHeader(HttpServletResponse response) {
response.setHeader("myHeader", "hahaha");
return "设置 header 成功!!";
}
END