目录
发送 Content-Type 为 application/x-www-form-urlencoded 的 POST 请求:
发送 Content-Type 为 application/json 的 POST 请求:
1.3 InterceptingHttpAccessor->getRequestFactory()
1.4 resttemplate->handleResponse()
1.5 HttpMessageConverterExtractor->extractData()
1.6 contentType与messageConverter之间的关系
1.1. 导入依赖:(RestTemplate集成在Web Start中)
1.3. 组件(自定义异常处理、interceptor拦截器、message转化器)
TrackLogClientHttpRequestInterceptor:
HeadClientHttpRequestInterceptor:
RestTemplate介绍
是
Spring用于同步client端的核心类,简化了与http服务的通信,并满足RestFul原则,程序代码可以给它提供URL,并提取结果。默认情况下,RestTemplate默认依赖jdk的HTTP连接工具。当然你也可以 通过setRequestFactory属性切换到不同的HTTP源,比如Apache HttpComponents、Netty和OkHttp。
RestTemplate能大幅简化了提交表单数据的难度,并且附带了自动转换JSON数据的功能,但只有理解了HttpEntity的组成结构(header与body),且理解了与uriVariables之间的差异,才能真正掌握其用法。
其中:
RestTemplate默认使用HttpMessageConverter实例将HTTP消息转换成POJO或者从POJO转换成HTTP消息。默认情况下会注册主mime类型的转换器,但也可以通过setMessageConverters注册自定义转换器。RestTemplate使用了默认的DefaultResponseErrorHandler,对40XBad Request或50Xinternal异常error等错误信息捕捉。RestTemplate还可以使用拦截器interceptor,进行对请求链接跟踪,以及统一head的设置。
RestTemplate 类是在 Spring Framework 3.0 开始引入的,这里我们使用的 Spring 版本为当前最新的 GA 版本 5.1.6。而在 5.0 以上,官方标注了更推荐使用非阻塞的响应式 HTTP 请求处理类 org.springframework.web.reactive.client.WebClient 来替代 RestTemplate,尤其是对应异步请求处理的场景上 。
RestTemplate 类提供的 API 有哪些,RestTemplate 提供了将近 30 个请求方法,其中多数是单个方法重载实现,这里我主要参考官方文档 rest-client-access 进行如下分类:
| 方法 | 解析 |
|---|---|
| delete() | 在特定的URL上对资源执行HTTP DELETE操作 |
| exchange() | 在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity |
| execute() | 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象 |
| getForEntity() | 发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所映射成的对象 |
| getForObject() | 发送一个HTTP GET请求,返回的请求体将映射为一个对象 |
| postForEntity() | POST 数据到一个URL,返回包含一个对象的ResponseEntity |
| postForObject() | POST 数据到一个URL,返回根据响应体匹配形成的对象 |
| headForHeaders() | 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头 |
| optionsForAllow() | 发送HTTP OPTIONS请求,返回对特定URL的Allow头信息 |
| postForLocation() | POST 数据到一个URL,返回新创建资源的URL |
| put() | PUT 资源到特定的URL |
RestTemplate简单使用
GET 请求
不带任何参数 的 GET 请求
// 一个不带任何参数 的 GET 请求
@Test
public void testGet_product1() {
String url = "http://localhost:8080/product/get_product1";
//方式一:GET 方式获取 JSON 串数据
String result = restTemplate.getForObject(url, String.class);
System.out.println("get_product1返回结果:" + result);
Assert.hasText(result, "get_product1返回结果为空");
//方式二:GET 方式获取 JSON 数据映射后的 Product 实体对象
Product product = restTemplate.getForObject(url, Product.class);
System.out.println("get_product1返回结果:" + product);
Assert.notNull(product, "get_product1返回结果为空");
//方式三:GET 方式获取包含 Product 实体对象 的响应实体 ResponseEntity 对象,用 getBody() 获取
ResponseEntity<Product> responseEntity = restTemplate.getForEntity(url, Product.class);
System.out.println("get_product1返回结果:" + responseEntity);
Assert.isTrue(responseEntity.getStatusCode().equals(HttpStatus.OK), "get_product1响应不成功");
}
带有参数的 GET 请求
@Test
public void testGet_product2() {
String url = "http://localhost:8080/product/get_product2/id={id}";
//方式一:将参数的值存在可变长度参数里,按照顺序进行参数匹配
ResponseEntity<Product> responseEntity = restTemplate.getForEntity(url, Product.class, 101);
System.out.println(responseEntity);
Assert.isTrue(responseEntity.getStatusCode().equals(HttpStatus.OK), "get_product2 请求不成功");
Assert.notNull(responseEntity.getBody().getId(), "get_product2 传递参数不成功");
//方式二:将请求参数以键值对形式存储到 Map 集合中,用于请求时URL上的拼接
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("id", 101);
Product result = restTemplate.getForObject(url, Product.class, uriVariables);
System.out.println(result);
Assert.notNull(result.getId(), "get_product2 传递参数不成功");
}
getForEntity()方法
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables){}
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables){}
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType){}
与getForObject()方法不同的是返回的是
ResponseEntity对象,如果需要转换成pojo,还需要json工具类的引入,这个按个人喜好用。不会解析json的可以百度FastJson或者Jackson等工具类。然后我们就研究一下ResponseEntity下面有啥方法。
ResponseEntity、HttpStatus、BodyBuilder结构
ResponseEntity.java
public HttpStatus getStatusCode(){}
public int getStatusCodeValue(){}
public boolean equals(@Nullable Object other) {}
public String toString() {}
public static BodyBuilder status(HttpStatus status) {}
public static BodyBuilder ok() {}
public static <T> ResponseEntity<T> ok(T body) {}
public static BodyBuilder created(URI location) {}
...
HttpStatus.java
public enum HttpStatus {
public boolean is1xxInformational() {}
public boolean is2xxSuccessful() {}
public boolean is3xxRedirection() {}
public boolean is4xxClientError() {}
public boolean is5xxServerError() {}
public boolean isError() {}
}
BodyBuilder.java
public interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
//设置正文的长度,以字节为单位,由Content-Length标头
BodyBuilder contentLength(long contentLength);
//设置body的MediaType 类型
BodyBuilder contentType(MediaType contentType);
//设置响应实体的主体并返回它。
<T> ResponseEntity<T> body(@Nullable T body);
}
可以看出来,ResponseEntity包含了HttpStatus和BodyBuilder的这些信息,这更方便我们处理response原生的东西。
POST 请求
发送 Content-Type 为 application/x-www-form-urlencoded 的 POST 请求:
@Test
public void testPost_product1() {
String url = "http://localhost:8080/product/post_product1";
Product product = new Product(201, "Macbook", BigDecimal.valueOf(10000));
// 设置请求的 Content-Type 为 application/x-www-form-urlencoded
MultiValueMap<String, String> header = new LinkedMultiValueMap();
header.add(HttpHeaders.CONTENT_TYPE, (MediaType.APPLICATION_FORM_URLENCODED_VALUE));
//方式二: 将请求参数值以 K=V 方式用 & 拼接,发送请求使用
String productStr = "id=" + product.getId() + "&name=" + product.getName() + "&price=" + product.getPrice();
HttpEntity<String> request = new HttpEntity<>(productStr, header);
ResponseEntity<String> exchangeResult = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
System.out.println("post_product1: " + exchangeResult);
Assert.isTrue(exchangeResult.getStatusCode().equals(HttpStatus.OK), "post_product1 请求不成功");
//方式一: 将请求参数以键值对形式存储在 MultiValueMap 集合,发送请求时使用
MultiValueMap<String, Object> map = new LinkedMultiValueMap();
map.add("id", (product.getId()));
map.add("name", (product.getName()));
map.add("price", (product.getPrice()));
HttpEntity<MultiValueMap> request2 = new HttpEntity<>(map, header);
ResponseEntity<String> exchangeResult2 = restTemplate.exchange(url, HttpMethod.POST, request2, String.class);
System.out.println("post_product1: " + exchangeResult2);
Assert.isTrue(exchangeResult.getStatusCode().equals(HttpStatus.OK), "post_product1 请求不成功");
}
发送 Content-Type 为 application/json 的 POST 请求:
@Test
public void testPost_product2() {
String url = "http://localhost:8080/product/post_product2";
// 设置请求的 Content-Type 为 application/json
MultiValueMap<String, String> header = new LinkedMultiValueMap();
header.put(HttpHeaders.CONTENT_TYPE, Arrays.asList(MediaType.APPLICATION_JSON_VALUE));
// 设置 Accept 向服务器表明客户端可处理的内容类型
header.put(HttpHeaders.ACCEPT, Arrays.asList(MediaType.APPLICATION_JSON_VALUE));
// 直接将实体 Product 作为请求参数传入,底层利用 Jackson 框架序列化成 JSON 串发送请求
HttpEntity<Product> request = new HttpEntity<>(new Product(2, "Macbook", BigDecimal.valueOf(10000)), header);
ResponseEntity<String> exchangeResult = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
System.out.println("post_product2: " + exchangeResult);
Assert.isTrue(exchangeResult.getStatusCode().equals(HttpStatus.OK), "post_product2 请求不成功");
}
RestTemplate源码
1.1 默认调用链路
restTemplate进行API调用时,默认调用链:
###########1.使用createRequest创建请求########
resttemplate->execute()->doExecute()
HttpAccessor->createRequest()
//获取拦截器Interceptor,InterceptingClientHttpRequestFactory,SimpleClientHttpRequestFactory
InterceptingHttpAccessor->getRequestFactory()
//获取默认的SimpleBufferingClientHttpRequest
SimpleClientHttpRequestFactory->createRequest()
#######2.获取响应response进行处理###########
AbstractClientHttpRequest->execute()->executeInternal()
AbstractBufferingClientHttpRequest->executeInternal()
###########3.异常处理#####################
resttemplate->handleResponse()
##########4.响应消息体封装为java对象#######
HttpMessageConverterExtractor->extractData()
1.2 restTemplate->doExecute()
在默认调用链中,restTemplate 进行API调用都会调用 doExecute 方法,此方法主要可以进行如下步骤:
1)使用
createRequest创建请求,获取响应
2)判断响应是否异常,处理异常
3)将响应消息体封装为java对象
@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
try {
//使用createRequest创建请求
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
//获取响应response进行处理
response = request.execute();
//异常处理
handleResponse(url, method, response);
//响应消息体封装为java对象
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
}finally {
if (response != null) {
response.close();
}
}
}
1.3 InterceptingHttpAccessor->getRequestFactory()
在默认调用链中,
InterceptingHttpAccessor的getRequestFactory()方法中,如果没有设置interceptor拦截器,就返回默认的SimpleClientHttpRequestFactory,反之,返回InterceptingClientHttpRequestFactory的requestFactory,可以通过resttemplate.setInterceptors设置自定义拦截器interceptor。
//Return the request factory that this accessor uses for obtaining client request handles.
public ClientHttpRequestFactory getRequestFactory() {
//获取拦截器interceptor(自定义的)
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();
}
}
然后再调用SimpleClientHttpRequestFactory的createRequest创建连接:
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name());
if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
}
else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
}
}
1.4 resttemplate->handleResponse()
在默认调用链中,
resttemplate的handleResponse,响应处理,包括异常处理,而且异常处理可以通过调用setErrorHandler方法设置自定义的ErrorHandler,实现对请求响应异常的判别和处理。自定义的ErrorHandler需实现ResponseErrorHandler接口,同时Spring boot也提供了默认实现DefaultResponseErrorHandler,因此也可以通过继承该类来实现自己的ErrorHandler。
DefaultResponseErrorHandler默认对40XBad Request或50Xinternal异常error等错误信息捕捉。如果想捕捉服务本身抛出的异常信息,需要通过自行实现RestTemplate的ErrorHandler。
ResponseErrorHandler errorHandler = getErrorHandler();
//判断响应是否有异常
boolean hasError = errorHandler.hasError(response);
if (logger.isDebugEnabled()) {
try {
int code = response.getRawStatusCode();
HttpStatus status = HttpStatus.resolve(code);
logger.debug("Response " + (status != null ? status : code));
}catch (IOException ex) {
// ignore
}
}
//有异常进行异常处理
if (hasError) {
errorHandler.handleError(url, method, response);
}
}
1.5 HttpMessageConverterExtractor->extractData()
在默认调用链中,
HttpMessageConverterExtractor的extractData中进行响应消息体封装为java对象,就需要使用message转换器,可以通过追加的方式增加自定义的messageConverter:先获取现有的messageConverter,再将自定义的messageConverter添加进去。
根据restTemplate的setMessageConverters的源码可得,使用追加的方式可防止原有的messageConverter丢失,源码:
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
//检验
validateConverters(messageConverters);
// Take getMessageConverters() List as-is when passed in here
if (this.messageConverters != messageConverters) {
//先清除原有的messageConverter
this.messageConverters.clear();
//后加载重新定义的messageConverter
this.messageConverters.addAll(messageConverters);
}
}
HttpMessageConverterExtractor的extractData源码:
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
//获取到response的ContentType类型
MediaType contentType = getContentType(responseWrapper);
try {
//依次循环messageConverter进行判断是否符合转换条件,进行转换java对象
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
//会根据设置的返回类型responseType和contentType参数进行匹配,选择合适的MessageConverter
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericMessageConverter =
(GenericHttpMessageConverter<?>) messageConverter;
if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
if (logger.isDebugEnabled()) {
ResolvableType resolvableType = ResolvableType.forType(this.responseType);
logger.debug("Reading to [" + resolvableType + "]");
}
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
}
}
if (this.responseClass != null) {
if (messageConverter.canRead(this.responseClass, contentType)) {
if (logger.isDebugEnabled()) {
String className = this.responseClass.getName();
logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
}
return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
}
}
}
}
.....
}
1.6 contentType与messageConverter之间的关系
在HttpMessageConverterExtractor的extractData方法中看出,会根据contentType与responseClass选择messageConverter是否可读、消息转换。关系如下:
| 类名 | 支持的JavaType | 支持的MediaType |
|---|---|---|
| ByteArrayHttpMessageConverter | byte[] | application/octet-stream, */* |
| StringHttpMessageConverter | String | text/plain, */* |
| ResourceHttpMessageConverter | Resource | */* |
| SourceHttpMessageConverter | Source | application/xml, text/xml, application/*+xml |
| AllEncompassingFormHttpMessageConverter | Map<K, List<?>> | application/x-www-form-urlencoded, multipart/form-data |
| MappingJackson2HttpMessageConverter | Object | application/json, application/*+json |
| Jaxb2RootElementHttpMessageConverter | Object | application/xml, text/xml, application/*+xml |
| JavaSerializationConverter | Serializable | x-java-serialization;charset=UTF-8 |
| FastJsonHttpMessageConverter | Object | */* |
springboot集成RestTemplate
根据上述源码的分析学习,可以轻松,简单地在项目进行对RestTemplate进行优雅地使用,比如增加自定义的异常处理、MessageConverter以及拦截器interceptor。本文使用示例demo,详情请查看接下来的内容。
1.1. 导入依赖:(RestTemplate集成在Web Start中)
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
1.2. RestTemplat配置:
- 使用
ClientHttpRequestFactory属性配置RestTemplat参数,比如ConnectTimeout,ReadTimeout;- 增加自定义的
interceptor拦截器和异常处理;- 追加
message转换器;- 配置自定义的异常处理.
@Configuration
public class RestTemplateConfig {
@Value("${resttemplate.connection.timeout}")
private int restTemplateConnectionTimeout;
@Value("${resttemplate.read.timeout}")
private int restTemplateReadTimeout;
@Bean
//@LoadBalanced
public RestTemplate restTemplate( ClientHttpRequestFactory simleClientHttpRequestFactory) {
RestTemplate restTemplate = new RestTemplate();
//配置自定义的message转换器
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
messageConverters.add(new CustomMappingJackson2HttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
//配置自定义的interceptor拦截器
List<ClientHttpRequestInterceptor> interceptors=new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(new HeadClientHttpRequestInterceptor());
interceptors.add(new TrackLogClientHttpRequestInterceptor());
restTemplate.setInterceptors(interceptors);
//配置自定义的异常处理
restTemplate.setErrorHandler(new CustomResponseErrorHandler());
restTemplate.setRequestFactory(simleClientHttpRequestFactory);
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory reqFactory= new SimpleClientHttpRequestFactory();
reqFactory.setConnectTimeout(restTemplateConnectionTimeout);
reqFactory.setReadTimeout(restTemplateReadTimeout);
return reqFactory;
}
}
1.3. 组件(自定义异常处理、interceptor拦截器、message转化器)
自定义
interceptor拦截器,实现ClientHttpRequestInterceptor接口
- 自定义
TrackLogClientHttpRequestInterceptor,记录resttemplate的request和response信息,可进行追踪分析;- 自定义
HeadClientHttpRequestInterceptor,设置请求头的参数。API发送各种请求,很多请求都需要用到相似或者相同的Http Header。如果在每次请求之前都把Header填入HttpEntity/RequestEntity,这样的代码会显得十分冗余,可以在拦截器统一设置。
TrackLogClientHttpRequestInterceptor:
/**
* @Date: 2019/10/25 22:48,记录resttemplate访问信息
* @Description: 记录resttemplate访问信息
*/
@Slf4j
public class TrackLogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
trackRequest(request,body);
ClientHttpResponse httpResponse = execution.execute(request, body);
trackResponse(httpResponse);
return httpResponse;
}
private void trackResponse(ClientHttpResponse httpResponse)throws IOException {
log.info("============================response begin==========================================");
log.info("Status code : {}", httpResponse.getStatusCode());
log.info("Status text : {}", httpResponse.getStatusText());
log.info("Headers : {}", httpResponse.getHeaders());
log.info("=======================response end=================================================");
}
private void trackRequest(HttpRequest request, byte[] body)throws UnsupportedEncodingException {
log.info("======= request begin ========");
log.info("uri : {}", request.getURI());
log.info("method : {}", request.getMethod());
log.info("headers : {}", request.getHeaders());
log.info("request body : {}", new String(body, "UTF-8"));
log.info("======= request end ========");
}
}
HeadClientHttpRequestInterceptor:
@Slf4j
public class HeadClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
log.info("#####head handle########");
HttpHeaders headers = httpRequest.getHeaders();
headers.add("Accept", "application/json");
headers.add("Accept-Encoding", "gzip");
headers.add("Content-Encoding", "UTF-8");
headers.add("Content-Type", "application/json; charset=UTF-8");
ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);
HttpHeaders headersResponse = response.getHeaders();
headersResponse.add("Accept", "application/json");
return response;
}
}
自定义异常处理
可继承DefaultResponseErrorHandler或者实现ResponseErrorHandler接口:
- 实现自定义
ErrorHandler的思路是根据响应消息体进行相应的异常处理策略,对于其他异常情况由父类DefaultResponseErrorHandler来进行处理。- 自定义
CustomResponseErrorHandler进行30x异常处理
CustomResponseErrorHandler:
/**
* @Description: 30X的异常处理
*/
@Slf4j
public class CustomResponseErrorHandler extends DefaultResponseErrorHandler {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = response.getStatusCode();
if(statusCode.is3xxRedirection()){
return true;
}
return super.hasError(response);
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = response.getStatusCode();
if(statusCode.is3xxRedirection()){
log.info("########30X错误,需要重定向!##########");
return;
}
super.handleError(response);
}
}
自定义message转化器
/**
* @Description: 将Content-Type:"text/html"转换为Map类型格式
*/
public class CustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
public CustomMappingJackson2HttpMessageConverter() {
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaType.TEXT_PLAIN);
mediaTypes.add(MediaType.TEXT_HTML); //加入text/html类型的支持
setSupportedMediaTypes(mediaTypes);// tag6
}
}
RestTemplate工具类
package com.fly.apigateway;
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;
/**
* @Title: restTemplateUtils 高性能请求http https
* @ClassName: com.fly.apigateway.RestTemplateUtils.java
* @Description:
*
* @Copyright 2016-2019 - Powered By 研发中心
* @author: 王延飞
* @date: 2020/4/10 18:25
* @version V1.0
*/
@Slf4j
public class RestTemplateUtils {
/**
* http 请求 GET
*
* @param url 地址
* @param params 参数
* @param connecTimeout 连接时间
* @param readTimeout 读取时间
* @param retryCount 重试机制
* @return String 类型
*/
public static String getHttp(String url, JSONObject params, int connecTimeout, int readTimeout, int retryCount) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(connecTimeout);
requestFactory.setReadTimeout(readTimeout);
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 设置编码集
restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); // 异常处理
url = expandURL(url, params);
String result = null; // 返回值类型;
for (int i = 1; i <= retryCount; i++) {
try {
result = restTemplate.getForObject(url, String.class, params);
return result;
} catch (Exception e) {
log.error("-----------开始-----------重试count:{} " , i);
e.printStackTrace();
}
}
return result;
}
/**
* https 请求 GET
*
* @param url 地址
* @param params 参数
* @param connecTimeout 连接时间
* @param readTimeout 读取时间
* @param retryCount 重试机制
* @return String 类型
*/
public static String getHttps(String url, JSONObject params, int connecTimeout, int readTimeout, int retryCount) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(connecTimeout);
requestFactory.setReadTimeout(readTimeout);
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 设置编码集
restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); //error处理
restTemplate.setRequestFactory(new HttpsClientRequestFactory()); // 绕过https
url = expandURL(url, params);
String result = null; // 返回值类型;
for (int i = 1; i <= retryCount; i++) {
try {
result = restTemplate.getForObject(url, String.class, params);
return result;
} catch (Exception e) {
log.error("-----------开始-----------重试count:{} " , i);
e.printStackTrace();
}
}
return result;
}
/**
* http 请求 post
*
* @param url 地址
* @param params 参数
* @param headersMap header
* @param connecTimeout 连接时间
* @param readTimeout 读取时间
* @param retryCount 重试机制
* @return String 类型
*/
public static String postHttp(String url, JSONObject params, Map headersMap, int connecTimeout, int readTimeout, int retryCount) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); // 时间函数
requestFactory.setConnectTimeout(connecTimeout);
requestFactory.setReadTimeout(readTimeout);
//内部实际实现为 HttpClient
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 设置编码集
restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); // 异常处理的headers error 处理
// 设置·header信息
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAll(headersMap);
HttpEntity<JSONObject> requestEntity = new HttpEntity<JSONObject>(params, requestHeaders); // josn utf-8 格式
String result = null; // 返回值类型;
for (int i = 1; i <= retryCount; i++) {
try {
result = restTemplate.postForObject(url, requestEntity, String.class);
return result;
} catch (Exception e) {
log.error("-----------开始-----------重试count:{} " , i);
e.printStackTrace();
}
}
return result;
}
/**
* http 普通请求 post
* @param url 地址
* @param params MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
* @param headersMap header
* @param connecTimeout 连接时间
* @param readTimeout 读取时间
* @param retryCount 重试机制
* @return String 类型
*/
public static String postHttp(String url, MultiValueMap params, Map headersMap, int connecTimeout, int readTimeout, int retryCount) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); // 时间函数
requestFactory.setConnectTimeout(connecTimeout);
requestFactory.setReadTimeout(readTimeout);
//内部实际实现为 HttpClient
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 设置编码集
restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); // 异常处理的headers error 处理
// 设置·header信息
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAll(headersMap);
HttpEntity<Map> requestEntity = new HttpEntity<Map>(params, requestHeaders); // josn utf-8 格式
String result = null; // 返回值类型;
for (int i = 1; i <= retryCount; i++) {
try {
result = restTemplate.postForObject(url, requestEntity, String.class);
return result;
} catch (Exception e) {
log.error("-----------开始-----------重试count:{} " , i);
e.printStackTrace();
}
}
return result;
}
/**
* @param url 请求地址
* @param params 请求 josn 格式参数
* @param headersMap headers 头部需要参数
* @param retryCount 重试机制
* @return 返回string类型返回值
*/
public static String postHttps(String url, JSONObject params, Map headersMap, int connecTimeout, int readTimeout, int retryCount) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); // 时间函数
requestFactory.setConnectTimeout(connecTimeout);
requestFactory.setReadTimeout(readTimeout);
//内部实际实现为 HttpClient
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 设置编码集
restTemplate.setRequestFactory(new HttpsClientRequestFactory()); // 绕过https
restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); // 异常处理的headers error 处理
// 设置·header信息
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAll(headersMap);
HttpEntity<JSONObject> requestEntity = new HttpEntity<JSONObject>(params, requestHeaders); // josn utf-8 格式
String result = null; // 返回值类型;
for (int i = 1; i <= retryCount; i++) {
try {
result = restTemplate.postForObject(url, requestEntity, String.class);
return result;
} catch (Exception e) {
log.error("-----------开始-----------重试count:{} " , i);
e.printStackTrace();
}
}
return result;
}
/**
* 加密参数类型请求 application/x-www-form-urlencoded
* MultiValueMap<String, Object>
* 采用 HttpEntity<MultiValueMap<String, Object>> 构造
* http 请求 post
*
* @param url 地址
* @param postParameters 普通post请求需要的参数
* @param headersMap header
* @param connecTimeout 连接时间
* @param readTimeout 读取时间
* @param retryCount 重试机制
* @return String 类型
*/
public static String postHttpEncryption(String url, MultiValueMap<String, Object> postParameters, Map headersMap, int connecTimeout, int readTimeout, int retryCount) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); // 时间函数
requestFactory.setConnectTimeout(connecTimeout);
requestFactory.setReadTimeout(readTimeout);
//内部实际实现为 HttpClient
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); // 设置编码集
restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); // 异常处理的headers error 处理
// 设置·header信息
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAll(headersMap);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(postParameters, requestHeaders);
String result = null; // 返回值类型;
for (int i = 1; i <= retryCount; i++) {
try {
result = restTemplate.postForObject(url, requestEntity, String.class);
return result;
} catch (Exception e) {
log.error("-----------开始-----------重试count:{} " , i);
e.printStackTrace();
}
}
return result;
}
private static String expandURL(String url,JSONObject jsonObject) {
StringBuilder sb = new StringBuilder(url);
sb.append("?");
Set<String> keys = jsonObject.keySet();
for (String key : keys) {
sb.append(key).append("=").append(jsonObject.getString(key)).append("&");
}
return sb.deleteCharAt(sb.length() - 1).toString();
}
/**
* 出现异常,可自定义
*/
private static class DefaultResponseErrorHandler implements ResponseErrorHandler {
/**
* 对response进行判断,如果是异常情况,返回true
*/
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return response.getStatusCode().value() != HttpServletResponse.SC_OK;
}
/**
* 异常情况时的处理方法
*/
@Override
public void handleError(ClientHttpResponse response) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(response.getBody()));
StringBuilder sb = new StringBuilder();
String str = null;
while ((str = br.readLine()) != null) {
sb.append(str);
}
try {
throw new Exception(sb.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
参考链接
https://juejin.im/post/5db99c285188257e435592ac
来源:oschina
链接:https://my.oschina.net/u/4270922/blog/4388868