web 资源就是运行在服务器上的资源,比如放到 web 下的页面 js 文件、图片、css等,web资源分为静态web资源和动态web资源两类,接下来访问的就是动态资源(页面返回的数据是动态的,由后端程序产生),本文主要借助 RestTemplate 和 WebClient 两个工具。
目录
1.2 编写 Dao层、service层和 controller层
① getForObject() / getForEntity()
② postForObject() /postForEntity()—HTTP请求
项目源码:尹煜 / visitwebdemo · GitCode
因为文章尽可能想写的详尽基础一些,所以内容可能会有点多,熟练者可直接看2、3章内容
因为访问 Web 资源的前提是存在 Web 资源可供访问,因此本文的逻辑是在本地创建一个 Web 环境(写 controller),然后由 test 类进行访问测试。

Spring Boot 版本是 2.7.6 ,建议将版本控制在 2-3 之间,超出范围的话会容易产生兼容问题

路径:pom.xml
-
-
-
com.baomidou -
mybatis-plus-boot-starter -
3.5.2 -
前提是连接的数据库存在与实体类相对应的数据表,数据表初始搭建详解在Spring MVC 实践详解文章的【2/2.1Mysql 数据库初始化】小节
路径:src/main/resources/application.properties
- #数据库连接配置
- spring.datasource.username=root
- spring.datasource.password=root
- #mysql5~8 驱动不同driver-class-name 8需要增加时区的配置serverTimezone=UTC,放在url最后
- #useSSL=false 安全连接
- spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
- spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
使用 lombok 和 mybatisplus 的实体类注释,加大开发效率
路径:src/main/java/com/visitwebdemo/pojo/User.java
- package com.visitwebdemo.pojo;
-
- @Builder
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class User {
-
- @TableId(type = IdType.AUTO)//新增记录时未命名id时id自增
- private Long id;
-
- private String name;
- private Integer age;
- private String email;
- }
路径:src/main/java/com/visitwebdemo/mapper/UserMapper.java
- package com.visitwebdemo.mapper;
-
- //在对应的接口上面继承一个基本的接口 BaseMapper
- @Mapper
- public interface UserMapper extends BaseMapper
{ - //mybatisplus 将所有CRUD操作都编写完成了,不用像以前一样配置一大堆文件
-
- }
在主启动类添加@MapperScan注解
路径:src/main/java/com/visitwebdemo/VisitwebdemoApplication.java
- package com.visitwebdemo;
-
- @MapperScan("com.visitwebdemo.mapper")
- @SpringBootApplication
- public class VisitwebdemoApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(VisitwebdemoApplication.class, args);
- }
- }
编写实体类对应的 UserBaseService 接口
路径:src/main/java/com/visitwebdemo/service/UserBaseService.java
- package com.visitwebdemo.service;
-
- //如有需要用以重写IService里的抽象方法,如不需要重写也可去掉该文件,将IService
写在UserServiceImpl文件 - public interface UserBaseService extends IService
{ -
- }
编写 Service 层的实现类,以下就是具体的增删改查操作 👇
路径:src/main/java/com/visitwebdemo/service/impl/UserServiceImpl.java
- package com.visitwebdemo.service.impl;
-
- @Service
- public class UserServiceImpl extends ServiceImpl
implements UserBaseService { -
- @Autowired
- private UserMapper userMapper;
-
- /*
- Iservice CRUD(增删改查)
- */
-
- //增加一个User
- public boolean addUser(User user){
- return save(user);
- }
-
- //根据id删除一个User
- public boolean deleteUserById(int id){
- return removeById(id);
- }
-
- //更新User
- public boolean updateUser(User user){
- return updateById(user);
- }
-
- //根据id查询,返回一个User
- public User queryUser(int id){
- return getById(id);
- }
-
- //查询全部User,返回list集合
- public List
queryAllUser(){ - return list();
- }
-
-
- /*
- Mapper CRUD(增删改查)
- */
-
- //增加一个User
- public int addUser_ByMapper(User user){
- return userMapper.insert(user);
- }
-
- //根据id删除一个User
- public int deleteUserById_ByMapper(int id){
- return userMapper.deleteById(id);
- }
-
- //更新User
- public boolean updateUser_ByMapper(User user){
- userMapper.updateById(user);
- return true;
- }
-
- //根据id查询,返回一个User
- public User queryUser_ByMapper(int id){
- return userMapper.selectById(id);
- }
-
- //查询全部User,返回list集合
- public List
queryAllUser_ByMapper(){ - return userMapper.selectList(new QueryWrapper<>());//QueryWrapper没有任何条件
- }
- }
写了三个具有代表性的接口 👇
路径:src/main/java/com/visitwebdemo/controller/UserController.java
- package com.visitwebdemo.controller;
-
- @Slf4j
- @RestController
- @RequestMapping("/web")
- public class UserController {
-
- @Autowired
- private UserServiceImpl userService;
-
- @RequestMapping("/allUser")
- public List
allUser() { - return userService.queryAllUser();
- }
-
- //@PathVariable路径参数
- @RequestMapping("/query/{userId}")
- public User queryUser(@PathVariable("userId") int id) {
- return userService.queryUser(id);
- }
-
- //绑定请求参数到实体类对象
- @RequestMapping("/body")
- public boolean updateUser(User user) {
- return userService.updateUser(user);
- }
-
- }
以上准备工作就完成了~
RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,它提供了很多可以方便访问远程 http 服务的方法,这些方法可以帮助开发人员减少编写客户端代码的工作量。
其实最主要还是 Get 和 Post 请求:
xxxForObject() 和 xxxForEntity() 二者区别主要在于:xxxForObject() 的返回值是HTTP协议的响应体;而 xxxForEntity() 返回的是 ResponseEntity(ResponseEntity是对HTTP响应的封装),除了包含响应体,还包含HTTP状态码、contentType、contentLength、Header等信息。
发送请求需要携带 URI 👇,以下是几个常见的构造方式,UriComponentsBuilder 最为常用
- URI uri = UriComponentsBuilder
- .fromUriString("http://localhost:8080/web/query")
- .build()
- .toUri();
- URI uri = UriComponentsBuilder
- .fromUriString("http://localhost:8080/web/query/{userId}")
- .build(1);
- URI uri = MvcUriComponentsBuilder
- .fromMethodCall(MvcUriComponentsBuilder.on(UserController.class).allUser())
- .build()
- .toUri();
④ 获取当前请求的URI
- URI uri = ServletUriComponentsBuilder
- .fromCurrentRequest()
- .build()
- .toUri();
注意由于是请求本地项目的 web 资源,因此需要在先启动项目 👇,然后再对测试类进行测试

