RestTemplate工具类

1,439 阅读6分钟

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();
    }

测试截图:

image.png