目录
2.5 响应
2.5.1 返回静态页面
@RestController 和 @Controller 的关联及区别
2.5.2 返回数据@ResponseBody
小结
2.5.3 返回 HTML 代码片段
2.5.4 返回 JSON
2.5.5 设置状态码
2.5.6 设置 Header(了解)
设置 Content-Type(课件内容)
设置其他 Header(课件内容)
书接上文:Java EE 进阶:4.Spring Web MVC 入门(第二弹)~~
2.5 响应
在我们前面的代码例子中,都已经设置了响应数据, Http响应结果可以是数据, 也可以是静态页面,也可以针对响应设置状态码, Header信息等.
2.5.1 返回静态页面
创建前端页面 index.html(注意路径)

html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>我是Index</h1>
</body>
</html>
比如说用户登录之后,我想给用户返回这个静态页面👇
后台代码:
package com.bite.spring.demo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/resp")
@RestController
public class RespController {
@RequestMapping("r1")
public String returnPage(){
return "index.html";
}
}
运行结果:http://127.0.0.1:8080/resp/r1
结果却发现, 页面未正确返回, http响应把"index.html" 当做了http响应正文的数据 那Spring MVC如何才能识别出来 index.html 是一个静态页面, 并进行返回呢? 我们需要把 @RestController 改为 @Controller,再来尝试一下
package com.bite.spring.demo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/resp")
@Controller
public class RespController {
@RequestMapping("r1")
public String returnPage(){
return "index.html";
}
}
再次运行: http://127.0.0.1:8080/resp/r1

原因是没有加 /,下面是正确代码👇
package com.bite.spring.demo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/resp")
@Controller
public class RespController {
@RequestMapping("r1")
public String returnPage(){
return "/index.html";
}
}

发现页面正确展示了
闲聊:啥时候加 / ,啥时候不加 / 呢??
个人理解,就是前端不加,后端加,当然这只是个人感觉,不是很权威,一切以运行结果为准~~
如果我们把它再加一层路径的话,后端代码也必须相应加一层👇

后端代码👇
@RequestMapping("/resp")
@Controller
public class RespController {
@RequestMapping("r1")
public String returnPage(){
return "/aa/index.html";
}
}

所以前端路径是从 static 下面往下数,有几层就写几层~~
@RestController 和 @Controller 的关联及区别
当时我们说 @RestController 是帮助我们快速扫描代码,因为代码太多了,下面都是我们的代码,只是不用我们写,直接拿人家写好了的用👇

这其中任何一个出现问题都会直接影响我们的使用,但是全部扫描一遍又太耗时间了,所以我们用 @RestController 快速定位到需要扫描的代码下面
注意:SpringBoot 默认扫描路径为 启动类所在的路径及其子目录
如果我们新建一层路径 aa,将启动类放进去,我们会发现我们之前的所有代码在启动服务后都运行不了了👇
浏览器结果👇
Postman 结果👇
下面我们再把 启动类改回来,将其他代码放进去看看👇

浏览器结果👇
Postman 结果👇

其他代码放在 启动类所在路径的子目录下依然能够被访问到,也验证了我们的结论~~
那 @Controller 又是啥??
先给结论:@RestController = @ResponseBody + @Controller,这是一个复合注解
下面我们来看一下注解 @RestController 的构成👇

①Target:这个注解可以放在哪里,上述 @@RestController 就只能放在类上,放在方法上就会报错👇

这个属性就决定了有的可以放在类上,有的可以放在方法上,也有的可以放在参数上,而 @RequestMapping 既可以放在类上又可以放在方法上就是因为👇

而 @RequestParam 可以放在参数上也是因为这个👇

②Retention:注解的生命周期
我们都知道,Java 代码运行有几个阶段,第一个是源码阶段,就是我们现在的阶段👇

我们启动的时候就要进行编译,编译之后才是运行,只不过我们通过 IDEA 就看不到编译的过程了,其实下面的 classes 就是编译的结果👇

注解的生命周期总共有三个,源码→class→runtime(运行时期)👇

