web 资源就是运行在服务器上的资源,比如放到 web 下的页面 js 文件、图片、css等,web资源分为静态web资源和动态web资源两类,接下来访问的就是动态资源(页面返回的数据是动态的,由后端程序产生),本文主要借助 RestTemplate 和 WebClient 两个工具。
目录
1 项目初始化(实现 MVC)
① Spring Boot 项目初始化
② 添加 Spring Web(最关键)等依赖
③ 导入 mybatis-plus 依赖
④ 连接数据库配置
⑤ 编写数据库对应的实体类
1.2 编写 Dao层、service层和 controller层
① Dao 层的 Mapper 接口
② Service层的 Iservice 接口和实现类
③ 编写 controller 层的接口
2 通过 RestTemplate 访问
2.1 常用请求方法
2.2 URI 的构造
① 普通构造 URI
② 构造含有变量值的 URI
③ 构造指向 Controller 的 URI
2.3 RestTemplate 代码实现
① getForObject() / getForEntity()
② postForObject() /postForEntity()—HTTP请求
③ exchange 实现泛型
3 通过 WebClient 访问
3.1 基本用法
2.3 WebClient 代码实现
① get -- 返回 user
② get -- 返回 user 列表
③ post
项目源码:尹煜 / visitwebdemo · GitCode
1 项目初始化(实现 MVC)
因为文章尽可能想写的详尽基础一些,所以内容可能会有点多,熟练者可直接看2、3章内容
因为访问 Web 资源的前提是存在 Web 资源可供访问,因此本文的逻辑是在本地创建一个 Web 环境(写 controller),然后由 test 类进行访问测试。
① Spring Boot 项目初始化

② 添加 Spring Web(最关键)等依赖
Spring Boot 版本是 2.7.6 ,建议将版本控制在 2-3 之间,超出范围的话会容易产生兼容问题

③ 导入 mybatis-plus 依赖
路径:pom.xml
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>④ 连接数据库配置
前提是连接的数据库存在与实体类相对应的数据表,数据表初始搭建详解在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;
}1.2 编写 Dao层、service层和 controller层
① Dao 层的 Mapper 接口
路径:src/main/java/com/visitwebdemo/mapper/UserMapper.java
package com.visitwebdemo.mapper;
//在对应的接口上面继承一个基本的接口 BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
    //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);
    }
}② Service层的 Iservice 接口和实现类
编写实体类对应的 UserBaseService 接口
路径:src/main/java/com/visitwebdemo/service/UserBaseService.java
package com.visitwebdemo.service;
//如有需要用以重写IService里的抽象方法,如不需要重写也可去掉该文件,将IService<User>写在UserServiceImpl文件
public interface UserBaseService extends IService<User> {
}编写 Service 层的实现类,以下就是具体的增删改查操作 👇
路径:src/main/java/com/visitwebdemo/service/impl/UserServiceImpl.java
package com.visitwebdemo.service.impl;
@Service
public class UserServiceImpl  extends ServiceImpl<UserMapper, User> 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<User> 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<User> queryAllUser_ByMapper(){
        return userMapper.selectList(new QueryWrapper<>());//QueryWrapper没有任何条件
    }
}③ 编写 controller 层的接口
写了三个具有代表性的接口 👇
路径: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<User> 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);
    }
    
}
以上准备工作就完成了~
2 通过 RestTemplate 访问
RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,它提供了很多可以方便访问远程 http 服务的方法,这些方法可以帮助开发人员减少编写客户端代码的工作量。
2.1 常用请求方法
其实最主要还是 Get 和 Post 请求:
- GET请求:getForObject() / getForEntity()
- POST请求:postForObject() /postForEntity()
- PUT请求:put()
- DELETE请求:delete()
xxxForObject() 和 xxxForEntity() 二者区别主要在于:xxxForObject() 的返回值是HTTP协议的响应体;而 xxxForEntity() 返回的是 ResponseEntity(ResponseEntity是对HTTP响应的封装),除了包含响应体,还包含HTTP状态码、contentType、contentLength、Header等信息。
2.2 URI 的构造
发送请求需要携带 URI 👇,以下是几个常见的构造方式,UriComponentsBuilder 最为常用
① 普通构造 URI
URI uri = UriComponentsBuilder
        .fromUriString("http://localhost:8080/web/query")
        .build()
        .toUri();② 构造含有变量值的 URI
URI uri = UriComponentsBuilder
        .fromUriString("http://localhost:8080/web/query/{userId}")
        .build(1);③ 构造指向 Controller 的 URI
URI uri = MvcUriComponentsBuilder
        .fromMethodCall(MvcUriComponentsBuilder.on(UserController.class).allUser())
        .build()
        .toUri();④ 获取当前请求的URI
URI uri = ServletUriComponentsBuilder
        .fromCurrentRequest()
        .build()
        .toUri();2.3 RestTemplate 代码实现
注意由于是请求本地项目的 web 资源,因此需要在先启动项目 👇,然后再对测试类进行测试

路径均在:src/test/java/com/visitwebdemo/restemplateTests.java
① getForObject() / getForEntity()
//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> 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 查询成功 👇

② postForObject() /postForEntity()—HTTP请求
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<String, Object> map = new LinkedMultiValueMap<>();
    map.add("id", 2L);
    map.add("name", "jack");
    map.add("age", 16);
    map.add("email", "yinyu@baomidou.com");
    // 组装请求体
    HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(map, headers);
    // 发送post请求,并打印结果,以String类型接收响应结果JSON字符串
    String result = restTemplate.postForObject(url, request, String.class);
    System.out.println(result);
}post 请求成功 👇,由于没用 postForEntity + ResponseEntity,所以直接输出内容

数据表更新成功 👇

③ exchange 实现泛型
主要是用到了 exchange + ParameterizedTypeReference,最后返回 User 列表
@Test
public void queryAllUser(){
    //构建 uri
    URI uri = UriComponentsBuilder.fromUriString("http://localhost:8080/web/allUser").build().toUri();
    //解析泛型对象
    ParameterizedTypeReference<List<User>> ptr = new ParameterizedTypeReference<List<User>>() {};
    ResponseEntity<List<User>> 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());
}返回成功👇

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

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

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

③ post
@Test
public void updateUser(){
    MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
                map.add("id", 2L);
                map.add("name", "jack");
                map.add("age", 18);
                map.add("email", "yinyu@baomidou.com");
    Mono<Boolean> 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 请求成功

控制台输出:

参考文章
Web资源_你啊我啊你好的博客-CSDN博客_web资源
通过 RestTemplate 访问 Web 资源_L# S@的博客-CSDN博客

![[LeetCode周赛复盘] 第 322 场周赛20221204](https://img-blog.csdnimg.cn/13704aaa394e4710b5355955dff685a5.png)


![[附源码]Python计算机毕业设计Django汽车美容店管理系统](https://img-blog.csdnimg.cn/07ab0e30abf14c7496e562d0ca8a2715.png)














