【Java开发】 Spring 08 :访问 Web 资源( 借助 RestTemplate 或 WebClient )

news2024/11/15 13:21:48

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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/61571.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Rust权威指南之编写自动化测试

一. 简述 虽然Rust的类型系统为我们提供了相当多的安全保障&#xff0c;但是还是不足以防止所有的错误。因此&#xff0c;Rust在语言层面内置了编写测试代码、执行自动化测试任务的功能。 测试是一门复杂的技术&#xff0c;本章覆盖关于如何编写优秀测试的每一个细节&#xf…

[LeetCode周赛复盘] 第 322 场周赛20221204

[LeetCode周赛复盘] 第 322 场周赛20221204 一、本周周赛总结二、 [Easy] 6253. 回环句1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6254. 划分技能点相等的团队1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6255. 两个城市间路径的最小分数1. 题目描述2. 思路分析3. 代…

细粒度图像分类论文研读-2017

文章目录Higher-order Integration of Hierarchical Convolutional Activations for Fine-grained Visual Categorization(by end-to-end feature encoding)AbstractIntroduction关于核关于多尺度Kernelized convolutional activationsMatching kernel and polynomial predicto…

秒懂数据结构之Map _ Set ,竟如此简单

Map、Set 文章目录 前言一、Map、Set的初步理解二、Map、Set的CURD方法的实现三、Map、Set的遍历总结前言 Set和Map天然就是高效搜索/查找的语义在这里我为什么将这两个集合分别列举比较呢&#xff1f;希望通过我的这篇博客可以增进大家对Map和Set的认识&#xff01;一、Map、…

[附源码]Python计算机毕业设计Django汽车美容店管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

计算卫星高度角、方位角

最小二乘定权、电离层对流层改正&#xff0c;都需要卫星的高度角、方位角。本章将介绍求解完卫星的地固坐标系的位置后&#xff0c;如何求解卫星的高度角、方位角。 卫星位置求解请参考之前的博客&#xff1a;卫星位置解算原理与程序设计 参考书籍&#xff1a;黄丁发&#xff0…

读<算法图解><笔记摘录>

从很多途径当中,看到过这本书的知识点,是一本很有趣的算法入门书籍,最近花费了几天的时间将其阅读完,总想着总结一下这本书的算法知识点,分享给大家,也让自己掌握地更加踏实一点. 算法:一组完成任何任务的指令 算法这玩意,在保证满足条件,并且不浪费内存的情况下,要尽可能速度…

18.定位元素练习-淘宝网

注意&#xff1a; 如果一个盒子定位元素属性既有left又有right,则会执行left属性。 既有top又有bottom&#xff0c;会执行top <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compa…

五子棋游戏AI智能算法设计

五子棋游戏C语言AI智能算法设计 近来发现编制五子棋游戏很有趣&#xff0c;尤其是AI智能算法很烧脑。网上介绍有什么贪心算法&#xff0c;剪枝算法&#xff0c;博弈树算法等等&#xff0c;不一而足。 对于人机对战的电脑智能应子算法&#xff0c;参阅很多五子棋书籍棋谱和五…

有序Map集合:LinkedHashMap和TreeMap该如何选用

文章目录前言一、为什么HashMap是无序的二、LinkedHashMap如何保证有序性三、TreeMap的底层原理四、LinkedHashMap和TreeMap比较总结前言 为什么HashMap是无序的&#xff1f;有序的Map集合有哪些&#xff1f;LinkedHashMap和TreeMap都是有序的Map集合&#xff0c;他们有什么区…

智能优化算法期末复习(更新ing)

目录 一、GA遗传算法 二、ACO蚁群算法 三、PSO粒子群算法 四、SA模拟退火算法 五、ABC人工蜂群算法 六、综合 一、GA遗传算法 1.运算流程 2.遗传算法适应值分配策略&#xff08;基于目标函数的直接分配、基于排名的分配&#xff09; 3.遗传算法在二进制问题&#xff08;如0…

Windows OpenGL ES 图像绿幕抠图

目录 一.OpenGL ES 图像绿幕抠图 1.原始图片2.效果演示 二.OpenGL ES 图像绿幕抠图源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基础…

Java开发必须掌握的运维知识 (十)-- Docker集群自动化部署管理:Kubernetes快速入门

一、什么是Kubernetes Kubernetes(K8S)是Google在2014年发布的一个开源项目&#xff0c;用于自动化容器化应用程序的部署、扩展和管理。 Kubernetes通常结合docker容器工作&#xff0c;并且整合多个运行着docker容器的主机集群。 Kubernetes官网地址 二、Kubernetes相关特性 …

JavaWeb_第3章_HTTPTomcatServlet

JavaWeb_第3章_HTTP&Tomcat&Servlet 文章目录JavaWeb_第3章_HTTP&Tomcat&Servlet1&#xff0c;Web概述1.1 Web和JavaWeb的概念1.2 JavaWeb技术栈1.2.1 B/S架构1.2.2 静态资源1.2.3 动态资源1.2.4 数据库1.2.5 HTTP协议1.2.6 Web服务器1.3 Web核心课程安排2, HT…

vue中,页面布局之使用vue-splitpane实现窗格的拆分和调节,类似于flex布局

vue中&#xff0c;页面布局之使用vue-splitpane实现窗格的拆分和调节&#xff0c;类似于flex布局 1、基本介绍 npm地址&#xff1a;https://www.npmjs.com/package/vue-splitpane 安包 npm install vue-splitpane注册 main.js import splitPane from vue-splitpane // 注…

土豆清洗去皮机设计

目 录 摘 要 I Abstract II 1绪论 1 1.1选题背景及意义 1 1.2国内外研究现状 1 1.2.1国内外现状 2 1.2.2国外研究现状 2 1.3 发展趋势 3 1.4研究内容及方法 4 2毛刷式土豆清洗去皮机总体设计 5 2.1毛刷式土豆清洗去皮机的构造及工作原理 5 2.2 土豆擦皮机的相关计算 6 2.2.1设计…

动手学深度学习(1)—— 基础知识

文章目录一、基本概念1.1 关键组件数据模型目标函数优化算法1.2 各种机器学习问题监督学习无监督学习强化学习1.3 神经网络的特点二、预备知识2.1 数据操作入门运算符广播机制索引和切片节省内存转换为其他python 对象2.2 数据预处理读取数据集处理缺失的数据2.3 线性代数标量向…

WPS通过“文档部件”的“域”设置图、表和公式的自动序列号

写文档时&#xff0c;当有多张图片、多个表格和多个公式需要编号时&#xff0c;可以通过设置自动序列号&#xff0c;实现一定程度的自动编号和任意位置插入后随时更新序号。具体操作如下 ​ 1. 图的设置 在WPS中&#xff0c;首先设置好一张图片的格式后&#xff0c;对于需要设…

【并发】J.U.C线程池

线程池 经历了Java内存模型、JUC基础之AQS、CAS、Lock、并发工具类、并发容器、阻塞队列、atomic类后&#xff0c;我们开始JUC的最后一部分&#xff1a;线程池。 线程池的优势 为什么多线程会带来性能问题 多线程的性能问题&#xff0c;分为两类&#xff0c;一类是线程本身…

从今天起真正释放创造力——亚马逊云科技re:Invent

对于开发者而言&#xff0c;成就感来自于每一次敲下代码后可实现的创造力&#xff0c;而不是把时间和精力消耗在写千篇一律又无法复用的“胶水”代码&#xff0c;或是在越来越复杂的软件栈面前&#xff0c;疲惫地写业务流程并尽量减少Bug。更加不堪的是&#xff0c;有时仅仅是因…