如果只是标识 Source,代表你只能从源码阶段看到它,在 class 阶段你就看不到它了,它就消失了,Class 时期 和 runtime 时期也类似~~
③Documented:文档,这个我们不用了解,知道就行了
所以 @RestController 和 @Controller 就只是差在 @ResponseBody 上了👇

@ResponseBody:返回数据
闲聊:@ResponseBody 这就与我们的历史有关系了~~
早期的时候“前后端不分家”
咱们前面讲了MVC模式, 后端会返回视图, 这是早期时的概念
像前面这行代码,其实就是返回的视图👇
return "/aa/index.html";
随着互联网的发展, 目前项目开发流行"前后端分离"模式, Java主要是用来做后端项目的开发, 所以也就不再处理前端相关的内容了 MVC的概念也逐渐发生了变化, View不再返回视图, 而是返回显示视图时需要的数据. 所以前面使用的@RestController 其实是返回的数据. @Controller : 定义一个控制器, Spring 框架启动时加载, 把这个对象交给Spring管理. @ResponseBody : 定义返回的数据格式为非视图, 返回一个 text/html 信息
如果想返回视图的话, 只需要把@ResponseBody 去掉就可以了, 也就是@Controller
在早期的时候,我们后端只是返回页面(视图),但是现在的话,我们就直接返回数据了,跟页面没关系了,前端的页面前端爱咋整咋整~~
2.5.2 返回数据@ResponseBody
我们上面讲到, @ResponseBody 表示返回数据.
如果你想返回页面的话,就得使用 @Controller,那么我现在又想返回数据又想返回页面,返回数据就要用到 @RestController,于是代码就变成了这样👇
package com.bite.spring.demo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/resp")
//@RestController
@Controller
public class RespController {
/**
* 返回页面
* @return
*/
@RequestMapping("r1")
public String returnPage(){
return "/aa/index.html";
}
/**
* 返回数据
* @return
*/
@RequestMapping("r2")
public String returnData(){
return "我是前端需要的数据";
}
}
此时我们依次尝试一下
r1:没有问题👇
r2:404👇
为啥会报 404??因为它认为这是一个页面,它去找这个页面,但是没找到,所以报错了
接下来咋办??一个要 @RestController,一个要 @Controller,两者相减就等于 @ResponseBody,加上它,表示该方法返回的是一个数据,所以我们的代码是👇
package com.bite.spring.demo;
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;
@RequestMapping("/resp")
//@RestController
@Controller
public class RespController {
/**
* 返回页面
* @return
*/
@RequestMapping("r1")
public String returnPage(){
return "/aa/index.html";
}
/**
* 返回数据
* @return
*/
@ResponseBody
@RequestMapping("r2")
public String returnData(){
return "我是前端需要的数据";
}
}
接下来再观察一下:
r1:没问题👇
r1:没问题👇
小结
①如果一个类既返回页面,又返回数据:类上使用 @Controller,在返回数据的方法上加 @ResponseBody👇
@Controller
public class RespController {
/**
* 返回页面
* @return
*/
@RequestMapping("r1")
public String returnPage(){
return "/aa/index.html";
}
/**
* 返回数据
* @return
*/
@ResponseBody
@RequestMapping("r2")
public String returnData(){
return "我是前端需要的数据";
}
}
②如果一个类全部返回数据:可以使用 @RestController 或者 @ResponseBody + @Controller👇
或者👇
@ResponseBody 既是类注解, 又是方法注解 如果作用在类上:表示该类的所有方法, 返回的都是数据,
如果作用在方法上:表示该方法返回的是数据. 也就是说: 在类上添加@ResponseBody 就相当于在所有的方法上添加了@ResponseBody 注解. 同样, 如果类上有@RestController 注解时:表示所有的方法上添加了@ResponseBody 注 解, 也就是当前类下所有的方法返回值做为响应数据
③如果一个类全部返回页面:使用 @Controller
答疑
Q1:可以一次发起两个请求不??
指的是 http 请求嘛??这是不可以的,因为这是客户端,就像你一个人多开两个网页的话,那就不叫一次了,如果说同时发起两个请求的话,可以两个用户一起发送,或者借助一些工具~~其实说白了就是多创建了几个线程而已,后面学习测试的时候,会学到用一些工具同时发送多个请求~~
Q2:全部返回数据,只用 @ResponseBody 可以吗??
不可以,需要加上 @Controller,这个是必须要加的
2.5.3 返回 HTML 代码片段
如果前端的一些图标是由后端来返的,那么后端就需要给返回一个 HTML 片段,但是也非常非常少啊,因为后端现在不做前端了,但是还是给大家讲一讲,了解一下而已
后端返回数据时, 如果数据中有HTML代码, 也会被浏览器解析
@ResponseBody
@RequestMapping("r3")
public String returnHTML(){
return "<h1>我是一级标题</h1>";
}
运行程序, 浏览器响应结果如下:http://127.0.0.1:8080/resp/r3 
通过Fiddler观察响应结果, Content-Type 为text/html
Content-Type 是 text/html 类型时会自动解析我们返回数据的 HTML 片段,就会把它作为 HTML 去解析的,如果你想返回片段的话,默认情况下就可以满足
那如果不想让它去解析,我这就是一个正儿八经的数据,我的内容就是 h1,咋让它去返回??
由于上面说了,解析的原因就是 Content-Type 是 text/html 类型,那么我们把 text/html 改一下就可以了👇
@ResponseBody
@RequestMapping(value = "r4",produces = "text/plain")
public String returnText(){
return "<h1>我是一级标题</h1>";
}
这次结果就变成一个数据了👇
差异就在这👇
响应中的 Content-Type 常见取值有以下几种: • text/html : body 数据格式是 HTML
• text/plain : body 数据格式是 纯文本格式 • text/css : body 数据格式是 CSS • application/javascript : body 数据格式是 JavaScript • application/json : body 数据格式是 JSON
• multipart/form-data:需要在表单中进行文件上传时,就需要使用该格式 如果请求的是js文件, Spring MVC会自动设置Content-Type为application/javascript 如果请求的是css文件, Spring MVC会自动设置Content-Type为text/css
闲聊:学过 Servlet 的都知道,Servlet 就非常麻烦,需要自己去设置它的 Content-Type,还有编码都需要设置,但是 我们的 Spring 的这些都不用设置,默认情况下都给我们设置好了,但是特殊情况下比如乱码,就需要自己再设置一下了
2.5.4 返回 JSON
Spring MVC 也可以返回JSON 后端方法返回结果为对象
@ResponseBody
@RequestMapping(value = "/r5")
public UserInfo returnJson(){
UserInfo userInfo=new UserInfo("CoderYanger",21,1);
return userInfo;
}
运行程序, 浏览器响应结果如下: http://127.0.0.1:8080/resp/r5
通过Fiddler观察响应结果, Content-Type 为 application/json
可能在我们后端看起来都是一样的,但是对于前端就是不同的,如果是 JSON,那么前端就可以直接转成对象处理,当对象来使用,如果是文本的话,那前端就需要再处理一下才能把这个字符串转成对象
如果用 Postman 的话就直接转成 JSON 了👇
答疑:这代码返回的不是一个对象吗??为啥是一个 JSON??
我们的 HTTP 没办法设置对象,我们来简单回顾一下 HTTP 的 Content-Type 类型👇
• text/html : body 数据格式是 HTML
• text/plain : body 数据格式是 纯文本格式 • text/css : body 数据格式是 CSS • application/javascript : body 数据格式是 JavaScript • application/json : body 数据格式是 JSON
• multipart/form-data:需要在表单中进行文件上传时,就需要使用该格式
我们的 Content-Type 分为两种,一个是 Request Content-Type 一个是 Response Content-Type ,一个是请求的一个是响应的,而这时我们返回的 UserInfo 一定是响应的,所以在查资料的时候一定要分清是 Request 的还是 Response 的
也就是说我们虽然返回了 UserInfo 对象,但是我们没有任何一个 Content-Type 类型可以表示对象的,我们所谓的这个对象只是 Java 的格式,包括我们返回的 List、Map,这都是我们 Java 的格式,我们的前端如何识别??对于这种对象全部返回 JSON,所以说 List、Map 还有一些更复杂的其他格式的对象,Spring 返回的都是 JSON ,这样前端就能识别为对象了~~
2.5.5 设置状态码
Spring MVC会根据我们方法的返回结果自动设置响应状态码, 程序员也可以手动指定状态码 通过Spring MVC的内置对象HttpServletResponse 提供的方法来进行设置
@ResponseBody
@RequestMapping(value = "/r6")
public UserInfo setStatus(HttpServletResponse response){
response.setStatus(404);
UserInfo userInfo=new UserInfo("CoderYanger",21,1);
return userInfo;
}
运行程序, 浏览器响应结果如下: http://127.0.0.1:8080/resp/r6
状态码不影响页面的展示,所以我们的页面并未发生变化~~ 通过Fiddler来观察设置的结果:
那这是咋回事儿??正常的 404 不应该是这样吗👇??
其实状态码和页面的展示是没有关系的~~
至于上面的那个错误页面,是 Spring 默认设置的
正如 B 站的 404 也是自己设置的👇

下面的前端代码就是这个漂亮 404 的界面代码
而这个状态码不是只是后端用,前端也要根据你这个状态码来对应做一些处理的~~
2.5.6 设置 Header(了解)
有时候前端也会根据 Header 去做一些处理~~
@ResponseBody
@RequestMapping(value = "/r7")
public String setHeader(HttpServletResponse response){
response.setHeader("myHeader","myHeader");
return "设置Header成功";
}

此时抓包就可以看到我们自己设置的 Header 了👇
当我不希望它通过正文返回时,就可以设置让其从 Header 返回,这种 Header 部分通常都是与用户相关的,比如用户登录、授权一些相关的信息~~
Http响应报头也会向客户端传递一些附加信息, 比如服务程序的名称,请求的资源已移动到新地址等, 如:Content-Type, Local等. 这些信息通过@RequestMapping 注解的属性来实现 先来看@RequestMapping 的源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
1. value: 指定映射的URL 2. produces: 响应,指定返回的内容类型,还可以同时设置返回值的字符编码,这个大家记一下就可以了~~就是我们之前的👇
以下都是请求相关的
3. method: 指定请求的method类型, 如GET, POST等 4. consumes: 指定处理请求(request)的提交内容类型(Content-Type),例如application/json, text/html;
5. Params: 指定request中必须包含某些参数值时,才让该方法处理 6. headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求 了解即可, 更多说明参考 Mapping Requests :: Spring Framework
设置 Content-Type(课件内容)
我们通过设置 produces属性的值, 设置响应的报头Content-Type
@RequestMapping(value = "/returnJson2",produces = "application/json")
@ResponseBody
public String returnJson2() {
return "{\\"success\\":true}";
}
运行程序, 浏览器响应结果如下: http://127.0.0.1:8080/returnJson2
通过Fiddler来观察设置的结果:
如果不设置produces , 方法返回结果为String时, Spring MVC默认返回类型, 是text/html. 设置返回类型时, 也可以同步设置响应编码
@RequestMapping(value = "/returnJson2",produces = "application/json;charset=utf-8")
@ResponseBody
public String returnJson2() {
return "{\\"success\\":true}";
}
观察Fiddler的响应结果
设置其他 Header(课件内容)
设置其他Header的话, 需要使用Spring MVC的内置对象HttpServletResponse 提供的方法来进行设置
@RequestMapping(value = "/setHeader")
@ResponseBody
public String setHeader(HttpServletResponse response) {
response.setHeader("MyHeader","MyHeaderValue");
return "设置Header成功";
}
void setHeader(String name, String value) 设置一个带有给定的名称和值的 header. 如果 name已经存在, 则覆盖旧的值. 运行程序, 浏览器响应结果如下: http://127.0.0.1:8080/setHeader
通过Fiddler来观察设置的结果:


