RestTemplate工具类进行http请求发送,包含常用的get,post,put请求和文件的上传下载,其余方式的请求可自定义拓展,话不多说,开整:
代码结构:
-
util
- RestUtil
- LocalHttpUtil
- ConvertUtil
-
interceptor
- ClientHttpRequestInterceptorX
上代码:
RestUtil:
typescript
复制代码
import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.axin229913.constant.util.ConvertUtil;
import com.axin229913.exception.base.BaseException;
import com.axin229913.exception.enums.BaseExceptionEnum;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.*;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 类名:restTemplate远程访问工具类
*
* @author a-xin
* @date 2024/1/17 15:19
*/
@Slf4j
@Component
public class RestUtil {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate(LocalHttpUtil.clientHttpRequestFactory(2, 2));
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(
MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN));
restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
restTemplate.getInterceptors().add(new ClientHttpRequestInterceptorX());
return restTemplate;
}
/**
* 发送PUT请求
*
* @param url 请求地址
* @param headers 请求头信息
* @param params 参数信息
* @param clsType 返回参数映射类型
* @param <T> 返回参数泛型
* @return 返回数据body
*/
public static <T> T put(String url, Map<String, String> headers, Map<String, String> params, TypeReference<T> clsType) {
return exchange(url, HttpMethod.PUT, headers, params, clsType);
}
/**
* 发送PUT请求
*
* @param url 请求地址
* @param headers 请求头信息
* @param params 参数信息
* @return 返回数据body
*/
public static String put(String url, Map<String, String> headers, Map<String, String> params) {
return exchange(url, HttpMethod.PUT, headers, params);
}
/**
* 发送POST请求
*
* @param url 请求地址
* @param headers 请求头信息
* @param params 参数信息
* @param clsType 返回参数映射类型
* @param <T> 返回参数泛型
* @return 返回数据body
*/
public static <T> T post(String url, Map<String, String> headers, Map<String, String> params, TypeReference<T> clsType) {
return exchange(url, HttpMethod.POST, headers, params, clsType);
}
/**
* 发送POST请求
*
* @param url 请求地址
* @param headers 请求头信息
* @param params 参数信息
* @return 返回数据body
*/
public static String post(String url, Map<String, String> headers, Map<String, String> params) {
return exchange(url, HttpMethod.POST, headers, params);
}
/**
* 发送GET请求
*
* @param url 请求地址
* @param headers 请求头信息
* @param params 参数信息
* @param clsType 返回参数映射类型
* @param <T> 返回参数泛型
* @return 返回数据body
*/
public static <T> T get(String url, Map<String, String> headers, Map<String, String> params, TypeReference<T> clsType) {
return exchange(getGetParamUrl(url, params), HttpMethod.GET, headers, params, clsType);
}
/**
* 发送GET请求
*
* @param url 请求地址
* @param headers 请求头信息
* @param params 参数信息
* @return 返回数据body
*/
public static String get(String url, Map<String, String> headers, Map<String, String> params) {
return exchange(getGetParamUrl(url, params), HttpMethod.GET, headers, params);
}
/**
* 上传文件
*
* @param url 请求地址
* @param fileKey 文件请求字段名称
* @param file 文件信息
* @return 返回数据body
*/
public static String uploadFile(String url, String fileKey, MultipartFile file) {
try {
RestTemplate restTemplate = SpringUtil.getBean(RestTemplate.class);
MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
InputStreamResource inputStreamResource = new InputStreamResource(file.getInputStream()) {
@Override
public String getFilename() {
return file.getOriginalFilename();
}
@Override
public long contentLength() {
return file.getSize();
}
};
params.add(fileKey, inputStreamResource);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
BaseExceptionEnum.NORMAL_EXCEPTION.exceptionIf(!responseEntity.getStatusCode().equals(HttpStatus.OK), responseEntity.getBody());
return responseEntity.getBody();
} catch (Exception e) {
throw new BaseException(e.getMessage());
}
}
/**
* 下载文件,直接返回到response中,没有中转文件数据
*
* @param url 请求地址
* @param httpMethod 请求方式
* @param params 参数信息
* @param response 接收数据返回载体
*/
public static void downloadFile(String url, HttpMethod httpMethod, Map<String, String> params, HttpServletResponse response) {
Map<String, String> uriVariables = new HashMap<>();
if (CollUtil.isNotEmpty(params)) {
uriVariables.putAll(params);
}
ResponseExtractor<Boolean> responseExtractor = clientHttpResponse -> {
// 设置响应头,直接用第三方文件服务的响应头
HttpHeaders headers = clientHttpResponse.getHeaders();
headers.forEach((key, value) -> response.setHeader(key, value.get(0)));
// 收到响应输入流即时拷贝写出到响应输出流中: inputStream -> outputStream
StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
return true;
};
RestTemplate restTemplate = SpringUtil.getBean(RestTemplate.class);
restTemplate.execute(url, httpMethod, null, responseExtractor, uriVariables);
}
/**
* restTemplate请求工具主类
*
* @param url 请求地址
* @param httpMethod 请求方式
* @param headers 请求头信息
* @param params 参数信息
* @param clsType 返回参数映射类型
* @param <T> 返回参数泛型
* @return 返回数据body
*/
private static <T> T exchange(String url, HttpMethod httpMethod, Map<String, String> headers, Map<String, String> params, TypeReference<T> clsType) {
return ConvertUtil.toObject(ConvertUtil.xmlToJsonStr(exchange(url, httpMethod, headers, params)), clsType);
}
/**
* restTemplate请求工具主类
*
* @param url 请求地址
* @param httpMethod 请求方式
* @param headers 请求头信息
* @param params 参数信息
* @return 返回数据body
*/
private static String exchange(String url, HttpMethod httpMethod, Map<String, String> headers, Map<String, String> params) {
RestTemplate restTemplate = SpringUtil.getBean(RestTemplate.class);
MultiValueMap<String, String> headersMap = new LinkedMultiValueMap<>();
if (CollUtil.isNotEmpty(headers)) {
headersMap.setAll(headers);
}
MultiValueMap<String, String> bodyMap = new LinkedMultiValueMap<>();
if (CollUtil.isNotEmpty(params)) {
bodyMap.setAll(params);
}
HttpEntity<MultiValueMap<String, String>> multiValueMapHttpEntity = new HttpEntity<>(bodyMap, headersMap);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, httpMethod, multiValueMapHttpEntity, String.class);
BaseExceptionEnum.NORMAL_EXCEPTION.exceptionIf(!responseEntity.getStatusCode().equals(HttpStatus.OK), responseEntity.getBody());
return responseEntity.getBody();
}
/**
* 格式化get请求地址
*
* @param url 访问地址
* @param params 参数信息
* @return 组装好的请求地址
*/
private static String getGetParamUrl(@NonNull String url, Map<String, String> params) {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
params.entrySet().stream().forEach(o -> builder.queryParam(o.getKey(), o.getValue()));
return builder.build().encode().toString();
}
}
LocalHttpUtil:
java
复制代码
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.util.ArrayList;
import java.util.List;
/**
* 类名:http请求配置工厂工具类
*
* @author a-xin
* @date 2024/5/8 09:22
*/
@Slf4j
public class LocalHttpUtil {
/**
* http请求客户端配置信息
*
* @return 配置信息
*/
public static HttpClient httpClient() {
try {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
//设置信任ssl访问
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (arg0, arg1) -> true).build();
httpClientBuilder.setSSLContext(sslContext);
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
// 注册http和https请求
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory).build();
//使用Httpclient连接池的方式配置(推荐),同时支持netty,okHttp以及其他http框架
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
// 最大连接数
poolingHttpClientConnectionManager.setMaxTotal(1000);
// 同路由并发数
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
//配置连接池
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
// 重试次数
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, true));
//设置默认请求头
List<Header> headers = new ArrayList<>();
httpClientBuilder.setDefaultHeaders(headers);
return httpClientBuilder.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 获取链接工厂
*
* @param connectTimeOut 连接超时(秒)
* @param readTimeOut 响应超时(秒)
* @return 连接工厂
*/
public static ClientHttpRequestFactory clientHttpRequestFactory(int connectTimeOut, int readTimeOut) {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
// 连接超时(毫秒),配置动态设置(秒)
clientHttpRequestFactory.setConnectTimeout(connectTimeOut * 1000);
// 数据读取超时时间(毫秒),配置动态设置(秒)
clientHttpRequestFactory.setReadTimeout(readTimeOut * 1000);
// 从连接池获取请求连接的超时时间(毫秒),不宜过长,必须设置,比如连接不够用时,时间过长将是灾难性的
clientHttpRequestFactory.setConnectionRequestTimeout(connectTimeOut * 1000);
return clientHttpRequestFactory;
}
}
ClientHttpRequestInterceptorX:
java
复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.NonNull;
import java.io.IOException;
/**
* 类名:rest连接管理拦截器
*
* @author a-xin
* @date 2024/5/8 09:47
*/
@Slf4j
public class ClientHttpRequestInterceptorX implements ClientHttpRequestInterceptor {
private static final String NO_CONNECTION_UPDATE = "NoConnectionUpdate";
private static final String CONNECTION_UP = "Connection";
private static final String CONNECTION_LOW = "connection";
@NonNull
@Override
public ClientHttpResponse intercept(@NonNull HttpRequest httpRequest, @NonNull byte[] bytes, @NonNull ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
try {
HttpHeaders headers = httpRequest.getHeaders();
if (headers.containsKey(NO_CONNECTION_UPDATE)) {
log.info("skip connection intercept");
} else {
if (!headers.containsKey(CONNECTION_UP) && !headers.containsKey(CONNECTION_LOW)) {
headers.set(CONNECTION_LOW, "close");
}
}
} catch (Exception e) {
log.error("http request intercept fail, {}", e.getMessage(), e);
}
return clientHttpRequestExecution.execute(httpRequest, bytes);
}
}
ConvertUtil:
typescript
复制代码
import cn.hutool.core.text.CharSequenceUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.dozer.DozerBeanMapper;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* dozer 类型转化工具
*
* @author a-xin
*/
@Slf4j
@Component
public class ConvertUtil {
/**
* 持有Dozer单例, 避免重复创建DozerMapper消耗资源.
*/
private static final DozerBeanMapper dozerBeanMapper;
static {
dozerBeanMapper = new DozerBeanMapper();
}
/**
* 对象通用转换
*
* @param source 源对象
* @param destinationClass 目标类
* @param <T> 目标对象泛型
* @return 返回得到destinationClass
*/
public static <T> T converter(Object source, Class<T> destinationClass) {
return source == null ? null : dozerBeanMapper.map(source, destinationClass);
}
/**
* 对象通用转换
*
* @param source 源对象
* @return 返回得到destinationClass
*/
public static Map<String, Object> convertToMap(Object source) {
return source == null ? null : dozerBeanMapper.map(source, Map.class);
}
/**
* 基于Dozer将对象A的值拷贝到对象B中.
*
* @param source 源数据
* @param destinationObject 拷贝数据对象
*/
public static void copy(Object source, Object destinationObject) {
dozerBeanMapper.map(source, destinationObject);
}
/**
* 集合转换
*
* @param sourceList 源集合
* @param destinationClass 目标类
* @param <T> 泛型
* @return 返回得到destinationClass的集合结果
*/
public static <T> List<T> convertToList(List<?> sourceList, Class<T> destinationClass) {
return sourceList.stream().map(source -> ConvertUtil.converter(source, destinationClass))
.collect(Collectors.toList());
}
/**
* json字符串转对象,带泛型,
* 对象转换json字符串推荐使用:{@link ConvertUtil#toJsonString(Object)}
*
* @param data json字符串
* @param clsType 转换类型
* @param <T> 转换泛型
* @return 转换后数据
*/
public static <T> T toObject(String data, TypeReference<T> clsType) {
try {
if (CharSequenceUtil.isBlank(data)) {
return null;
}
val mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return mapper.readValue(data, clsType);
} catch (JsonProcessingException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 对象转json字符串,
* json字符串转换对象(带泛型)推荐使用:{@link ConvertUtil#toObject(String, TypeReference)}
*
* @param data 对象
* @return json字符串
*/
public static <T> String toJsonString(T data) {
try {
val mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
return mapper.writeValueAsString(data);
} catch (JsonProcessingException e) {
return data.toString();
}
}
/**
* xml格式字符串转json字符串
*
* @param xml xml字符串
* @return json字符串
*/
public static String xmlToJsonStr(String xml) {
try {
XmlMapper xmlMapper = new XmlMapper();
JsonNode jsonNode = xmlMapper.readTree(xml);
ObjectMapper jsonMapper = new ObjectMapper();
return jsonMapper.writeValueAsString(jsonNode);
} catch (Exception e) {
return xml;
}
}
}
测试代码:
less
复制代码
/**
* 测试API接口
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Result<String> testApi(SysForm form) {
Map<String, String> headers = new HashMap<>();
headers.put(TokenConstant.API_KEY_REQUEST_HEADER, TokenUtil.getApiEncryptKey(16, RequestTypeEnum.OPEN_FEIGN));
headers.put(TokenConstant.REQUEST_TYPE, RequestTypeEnum.OPEN_FEIGN.getType());
String s = RestUtil.get("http://www.baidu.com", headers, new HashMap<>());
System.out.println(s);
return Result.success();
}
测试截图: