因为之前系统学习过SpringMVC,所以这里只是做一个简要的了解,SpringMVC详细知识可以看我以前写的SpringMVC专栏。
4. SpringMVC入门
SpringMVC简介
HTTP
**超文本传输协议(HTTP)**是一个用于传输超媒体文档(例如 HTML)的应用层协议。它是为 Web 浏览器与 Web 服务器之间的通信而设计的。HTTP 遵循经典的客户端 - 服务端模型,客户端打开一个连接以发出请求,然后等待直到收到服务器端响应。
HTTP 是一种能够获取如 HTML 这样的网络资源的 protocol(通讯协议)。它是在 Web 上进行数据交换的基础,是一种 client-server 协议,也就是说,请求通常是由像浏览器这样的接受方发起的。一个完整的 Web 文档通常是由不同的子文档拼接而成的,像是文本、布局描述、图片、视频、脚本等等。
HTTP 流
当客户端想要和服务端进行信息交互时(服务端是指最终服务器,或者是一个中间代理),过程表现为下面几步:
- 打开一个 TCP 连接:TCP 连接被用来发送一条或多条请求,以及接受响应消息。客户端可能打开一条新的连接,或重用一个已经存在的连接,或者也可能开几个新的 TCP 连接连向服务端。
- 发送一个 HTTP 报文:HTTP 报文(在 HTTP/2 之前)是语义可读的。在 HTTP/2 中,这些简单的消息被封装在了帧中,这使得报文不能被直接读取,但是原理仍是相同的。
- 读取服务端返回的报文信息
- 关闭连接或者为后续请求重用连接。
SpringMVC就是基于HTTP协议的框架
thymeleaf模板简介
使用thymeleaf的时候记得在配置文件里设置将缓存关掉,因为如果不把缓存关掉的话,可能改了页面看到的还是旧的内容,没有刷新,有一个延迟。在开发的时候建议关闭缓存,但是在项目上线的时候建议开启,因为有缓存可以降低服务器的压力。
只需要在application.properties配置文件加上下面这句话就可以关闭thymeleaf缓存
spring.thymeleaf.cache=false
写一个SpringMVC的小demo
前提小知识
# 为什么我们写的方法不需要返回值并且不需要@ResponseBody注解了
之前我们写的方法都有返回值,并且加有@ResponseBody注解,是因为之前我们没有使用response对象,是将返回值
转换成json数据呈现在了页面上,但是现在我们可以通过 response 对象直接向浏览器输出任何数据,就不需要依赖
返回值了,也就不需要@ResponseBody注解将返回值转换成json数据了。
# 如何获取浏览器的请求对象以及获得controller的响应对象
只需要在方法上声明请求对象和响应对象即可。
声明了这两个对象之后,DispatcherServlet在调这个方法的时候就会自动的把请求、响应对象传给
方法的参数。request和response是接口,多层接口,我们常用的接口是HttpServletRequest、
HttpServletResponse。
我们可以利用request对象处理请求,处理请求就是读取请求当中包含的数据加以处理。
# 关于response如何响应网页
通过response响应请求其实就是通过它里面封装的输出流向浏览器输出
小demo
@Controller
@RequestMapping("/alpha")
public class AlphaController {
@Autowired
private AlphaService alphaService;
@RequestMapping("/data")
@ResponseBody
// 这个方法是通过返回参数转化为json数据呈现在浏览器上,而下面的http方式是通过响应对象呈现
// 数据在浏览器上
public String getData(){
return alphaService.find();
}
@RequestMapping("/http")
// 浏览器的请求对象 controller响应请求的响应对象
public void http(HttpServletRequest request, HttpServletResponse response){
// 获取浏览器的请求数据
System.out.println(request.getMethod()); // 获取请求方式
System.out.println(request.getServletPath()); // 请求路径
Enumeration<String> enumeration = request.getHeaderNames(); // 获取所有的请求行的key(请求行是key-value结构)
// while里面的其实是请求头的数据
while (enumeration.hasMoreElements()){
String name = enumeration.nextElement();
String value = request.getHeader(name);
System.out.println(name + ": " + value);
}
// 获取请求体当中的参数(业务数据)
System.out.println(request.getParameter("code"));
// 返回controller的响应数据的对象: response
response.setContentType("text/html;charset=utf-8"); // 设置返回的相应数据的类型和编码
PrintWriter writer = null;
try {
writer = response.getWriter(); // 获取响应对象的输出流去响应网页(异常try/catch处理)
writer.write("<h1>牛客网</h1>"); // 通过writer向浏览器输出一个一级标题
} catch (IOException e) {
e.printStackTrace();
} finally {
writer.close(); // 关闭响应对象的输出流
}
}
}
通过浏览器传参的其中一种方法
直接在地址栏上拼接数据
地址栏传参在url上用 ?拼接 传参多个的话多个参数之间用 & 拼接
浏览器从服务器获取数据:GET请求
1. 浏览器传的参数通过地址栏拼接在一起
浏览器传的参数通过地址栏拼接在一起如何接收
http://localhost:8080/community/alpha/students?current=1&limit=30
除了上面我们可以从浏览器的请求对象获取数据,我们还可以在controller方法的传参中声明一下数据(需要注意一点的是:成员方法的名字要和传过来的参数的名字一样),这样传的参数会自动赋值给方法的参数。
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// GET请求
// /students?current=1&limit=20
@RequestMapping(path = "/students", method = RequestMethod.GET) // 制定了浏览器请求的方式只能为GET方式
@ResponseBody
public String getStudents( int current,int limit){
System.out.println("current = " + current);
System.out.println("limit = " + limit);
return "some students";
}
}
# 注意
使用这种方式接收参数的话浏览器在请求 http://localhost:8080/community/alpha/students
时必须传参,不传参会出现异常,那么我们想要不传参的话也不会出现异常并且有默认值的话该怎么做,
这个时候我们就需要给方法的参数加上注解了。如下:
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// GET请求
// /students?current=1&limit=20
@RequestMapping(path = "/students", method = RequestMethod.GET) // 制定了浏览器请求的方式只能为GET方式
@ResponseBody
public String getStudents(
@RequestParam(name = "current", required = false, defaultValue = "1") int current,
@RequestParam(name = "limit", required = false, defaultValue = "20") int limit){
System.out.println("current = " + current);
System.out.println("limit = " + limit);
return "some students";
}
}
2.浏览器传的参数成为了路径的一部分
浏览器传的参数成为了路径的一部分
http://localhost:8080/community/alpha/student/456
这种方式需要用到 @PathVariable 注解
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// GET请求
// /student/123
@RequestMapping(path = "/student/{id}", method = RequestMethod.GET)
@ResponseBody
public String getStudent(@PathVariable("id") int id){
System.out.println("id = " + id);
return "a student";
}
}
# 关于GET方式是从服务器获取数据,POST方式是向浏览器提交数据怎么解释?
参考了一下别人的博客:
其实二者都是在向服务器提交请求,如果非要说get是获取数据,post是提交数据的话,你可以从二者的区别理解:
安全性:get请求的数据是url地址明文发送,不安全,而post的请求数据不会再地址栏出现,较为安全
数据量限制:get有数据量限制,而post没有
等幂性:get请求的数据不会修改服务器的状态,如读取静态文件(图片、html文件等),所以这里你可以理解为是获
取数据,而post一般会改变服务器的状态.比方说添加某一条数据(你所说的提交数据),那么此数据将会更改数据库
的记录 ,所以,你可以理解post是向服务器提交数据
另一种解释:
说get是向服务器获取数据,而post是向服务器传递数据 其实是英文文档翻译的时候断章取义了,文档中可出这点
(HTML规范在技术上定义了“GET”和“POST”之间的区别,前者意味着表单数据将(通过浏览器)编码成URL,而后者意
味着表单数据将出现在消息正文中。但是规范也给出了使用建议,当表单处理是“幂等”时,应该使用“GET”方法,并且
仅在那些情况下。作为一种简化,我们可以说“GET”基本上是为了获取(检索)数据,而“POST”可能涉及到存储或更新
数据、订购产品或发送电子邮件等任何事情。)
浏览器向服务器提交数据:POST请求
# 浏览器向服务器提交数据:POST请求
GET方式是从服务器获取数据那么如果浏览器是向服务器提交数据通常我们都用post,post就是浏览器向服务器
提交数据的时候使用的。
浏览器相向服务器提交数据,它得打开一个带有表单的网页,通过表单填写数据之后提交给服务器。
我们先创建一个html文件(静态资源):student.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>增加学生</title>
</head>
<body>
<form method="post" action="/community/alpha/student">
<p>
姓名:<input type="text" name="name">
</p>
<p>
年龄:<input type="text" name="age">
</p>
<p>
<input type="submit" value = "保存">
</p>
</form>
</body>
</html>
- form中的method指明浏览器提交数据的方式为“POST”
- form中的action表示这个表单提交给指定路径的controller
- 中的type表示类型,name表示这个提交数据的名字
- 中的type是submit表示提交,value是这个按钮在浏览器中显示的名字
接下来我们来创建一个controller来处理上面所提交的表单:
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// POST请求
@RequestMapping(path = "/student", method = RequestMethod.POST)
@ResponseBody
public String saveStudent(String name, int age){
System.out.println("name = " + name);
System.out.println("age = " + age);
return "success";
}
}
controller向浏览器返回html文件
themeleaf模板的方式返回网页
返回 ModelAndView 方式返回themeleaf资源
接下来演示一下如何向浏览器返回响应数据:
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// 响应html数据
@RequestMapping(path = "/teacher", method = RequestMethod.GET)
public ModelAndView getTeacher(){
ModelAndView modelAndView = new ModelAndView();
// 传值
modelAndView.addObject("name", "张三");
modelAndView.addObject("age", 30);
// 设置模板的路径和名字(模板会放到 templates 目录,这个路径不用写,)
modelAndView.setViewName("/demo/view"); // view.html这个文件,后面的 .html 后缀省略
return modelAndView;
}
}
接下来我们来常见一个模板文件:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.themeleaf.org">
<head>
<meta charset="UTF-8">
<title>teacher</title>
</head>
<body>
<p th:text="${name}"></p>
<p th:text="${age}"></p>
</body>
</html>
创建一个html文件之后需要声明,这个html文件不是静态的,得让服务器直到它是 themeleaf 模板(动态资源),而不是普通的html。
需要将第二行的
<html lang="en">
修改为
<html lang="en" xmlns:th="http://www.themeleaf.org">
这句话就声明当前这个html是一个模板,这个模板的语法来源于 themeleaf 官网。
返回String字符串来返回themeleaf模板资源
上面我们使用的是返回一个ModelAndView去返回一个themeleaf模板,接下来我们来试一下返回String字符串来返回themeleaf模板,返回的是字符串的话返回的是themeleaf模板的路径。
如果我们采用返回字符串方法声明中需要声明一个 Model ,这样 DispatcherServlet 会给这个Model传一个值,DispatcherServlet 持有这个 Model 的引用,也就是可以获得数据,然后通过返回字符串返回 themeleaf 的路径(View),这样 DispatcherServlet 持有 Mode 和 View 然后会把它们交给模板引擎然后模板引擎在浏览器上进行渲染。
@Controller
@RequestMapping("/alpha")
public class AlphaController {
@RequestMapping(path = "/school", method = RequestMethod.GET)
public String getSchool(Model model){
// 传值
model.addAttribute("name", "北京大学");
model.addAttribute("age", "80");
return "/demo/view"; // 返回 view
}
}
themeleaf模板还是上面那个view.html文件
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.themeleaf.org">
<head>
<meta charset="UTF-8">
<title>teacher</title>
</head>
<body>
<p th:text="${name}"></p>
<p th:text="${age}"></p>
</body>
</html>
服务器向浏览器返回JSON格式的数据
一般在异步请求当中会响应JSON数据
# 什么是异步请求
举个例子 :
注册用户时,输入的用户名重复,会立即显示当前用户名已被占用,当前网页不刷新,
但是浏览器访问了服务器得到了结果
# JSON作用
如果把java对象这个数据返回给浏览器,浏览器解析这个对象用的是JS,两者不兼容。
我们使用JSON可以保证两者的兼容 :
Java对象 -> JSON字符串 -> JS对象
当然下面并不会演示如何发送异步请求,现在我们关心的是如何向浏览器响应JSON数据
@Controller
@RequestMapping("/alpha")
public class AlphaController {
// 响应JSON数据
// Java对象 -> JSON字符串 -> JS对象
@RequestMapping(path = "/emp", method = RequestMethod.GET)
@ResponseBody // 加上这个注解才会将返回的数据转换成JSON字符串
public Map<String, Object> getEmp(){
Map<String, Object> emp = new HashMap<>();
emp.put("name", "张三");
emp.put("age", 23);
emp.put("salary", 8000.00);
return emp;
}
}
- 我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。