【springboot】RestTemplate的使用、格式转换、异常处理、拦截器,

RestTemplate提供了一个基于Http客户端库(HttpClient,OkHttp等)的高层次API,并不是重复制造轮子。

RestTemplate提供了常见的REST请求方案的模版,例如GET请求、POST请求、PUT请求、DELETE请求以及一些通用的请求执行方法exchange以及execute。

RestTemplate已处于维护状态,官方推荐使用WebClient。

RestTemplate继承自InterceptingHttpAccessor并且实现了RestOperations接口,其中RestOperations接口定义了基本的RESTful操作,这些操作在RestTemplate中都得到了实现。

RestTemplate的核心API

  • getForObject:发送GET请求,直接返回一个对象
  • getForEntity:发送GET请求,返回ResponseEntity (包含status, headers, and body)
  • headForHeaders:指定header,返回HttpHeaders对象
  • postForLocation:返回URI
  • postForObject:发送POST请求,直接返回一个对象
  • postForEntity:发送POST请求,返回ResponseEntity (包含status, headers, and body)
  • put:发送PUT请求
  • delete:发送Delete请求
  • optionsForAllow:发送OPTIONS请求
  • exchange:提供更灵活的方式去发送请求,请求参数RequestEntity(包含HTTP方式,请求头,请求体),返回ResponseEntity,支持泛型
  • execute:在exchange的基础上,可以通过callback来对请求和响应进行处理

RestTemplate的创建

RestTemplate底层默认使用JDK自带的HttpURLConnection来处理请求的。我们可以使用其他HTTP客户端库来处理请求,只需要实现ClientHttpRequestFactory。

目前官方支持的HTTP客户端库有:

  • Apache HttpComponents:HttpComponentsClientHttpRequestFactory
  • Netty:Netty4ClientHttpRequestFactory
  • OkHttp:OkHttp3ClientHttpRequestFactory

例如使用Apache HttpComponents,可以这样创建RestTemplate:

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());

URIs

Restful风格的请求URL上面通常会带有变量。

RestTemplate可以使用可变数组来处理URL上面的变量:

String result = restTemplate.getForObject("https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");

也可以使用MAP来处理URL上面的变量,MAP中的key需要与URL中的占位符的变量一致:

Map<String, String> vars = Collections.singletonMap("hotel", "42");String result = restTemplate.getForObject("https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);

注意URI会自动进行编码,例如:

restTemplate.getForObject("https://example.com/hotel list", String.class);// Results in request to "https://example.com/hotel%20list"

可以指定RestTemplate的uriTemplateHandler属性来自定义URI如何进行编码,另外RestTemplate的方法也支持传入URI类型的参数。

RestTemplate的简单使用

GET

发送简单的GET请求,URL支持带参数。

@GetMapping("get")
public String getUser() {String url = "http://localhost:8888/rest/user/{id}";RestTemplate restTemplate = new RestTemplate();String result = restTemplate.getForObject(url, String.class, 1);log.info("getForObject: {}", result);HashMap<String, Long> map = new HashMap<>();map.put("id", 1L);String result2 = restTemplate.getForObject(url, String.class, map);log.info("getForObject2: {}", result2);URI uri = UriComponentsBuilder.fromUriString(url).build(1);ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);log.info("getForEntity status: {}", responseEntity.getStatusCode());log.info("getForEntity header: {}", responseEntity.getHeaders());log.info("getForEntity body: {}", responseEntity.getBody());return result;
}

POST

发送POST请求。

@GetMapping("post")
public R<Void> postUser() {String url = "http://localhost:8888/rest/user";RestTemplate restTemplate = new RestTemplate();User user = new User();user.setName("jj");user.setAge(16);String result = restTemplate.postForObject(url, user, String.class);log.info("postForObject: {}", result);ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, user, String.class);log.info("postForEntity: {}", responseEntity);return R.ok();
}

PUT

发送PUT请求。

@GetMapping("put")
public R<Void> putUser() {String url = "http://localhost:8888/rest/user";RestTemplate restTemplate = new RestTemplate();User user = new User();user.setName("jj");user.setAge(16);restTemplate.put(url, user);log.info("put: {}", user);return R.ok();
}

DELETE

发送DELETE请求

@GetMapping("delete")
public R<Void> deleteUser() {String url = "http://localhost:8888/rest/user/{id}";RestTemplate restTemplate = new RestTemplate();restTemplate.delete(url, 1);return R.ok();
}

指定Header

可以使用HttpEntity来指定请求Header头中的参数,也可以使用RequestEntity,RequestEntity是HttpEntity的子类。

可以从ResponseEntity中获取响应的Header头中的参数。