路径均在:src/test/java/com/visitwebdemo/restemplateTests.java
- //new 一个RestTemplate ,后面也会用到
- RestTemplate restTemplate = new RestTemplate();
-
- @Test
- public void queryOneUser(){
- //构建 uri
- URI uri = UriComponentsBuilder
- .fromUriString("http://localhost:8080/web/query/{userId}")
- .build(1);
- //执行rest请求,ResponseEntity封装了返回信息,若将getForEntity 替换成 getForObject,则不需要 ResponseEntity
- ResponseEntity
user = restTemplate.getForEntity(uri,User.class); - //打印返回信息
- System.out.printf("Response Status: {%s}"+"\n"+"Response Headers: {%s}"+"\n", user.getStatusCode(), user.getHeaders().toString());
- System.out.printf("Users: {%s}", user.getBody());
- }
通过RestTemplate 查询成功 👇

HTTP 请求方式更为常见,同时也更为稳定,本质上来说是模拟 HTTP 请求的形式,将请求信息最后封装在 HttpEntity
- @Test
- public void updateUser11(){
- //请求地址
- String url = "http://localhost:8080/web/body";
-
- // 请求头设置,x-www-form-urlencoded格式的数据
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
-
- //提交参数设置
- MultiValueMap
map = new LinkedMultiValueMap<>(); - map.add("id", 2L);
- map.add("name", "jack");
- map.add("age", 16);
- map.add("email", "yinyu@baomidou.com");
-
- // 组装请求体
- HttpEntity
> request = new HttpEntity<>(map, headers); -
- // 发送post请求,并打印结果,以String类型接收响应结果JSON字符串
- String result = restTemplate.postForObject(url, request, String.class);
- System.out.println(result);
- }
post 请求成功 👇,由于没用 postForEntity + ResponseEntity,所以直接输出内容

