- 在上一讲,学习了Spring Boot Web的快速入门以及Web开发的基础知识,包括HTTP协议以及Web服务器Tomcat等内容。
- 基于SpringBoot的方式开发一个web应用,浏览器发起请求 /hello 后 ,给浏览器返回字符串 “Hello World ~”。
- 运行启动类启动内嵌的Tomcat服务器之后就可以打开浏览器,然后在浏览器的地址栏当中输入访问路径,回车之后就可以访问到我们部署在Tomcat服务器当中的应用程序了。
- 其实呢,是我们在浏览器发起请求,请求了我们的后端web服务器(也就是内置的Tomcat)。而我们在开发web程序时呢,定义了一个控制器类Controller,请求会被部署在Tomcat中的Controller接收,Controller接收到请求之后,对请求进行处理,处理完毕之后然后Controller再给浏览器响应一个对应的结果,响应一个字符串 “Hello World”。
- 而客户端浏览器和服务器在进行数据传输的时候,是基于HTTP协议的,因为HTTP协议就规定了浏览器与服务器进行数据传输的规则,请求响应的过程中是遵循HTTP协议的。
- 我们之前所编写的这个Controller程序,它仅仅是一个简单的Java类,它并没有实现任何的接口,也没有继承任何的类,那其实在Tomcat这类Web服务器中,是不识别我们自己定义的Controller程序的。
- Tomcat虽然不识别我们所编写的Controller程序,但是Tomcat它是一个Servlet容器,它是支持Servlet规范的,所以在Tomcat中是可以识别Servlet程序的。
那我们所编写的XxxController 是如何处理请求的,又与Servlet之间有什么联系呢?
- 在SpringBoot进行web程序开发时,它内置了一个核心的Servlet程序DispatcherServlet
- 其实,在我们基于Spring Boot进行Web程序开发时,Spring Boot底层给我们提供了一个非常核心的Servlet程序DispatcherServlet(核心控制器 / 前端控制器)。
- 而这个Dispatcher Servlet它就实现了Servlet规范当中的 Servlet接口,所以它是一个可以被Tomcat识别的Servlet程序。
- 有了DispatcherServlet之后,前端浏览器发起的请求,都会先经过DispatcherServlet,由DispatcherServlet再将这个请求转发给后面的各个请求处理器Controller程序,由Controller程序再对请求进行处理,请求处理器Controller程序处理完请求之后再将处理完的结果返回给DispatcherServlet,最终再由DispatcherServlet给浏览器响应数据。
- 通过该流程我们可以看出DispatcherServlet在Web请求当中是一个非常核心的一个类,所以这个类在Spring Boot当中我们称为核心控制器或者叫前端控制器。
我们如何在Servlet程序当中来获取请求的参数呢?
- 将来前端浏览器发起请求,会携带HTTP的请求数据,包括:请求行、请求头;
- 请求到达Web服务器Tomcat之后,Tomcat服务器会负责解析这些请求数据,然后将解析后的请求数据会传递给 / 封装到Servlet程序的HttpServletRequest对象(请求对象)
- 那也就意味着HttpServletRequest对象就可以获取到请求数据。
- 接下来我们的应用程序可以从HttpServletRequest对象当中来获取请求数据,然后再对这个请求进行处理,处理完毕之后Tomcat服务器需要根据HTTP响应数据的格式给浏览器响应数据。
那Tomcat怎么就知道要给浏览器响应哪些数据呢?
- 而Tomcat还给Servlet程序传递了一个参数HttpServletResponse。
- 其实在Servlet程序当中,我们可以借助于另外一个对象HttpServletResponse(响应对象),通过这个对象,我们就可以给浏览器设置响应的数据。
- 然后Tomcat就会根据我们在这个请求对象当中所设置的响应信息来响应数据给浏览器。
这就是Web应用程序整体的请求访问流程,对于我们来说,我们需要重点关注的就是我们所编写的Controller控制器程序。
在Controller程序当中,最重要的就是获取请求参数以及设置响应参数。
请求对象 HttpServeltRequest,它就是用来获取请求数据的。
响应对象 HttpServletResponse,它就是用来设置响应数据的。
那上述所描述的这种浏览器/服务器的架构模式呢,我们称之为:BS架构。
BS架构:Browser/Server,浏览器/服务器架构模式。
- 在这种架构模式当中,客户端只需要一个浏览器就行,应用程序所有的逻辑和数据都是存储在服务端的。
- 浏览器很简单,我们系统已经安装好了,我们要访问这一类的应用,直接打开浏览器地址栏,输入地址就可以直接访问,非常方便。
- 只要能通过浏览器所访问到的网站,都是BS架构。
- 由于BS架构不需要单独的安装客户端,因此BS架构维护起来会更加方便一些,只需要维护服务端就可以。
- 但由于所有的逻辑和数据都是存储在服务器端的,因此我们在通过浏览器端去访问服务器端的时候,它的加载速度就会收到网络带宽以及服务器的性能影响。
- 我们在以后基于Java语言去开发Web应用程序的时候基本上都是BS架构。
CS架构:Client/Server,客户端/浏览器架构模式。
- 在这种架构模式当中,我们是需要单独去下载对应的客户端的,而且不同的操作系统对应的客户端也不一样,Windows有Windows版的客户端,Mac有Mac版的客户端。
- 所以在这种软件架构模式下,软件的开发以及维护会比较麻烦,但是体验不错,因为它有独立的客户端,有很多的逻辑以及数据它是可以在客户端进行处理的,速度会更快一点。
请求:就是在Controller程序当中,我们要来获取各种各样的参数
响应:就是在Controller程序当中,我们要来设置响应的数据
分层解耦:就是将我们所编写的应用程序进行改造,完成代码的分层解耦
一. 请求
第一章请求,主要讲解如何接收页面传递过来的请求数据,以及各类请求参数的接收及封装(包括简单参数、实体参数、数组集合参数、日期参数、Json参数、路径参数等)
- 先需要介绍一款功能强大的接口测试工具-Postman
介绍并安装
- 当前最主流的开发模式就是前后端分离开发,在这种开发模式下,前端人员基于"接口文档",开发前端的工程 / 前端程序,后端人员基于"接口文档",开发后端的工程 / 程序。
- 在后端工程的开发过程当中,我们开发完一个功能,就需要对这个功能接口来进行测试,而由于是前后端分离开发的,对我们后端技术人员来讲,在开发过程中,我们是没有前端页面的,那这个时候我们该怎么测试?
方式1:像之前SpringBoot入门案例中一样,直接使用浏览器。在浏览器中输入地址,测试后端程序。
-
弊端:在浏览器地址栏中输入地址这种方式都是GET请求,如何我们要用到测试POST方式的请求怎么办呢?
要解决POST请求,要测试POST方式的请求,需要程序员自己编写前端代码(比较麻烦) -
如果我们要测试POST方式的请求,这个时候就比较麻烦了,我们可能需要自己去编写前端的代码,然后再来进行后端的功能接口测试,这是比较繁琐的,此时我们就可以借助一款功能强大的接口测试工具-Postman,通过Postman就可以轻松的解决各种接口测试的需求了。
注意:浏览器地址栏输入地址所发起的请求,全部都是GET请求。
方式2:使用专业的接口测试工具(Postman工具)
1.1.1 介绍
- Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。
- Postman原本是Chrome浏览器的一款插件,可以用来模拟浏览器来发起任何形式(如:get、post) 的HTTP请求,并且在请求的过程当中还可以很方便的去携带很多的请求参数,请求头等信息。常用于做接口的测试,也是后端开发工程师进行接口测试的首选工具。
- 作用:常用于进行接口测试
-
特征
-
简单
-
实用
-
美观
-
大方
-
- 基于Postman也衍生出来了很多其他的工具,比如:Apipost,Apifox等,这些工具都是基于Postman衍生出来的。所以它们的使用方式也很简单,和Postaman基本一致。
1.1.2 安装
- 登录完成之后,可以创建工作空间:
- 创建请求:
界面介绍:
- 第一栏要选择的是请求方式 后面要填写的就是URL请求路径(统一资源定位器)
- 请求的参数 请求头的信息 请求体的信息
- 点击send之后在下面的区域展示的就是响应回来的数据
- 如果我们需要将测试的请求信息保存下来,就需要创建一个postman的账号,然后登录之后才 可以。
-
如果想把本次请求测试完毕之后的请求的数据保存下来,可以点击右上角的Save或者直接按Ctrl+S就可以进入到保存的界面上
- Request Name:请求的名字
- 设置完请求的名字之后,在下面的这个位置需要来选择一个集合或者一个文件夹,来对这个请求进行归类处理。可以直接点击按钮来创建一个集合。
常见参数的接收及封装
1.2 简单参数
简单参数:在向服务器发起请求时,向服务器传递的是一些普通的请求数据。
那么在后端程序中,如何接收传递过来的普通参数数据呢?
我们在这里讲解两种方式:
-
原始方式
-
SpringBoot方式
1.2.1 原始方式
- 在原始的Web程序当中,需要通过Servlet中提供的API:HttpServletRequest(请求对象),获取请求的相关信息。比如获取请求参数:
- Tomcat接收到http请求时:把请求的相关信息封装到HttpServletRequest对象中
-
这个请求对象当中封装了所有的请求数据
我们所获取过来的所有请求参数都是一个字符串
- 在Controller中,我们要想获取Request对象,可以直接在方法的形参中声明 HttpServletRequest 对象。然后就可以通过该对象来获取请求信息:
package com.gch.controller;
import com.gch.pojo.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletRequest;
/**
测试请求参数接收
*/
@RestController // 该注解用来标识当前类就是Spring当中的一个请求处理类而不是普通类
public class RequestController {
// 原始方式
// http://localhost:8080/simpleParam?name=Tom&age=10
// 请求参数: name=Tom&age=10 (有2个请求参数)
// 第1个请求参数: name=Tom 参数名:name,参数值:Tom
// 第2个请求参数: age=10 参数名:age , 参数值:10
/**
* 原始方式接收传递过来的普通参数数据
* 注解@RequestMapping:用来指定请求路径
* @param request:请求对象
* @return "OK"
*/
@RequestMapping("/simpleParam")
public String simpleParam(HttpServletRequest request){
// 获取请求参数
String name = request.getParameter("name");
String ageStr = request.getParameter("age");
// 手动类型转换
int age = Integer.valueOf(ageStr);
System.out.println(name + ":" + age);
return "OK";
}
}
- 以上这种方式,我们仅做了解。(在以后的开发中不会使用到)
1.2.2 SpringBoot方式
在Spring Boot当中怎么简化这一块的参数接收?
在Springboot的环境中,对原始的API进行了封装,接收参数的形式更加简单。 如果是简单参数,参数名与形参变量名相同,定义同名的形参即可接收参数。
- 在Spring Boot当中,前端在请求的时候传递的这些简单参数,我们只需要在Controller方法当中声明对应的形参就可以接收到了。只需要保证前端的请求参数名与Controller方法的形参变量名保持一致,即可接收成功。并且在接收的过程中当中还会进行自动的类型转化。
@RestController
public class RequestController {
// http://localhost:8080/simpleParam?name=Tom&age=10
// 第1个请求参数: name=Tom 参数名:name,参数值:Tom
// 第2个请求参数: age=10 参数名:age , 参数值:10
/**
* springboot方式
* 请求处理方法
* 基于SpringBoot的方式来接收传递过来的普通参数数据
* SpringBoot会进行自动类型转换
* @RequestMapping注解是建立url路径跟我们这个方法之间的对应关系的
* @ReuqestMapping里面指定的value值就是它要处理的请求路径是什么
* 协议://主机[:端口][/路径][?查询参数]
* http://localhost:8080/hello?参数名=参数值&参数名=参数值
* @RestController和RequestMapping这两个注解就是SpringBoot提供给我们的规则、条条框框
* @return:该方法的返回值就是我们要返回给浏览器的数据
*/
@RequestMapping("/simpleParam")
public String simpleParam(String name, Integer age){
System.out.println(name + ":" + age);
return "OK";
}
}
postman测试( GET 请求):
- 在GET请求的请求路径后面携带了两个请求参数
postman测试( POST请求 ):POST请求请求参数是在请求体当中携带
结论:不管是GET请求还是POST请求,对于简单参数只需要保证请求的参数名与Controller方法 的形参名保持一致,就可以自动接收成功,从而获取到请求参数中的数据值。
1.2.3 参数名不一致
如果方法形参名称与请求参数名称不一致,controller方法中的形参还能接收到请求参数值吗?
@RestController
public class RequestController {
// http://localhost:8080/simpleParam?name=Tom&age=20
// 请求参数名:name
//springboot方式
@RequestMapping("/simpleParam")
public String simpleParam(String username , Integer age ){//请求参数名和形参名不相同
System.out.println(username+" : "+age);
return "OK";
}
}
答案:运行没有报错。 controller方法中的username值为:null,age值为20
- 结论:对于简单参数来讲,请求参数名和controller方法中的形参名不一致时,无法接收到请 求数据
- 只要参数能对应上,就可以接收成功。如果参数对应不上,接收不成功,但是它不会报错。
那么如果我们开发中,遇到了这种请求参数名和controller方法中的形参名不相同,怎么办?
- 如果参数对应不上,也可以接收成功,这个时候需要借助于Spring当中提供的一个注解@RequestParam手动的来完成映射。
- 我们需要在Controller方法的形参前面加上一个@RequestParam,并且通过name / value属性来指定请求参数名是什么。通过name / value属性指定的请求参数名会映射到Controller方法的形参上面来。
@RestController
public class RequestController {
// http://localhost:8080/simpleParam?name=Tom&age=20
// 请求参数名:name
//springboot方式
@RequestMapping("/simpleParam")
public String simpleParam(@RequestParam("name") String username , Integer age ){
System.out.println(username+" : "+age);
return "OK";
}
}
注意事项:
- @RequestParam中的required属性默认为true(默认值也是true),代表该请求参数必须传递,如果不传递将报错
如果该参数可传递可不传递,可以将required属性设置为false
@RequestMapping("/simpleParam")
public String simpleParam(@RequestParam(name = "name", required = false) String username, Integer age){
System.out.println(username+ ":" + age);
return "OK";
}
1.3 实体参数
- 在简单参数接收的时候,前端传递了多少个请求参数,我们就需要在后端Controller方法当中声明多少个形参来接收。
- 如果请求参数比较多,通过上述的方式一个参数一个参数的接收,会比较繁琐。
- 此时,我们可以考虑将请求参数封装到一个实体类对象中。 要想完成数据封装,需要遵守如下规则:请求参数名与实体类的属性名相同
- 将所有的请求参数都封装到一个实体类 / 对象当中。要想成功的封装,就必须保证一个原则:需要让请求的参数名与是实体类的属性名保持一致。
1.3.1 简单实体对象
定义POJO实体类:
public class User {
private String name;
private Integer age;
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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Controller方法:
@RestController
public class RequestController {
//实体参数:简单实体对象
@RequestMapping("/simplePojo")
public String simplePojo(User user){
System.out.println(user);
return "OK";
}
}
Postman测试:
- 参数名和实体类属性名一致时
- 参数名和实体类属性名不一致时
简单实体参数的请求用例
- 简单实体参数的封装只需要保证请求的参数名与形参对象的属性名保持一致。
1.3.2 复杂实体对象
上面我们讲的呢是简单的实体对象,下面我们在来学习下复杂的实体对象。
复杂实体对象指的是,在实体类中有一个或多个属性,也是实体对象类型的。如下:
- User类中有一个Address类型的属性(Address是一个实体类)
复杂实体对象的封装,需要遵守如下规则:
-
请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套实体类属性参数。
定义POJO实体类:
public class Address {
private String province;
private String city;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
'}';
}
}
User实体类:
public class User {
private String name;
private Integer age;
private Address address; //地址对象
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", address=" + address +
'}';
}
}
Controller方法:
@RestController
public class RequestController {
//实体参数:复杂实体对象
@RequestMapping("/complexPojo")
public String complexPojo(User user){
System.out.println(user);
return "OK";
}
}
Postman测试:
简单实体参数的接收
1.4 数组集合参数
数组集合参数的使用场景:在HTML的表单中,有一个表单项是支持多选的(复选框),可以提交选 择的多个值。
多个值是怎么提交的呢?其实多个值也是一个一个的提交。
后端程序接收上述多个值的方式有两种:
-
数组
-
集合
服务端请求参数的接收方式: 使用数组来接收 使用集合来接收
1.4.1 数组
数组参数:请求参数名与形参数组名称相同且请求参数为多个,定义数组类型形参即可接收参数
Controller方法:
@RestController
public class RequestController {
//数组集合参数
@RequestMapping("/arrayParam")
public String arrayParam(String[] hobby){
System.out.println(Arrays.toString(hobby));
return "OK";
}
}
Postman测试:
在前端请求时,有两种传递形式: 前端参数传递的形式:
- 方式一: xxxxxxxxxx?hobby=game&hobby=java
- 方式二:xxxxxxxxxxxxx?hobby=game,java
1.4.2 集合 使用集合来完成参数的接收以及封装
集合参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
- 默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam绑定参数关系
- 使用集合封装,要在形参的这个List集合前面加上一个注解@RequestParam来绑定参数关系,因为默认情况下这多个值它是会封装到数组当中的,如果要封装到List集合,就需要在前面加上这个@RequestParam注解。
Controller方法:
@RestController
public class RequestController {
//数组集合参数
@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby){
System.out.println(hobby);
return "OK";
}
}
Postman测试:
方式一: xxxxxxxxxx?hobby=game&hobby=java
方式二:xxxxxxxxxxxxx?hobby=game,java
- 这就是使用数组以及集合的形式来接收前端传递过来的多个请求参数值,
1.5 日期参数(Date Param)
日期时间类型的参数
- 在项目的前端表单页面当中,经常会遇到一些日期时间类型的参数,比如像用户的生日,入职日期,操作时间这样的请求参数,这一类型的请求参数可以根据项目的需要,将其封装到日期类Date或者是JDK1.8以后提供的LocalDateTime当中来。
- 由于前端在传递日期类时间参数的时候,格式多种多样,有可能是横杠分隔,有可能是斜杠分隔,有可能是年月日这种格式,所以在服务端我们接收的时候,需要指定前端传递过来的格式是什么样子的,通过一个注解@DateTimeFormat,然后里面声明一个属性pattern来指定前端传递的日期参数的格式。这就代表前端将来在请求的时候,就必须按照这个格式来传递对应的请求参数。方法形参的名称也需要与请求参数的名称保持一致。
- 因为日期的格式多种多样(如:2022-12-12 10:05:45 、2022/12/12 10:05:45),那么对于日期类型的参数在进行封装的时候,需要通过@DateTimeFormat注解,以及其pattern属性来设置日期的格式。
-
@DateTimeFormat注解的pattern属性中指定了哪种日期格式,前端的日期参数就必须按照指定的格式传递。
-
后端controller方法中,需要使用Date类型或LocalDateTime类型,来封装传递的参数。
Controller方法:
@RestController
public class RequestController {
//日期时间参数
@RequestMapping("/dateParam")
public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime){
System.out.println(updateTime);
return "OK";
}
}
Postman测试:
1.6 JSON参数
第五种接收Json格式的参数
- JSON格式的参数在前后端异步交互的时候,使用的是非常多的。
我们学习JSON格式参数,主要从以下两个方面着手:
-
Postman在发送请求时,如何传递json格式的请求参数
-
在服务端的controller方法中,如何接收json格式的请求参数
Postman发送JSON格式数据:
- 需要将请求方式设置为POST,因为这个JSON格式的请求数据是需要放在请求体当中携带到服务端的。
- 选择JSON然后填写JSON格式的请求参数
- JSON当中所有的key必须得使用双引号将其引起来,如果没有加双引号,将会报错。
服务端Controller方法接收JSON格式数据:
- 接收JSON格式的数据,一般会通过实体类对象来接收。
-
需要让JSON格式的键名与实体对象的属性名保持一致。
通过实体对象来接收并封装JSON格式的请求数据。
加上注解@RequestBody之后就可以将JSON格式的请求数据直接封装到这个实体对象当中。
-
传递json格式的参数,在Controller中会使用实体类进行封装。
-
封装规则:JSON数据键名与形参对象属性名相同,定义POJO类型形参即可接收参数。需要使用 @RequestBody标识。
-
@RequestBody注解:将JSON数据映射到形参的实体类对象中(JSON中的key和实体类中的属性名保持一致)
实体类:Address
public class Address {
private String province;
private String city;
//省略GET , SET 方法
}
实体类:User
public class User {
private String name;
private Integer age;
private Address address;
//省略GET , SET 方法
}
Controller方法:
@RestController
public class RequestController {
//JSON参数
@RequestMapping("/jsonParam")
public String jsonParam(@RequestBody User user){
System.out.println(user);
return "OK";
}
}
Postman测试:
1.7 路径参数(Path Param)
传统的开发中请求参数是放在请求体(POST请求)传递或跟在URL后面通过?key=value的形式传递(GET请求)。
- 在现在的开发中,经常还会直接在请求的URL中传递参数。例如:
http://localhost:8080/user/1
http://localhost:880/user/1/0
- 上述的这种传递请求参数的形式呢,我们称之为:路径参数。
- 路径参数是一种比较特殊的请求参数,路径参数指的是参数已经成为了URL请求路径的一部分。
在后端服务端Controller的方法当中怎么样来获取 / 接收这个路径参数?
路径参数:
-
前端:通过请求URL直接传递参数
-
后端:使用{…}来标识该路径参数,需要使用@PathVariable获取路径参数
路径参数:在后端Controller方法注解@RequestMapping中请求路径中的路径参数应该是动态的 而不应该写死
例如:@RequestMapping("/path/{id}")
- 请求路径里面 {id}代表路径参数,代表路径参数的参数名就叫id
- 在Controller方法当中,声明这么一个形参就叫id,并且在形参前面加上一个注解@PathVariable,用来指定我们要获取到这个路径参数,并且把路径参数的值绑定给我们的方法形参,这样方法形参它就会自动的获取到路径参数这个id上,路径参数的参数名id需要与方法形参名称id保持一致。
Controller方法:
@RestController
public class RequestController {
//路径参数
@RequestMapping("/path/{id}")
public String pathParam(@PathVariable Integer id){
System.out.println(id);
return "OK";
}
}
Postman测试:
传递多个路径参数:
- 如果我们需要传递多个路径参数,我们直接在请求路径当中使用斜杠来分隔,然后再写第二个参数就可以了。
- 然后我们再定义一个Controller方法来指定访问的路径,请求路径当中有几个路径参数就在Controller方法当中定义几个形参,并且在每个形参前面加上一个注解@PathVeriable,通过@PathVeriable就可以获取到这个路径参数,并且把路径参数的值绑定给这个形参。
注意:路径参数名必须与方法形参名称保持一致才可以绑定成功。
Controller方法:
@RestController
public class RequestController {
//路径参数
@RequestMapping("/path/{id}/{name}")
public String pathParam2(@PathVariable Integer id, @PathVariable String name){
System.out.println(id+ " : " +name);
return "OK";
}
}
Postman:
Web开发一些常见的请求参数的接收就已经介绍完了,还有一些比较特殊的请求参数的接收在后续会学习。