@GetMapping("header")
public R<Void> header() {String url = "http://localhost:8888/rest/user";RestTemplate restTemplate = new RestTemplate();User user = new User();user.setName("jj");user.setAge(16);MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();paramMap.add("token", "2yy01x90x2xxx25");// set headersHttpHeaders headers = new HttpHeaders();HttpEntity<User> httpEntity = new HttpEntity<>(user, headers);ResponseEntity<String> response2 = restTemplate.postForEntity(url, httpEntity, String.class);log.info("postForEntity: {}", response2.getBody());// 使用exchange请求接口ResponseEntity<String> response3 = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);log.info("exchange: {}", response3.getBody());return R.ok();
}

返回值支持泛型

可以使用ParameterizedTypeReference来支持泛型。

@GetMapping("generic")
public R<User> generic() {String url = "http://localhost:8888/rest/user/{id}";ParameterizedTypeReference<R<User>> parameterizedTypeReference = new ParameterizedTypeReference<R<User>>() {};RestTemplate template = new RestTemplate();ResponseEntity<R<User>> responseEntity = template.exchange(url, HttpMethod.GET, null, parameterizedTypeReference, 1);return responseEntity.getBody();
}

消息内容的转换

RestTemplate的方法中能够直接传入对象或者返回对象,这是因为底层使用了HttpMessageConverter进行了转换。

常用的HttpMessageConverter有以下几个:

  • StringHttpMessageConverter:将请求和响应当成字符串来处理,支持Content-Typetext/plain
  • MappingJackson2HttpMessageConverter:使用Jackson的ObjectMapper将请求和响应当成JSON对象来处理。

如果想修改MappingJackson2HttpMessageConverter的序列化或者反序列化方式,可以自定义ObjectMapper来实现:

RestTemplate template = new RestTemplate();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
template.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter(objectMapper)));

异常处理

RestTemplate发起请求时如果发生错误默认就会直接抛出异常,而不会往下执行,所以无法在代码中直接根据ResponseEntity的status来判断接口是否响应成功,例如下面的代码就会直接抛出异常:

@GetMapping("exception")
public String exception() {String url = "http://localhost:8888/rest/user11";RestTemplate template = new RestTemplate();ResponseEntity<String> responseEntity = template.getForEntity(url, String.class);log.info("responseEntity status: {}", responseEntity.getStatusCode());return responseEntity.getBody();
}

运行结果如下:

2023-07-19 15:50:49.425 ERROR 15268 --- [nio-8888-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException$NotFound: 404 : "{"timestamp":"2023-07-19T07:50:49.225+00:00","status":404,"error":"Not Found","path":"/rest/user11"}"] with root cause

RestTemplate支持设置ResponseErrorHandler来处理异常,默认是使用DefaultResponseErrorHandler,DefaultResponseErrorHandler的处理就是状态码不是20X都会抛出异常:

protected boolean hasError(int unknownStatusCode) {HttpStatus.Series series = HttpStatus.Series.resolve(unknownStatusCode);return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR);
}@Override
public void handleError(ClientHttpResponse response) throws IOException {HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());if (statusCode == null) {byte[] body = getResponseBody(response);String message = getErrorMessage(response.getRawStatusCode(),response.getStatusText(), body, getCharset(response));throw new UnknownHttpStatusCodeException(message,response.getRawStatusCode(), response.getStatusText(),response.getHeaders(), body, getCharset(response));}handleError(response, statusCode);
}private String getErrorMessage(int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset) {String preface = rawStatusCode + " " + statusText + ": ";if (ObjectUtils.isEmpty(responseBody)) {return preface + "[no body]";}charset = (charset != null ? charset : StandardCharsets.UTF_8);String bodyText = new String(responseBody, charset);bodyText = LogFormatUtils.formatValue(bodyText, -1, true);return preface + bodyText;
}protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {String statusText = response.getStatusText();HttpHeaders headers = response.getHeaders();byte[] body = getResponseBody(response);Charset charset = getCharset(response);String message = getErrorMessage(statusCode.value(), statusText, body, charset);switch (statusCode.series()) {case CLIENT_ERROR:throw HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset);case SERVER_ERROR:throw HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset);default:throw new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset);}
}

自定义ResponseErrorHandler进行异常处理:

@GetMapping("exception2")
public R<Void> exception2() {String url = "http://localhost:8888/rest/user11";RestTemplate template = new RestTemplate();template.setErrorHandler(new ResponseErrorHandler() {@Overridepublic boolean hasError(ClientHttpResponse response) throws IOException {log.info("hasError: {}", response.getStatusCode());return false;}@Overridepublic void handleError(ClientHttpResponse response) throws IOException {// hasErrorlog.info("handleError: {}", response.getStatusCode());}});ResponseEntity<String> responseEntity = template.getForEntity(url, String.class);log.info("responseEntity status: {}", responseEntity.getStatusCode());if(responseEntity.getStatusCode().is2xxSuccessful()) {return R.ok();}return R.fail();
}

拦截请求

RestTemplate支持添加拦截器ClientHttpRequestInterceptor在请求前对请求参数进行处理,在请求后对响应进行处理。

例如可以使用ClientHttpRequestInterceptor对URL进行重写,从注册中心拉取配置实现负载均衡。可以使用ClientHttpRequestInterceptor对Header增加全局TraceId参数,对请求进行链路跟踪定位。

下面通过拦截器来实现负载均衡。

String[] loabancerhost = {"https://www.baidu.com", "https://www.sina.com.cn"};
AtomicInteger times = new AtomicInteger(0);
@GetMapping("intercept")
public String intercept() {String url = "http://test";RestTemplate restTemplate = new RestTemplate();restTemplate.setInterceptors(Collections.singletonList((request, body, execution) -> {log.info("请求开始");int length = loabancerhost.length;int i = times.getAndIncrement() % length;// 因为 HttpRequest 和 URI 不提供修改功能,因此需要借助 HttpRequestWrapper 对request进行包装Request newRequest = new Request(request, loabancerhost[i]);return execution.execute(newRequest, body);}));ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);log.info("intercept: {}", exchange);return exchange.getBody();
}private static class Request extends HttpRequestWrapper {private String url;public Request(HttpRequest request,String url) {super(request);this.url = url;}@Overridepublic URI getURI() {try {return new URI(url);} catch (URISyntaxException e) {}return null;}
}

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

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

相关文章

整数拆分(力扣)动态规划 JAVA

给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 示例 1: 输入: n 2 输出: 1 解释: 2 1 1, 1 1 1。 示例 2: 输入: n 10 输出: 36 解释: 10 3 3 4…

Python应用实例(二)数据可视化(二)

数据可视化&#xff08;二&#xff09; 1.随机漫步1.1 创建RandomWalk类1.2 选择方向1.3 绘制随机漫步图1.4 模拟多次随机漫步1.5 设置随机漫步图的样式 1.随机漫步 使用Python来生成随机漫步数据&#xff0c;再使用Matplotlib以引人瞩目的方式将这些数据呈现出来。随机漫步是…

使用 YOLOv8 和 Streamlit 构建实时对象检测和跟踪应用程序:第 1 部分-介绍和设置

示例:图像上的对象检测 介绍 实时视频中的目标检测和跟踪是计算机视觉的一个重要领域,在监控、汽车和机器人等各个领域都有广泛的应用。 由于需要能够识别和跟踪对象、确定其位置并对它们进行实时分类的自动化系统,对视频帧中的实时对象检测和跟踪的需求日益增加。 在这…

MinIO:开源对象存储解决方案

MinIO是一款开源的云原生对象存储解决方案&#xff0c;旨在提供高性能、可扩展和持久化存储服务。它兼容Amazon S3 API&#xff0c;可以轻松地集成到现有的应用程序中&#xff0c;为用户提供可靠的对象存储和数据管理。本文将介绍MinIO的基本概念、架构设计以及常见的应用场景&…

Java反射

Java中的字节码&#xff1a;Java源代码经过虚拟机编译器编译后产生的文件&#xff08;即扩展为.class的文件&#xff09;&#xff0c;它不面向任何特定的处理器&#xff0c;只面向虚拟机。 1.反射的定义 反射机制 JAVA反射机制是在运行状态中&#xff0c;对于任意一个类&…

GitUI汉化

1.下载汉化文件 下载地址 备用下载地址 https://files.cnblogs.com/files/chenghu/git-gui-zh-master.zip https://files.cnblogs.com/files/chenghu/git-gui-zh-master.zip 2.找到git安装路径 C:\Program Files\Git\mingw64\share\git-gui\lib 3.解压出1下载的文件 复制粘…

VM(CentOS7安装和Linux连接工具以及换源)

目录 一、Linux意义 二、安装VMWare 三、centos7安装 1、正式安装CentOS7&#xff1a; 2、安装不了的解决方案 2.1常见问题——虚拟机开机就黑屏的完美解决办法 3、查看、设置IP地址 ① 查看ip地址&#xff1a;ip addr 或者 ifconfig&#xff0c; 注意与windows环境的区别…

了解区块链---一个去中心化技术

1.假如你是从事区块链的高端技术人员&#xff0c;我从来没有接触过区块链&#xff0c;请你为我讲解下他的概率、原理、应用&#xff1f; 概念&#xff1a; 区块链是一种去中心化的分布式账本技术&#xff0c;它是由一系列区块组成的链式结构&#xff0c;每个区块包含一些交易数…

宋浩线性代数笔记(一)行列式的计算

本帖更新b站宋浩老师的线代网课笔记&#xff0c;内容较为细致详细&#xff0c;参考书用的是科学出版社的第三版&#xff0c;之后会附加同济出版社第六版的教材内容。 &#xff08;字不好看大家将就看吧QAQ&#xff09;

当DevOps遇到AI,黑马迎来3.0时代丨IDCF

随着GhatGPT的爆火&#xff0c;人工智能和研发效能&#xff0c;无疑成为了2023的两个最重要的关键词。大规模语言模型LLM和相关应用的快速发展正在对研发团队的工作方式产生深远影响&#xff0c;这几乎象征着新的生产力革命的到来。 那么&#xff0c;作为一名工程师&#xff0…

java密码强度校验

一、代码 Testpublic void test(){//包含数字、大小写字母&#xff0c;长度10-20位 String regular "^(?.*\\d)(?.*[a-z])(?.*[A-Z]).{10,20}$";String example1 "1234567891";System.out.println(example1.matches(regular)); //falseString exa…

React(2)

题外话&#xff1a;vscode有个插件可以很方便的快速写代码 输入rcc回车 1.组件嵌套 import React, { Component } from reactclass Navbar extends Component{render(){return <div>Navbar</div>} }const Swiper()>{return <div>Swiper</div> }cons…

数据库信息速递 MONGODB 6.0 的新特性,更多的查询函数,加密查询,与时序数据集合 (译)...

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到3群&#xff08;共…

linux安装redis

背景 项目需要 安装redis&#xff0c;不使用root用户,假设使用redis用户。 root准备 安装依赖 yum install gcc安装目录 mkdir /usr/local/redis授权安装目录 注意&#xff0c;先要新建用户 chown -R redis:redis /usr/local/redis 安装 切换用户 下载 下载包地址 h…

Spring实现文件上传,文件上传

第一步&#xff1a;创建jsp文件 创建form表单 提交文件是post 文件上传的表单 服务端能不能获得数据&#xff0c;能 实现单文件上传的步骤&#xff1a; 导入相应的坐标&#xff1a;在pom.xml文件中进行导入 再导入这份&#xff1a; 第二步&#xff0c;在spring-MVC的上传中去配…

中文分词入门:使用IK分词器进行文本分词(附Java代码示例)

1. 介绍 中文分词是将连续的中文文本切分成一个个独立的词语的过程&#xff0c;是中文文本处理的基础。IK分词器是一个高效准确的中文分词工具&#xff0c;采用了"正向最大匹配"算法&#xff0c;并提供了丰富的功能和可定制选项。 2. IK分词器的特点 细粒度和颗粒…

Zookeeper简介及核心概念

一、Zookeeper简介 二、Zookeeper设计目标 三、核心概念 3.1 集群角色 3.2 会话 3.3 数据节点 3.4 节点信息 3.5 Watcher 3.6 ACL 四、ZAB协议 4.1 ZAB协议与数据一致性 4.2 ZAB协议的内容 五、Zookeeper的典型应用场景 5.1数据的发布/订阅 5.2 命名服务 5.3 Master选举 5.4 分…

2023云曦期末复现

目录 WEB sign SSTI serialize WEB sign 有10000个 进行bp爆破 能发现 410 和 414长度 还有 420 410 414存在16进制的字符 拼凑出来为 \x66\x6c\x61\x67\x7b\x61\x63\x63\x39\x39\x66\x39\x30\x34\x66\x30\x65\x61\x66\x61\x34\x31\x63\x30\x36\x34\x33\x36\x38\x31\x3…

一、对象的概念(3)

本章概要 单继承结构集合对象创建与生命周期异常处理其它 单继承结构 自从 C 引入以来&#xff0c;一个 OOP 问题变得尤为突出&#xff1a;是否所有的类都应该默认从一个基类继承呢&#xff1f;这个答案在 Java 中是肯定的&#xff08;实际上&#xff0c;除 C 以外的几乎所有…

JavaWeb_SpringCloud微服务_Day1-eureka, ribbon, nacos

JavaWeb_SpringCloud微服务_Day1-eureka, ribbon, nacos 认识微服务微服务技术对比 分布式服务架构案例远程调用 eureka注册中心原理搭建EurekaServer服务注册服务发现 Ribbon负载均衡修改负载均衡饥饿加载 nacos注册中心快速入门eureka和nacos对比 来源 认识微服务 微服务技术…