数据表更新成功 👇

主要是用到了 exchange + ParameterizedTypeReference,最后返回 User 列表
- @Test
- public void queryAllUser(){
- //构建 uri
- URI uri = UriComponentsBuilder.fromUriString("http://localhost:8080/web/allUser").build().toUri();
- //解析泛型对象
- ParameterizedTypeReference
> ptr = new ParameterizedTypeReference>() {};
- ResponseEntity
> userlist = restTemplate.exchange(uri, HttpMethod.GET,null,ptr);
- //打印返回信息
- System.out.printf("Response Status: {%s}"+"\n"+"Response Headers: {%s}"+"\n", userlist.getStatusCode(), userlist.getHeaders().toString());
- System.out.printf("Users: {%s}", userlist.getBody());
- }
返回成功👇

WebClient 是从 Spring WebFlux 5.0 版本开始提供的一个非阻塞的基于响应式编程的进行 Http 请求的客户端工具。它的响应式编程的基于 Reactor 的。WebClient 中提供了标准Http请求方式对应的 get、post、put、delete 等方法,可以用来发起相应的请求。
关于 Reactor 的相关内容-> Spring 05 :Project Reactor 响应式流框架
Ⅰ 创建 WebClient
WebClient.create()
WebClient.builder()
Ⅱ 发起请求
Ⅲ 获得结果
Ⅳ 处理 HTTP Status
Ⅴ 应答正文
还是一样,需要在先启动项目 👇,然后再对测试类进行测试

路径均在:src/test/java/com/visitwebdemo/webclientTests.java
返回的是 Mono 封装的 User ,然后对他做进一步处理,这是 Reactor 相关内容~
- //创建一个 webClient
- WebClient webClient = WebClient.create("http://localhost:8080");
-
- @Test
- public void queryOneUser(){
- Mono
mono = webClient.get()//创建一个get请求 - .uri("/web/query/1/") //也可写成 uri("/web/query/{userId}",1)
- .retrieve() // 获取结果 可以用exchange代替 返回的上一个 User
- .bodyToMono(User.class);//处理单个对象
- System.out.println(mono.block());
- }
查询成功 👇

- @Test
- public void queryAllUser(){
- Flux
flux = webClient.get()//创建一个get请求 - .uri("/web/allUser/")
- .retrieve() // 获取结果 可以用exchange代替 返回的上一个 User 的list
- .bodyToFlux(User.class);//处理多个对象 即多组数据
- flux.toStream().forEach(System.out::println);
- }
查询成功 👇

- @Test
- public void updateUser(){
- MultiValueMap
map = new LinkedMultiValueMap<>(); - map.add("id", 2L);
- map.add("name", "jack");
- map.add("age", 18);
- map.add("email", "yinyu@baomidou.com");
-
- Mono
mono = webClient.post()//创建一个get请求 - .uri("/web/body")
- .body(BodyInserters.fromValue(map))
- .retrieve() // 获取结果 可以用exchange代替 返回的上一个 User 的list
- .bodyToMono(Boolean.class);//处理多个对象 即多组数据
- System.out.println(mono.block());
- }
未执行前:

执行后,可以看到 age 变更 👇 ,说明执行 post 请求成功

控制台输出:
