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; package com.devicemag.core.utils; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; 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 * @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 { /** * 读取时间,自定义默认8s,0表示没有超时时间 */ public static final int READ_TIMEOUT = 1000*8; /** * 连接时间,自定义默认8s,0表示没有超时时间 */ public static final int CONNEC_TIMEOUT = 1000*8; /** * 重试次数,自定义默认1 */ public static final int RETRY_COUNT = 1; /** * http 请求 GET * * @param url 地址 * @param params 参数 * @return String 类型 */ public static String getHttp(String url, JSONObject params) { String result = getHttp(url, params, READ_TIMEOUT, CONNEC_TIMEOUT, RETRY_COUNT); return result; } /** * 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 { log.info("【GET/HTTP请求信息】,请求地址:{},请求参数:{}", url, params); result = restTemplate.getForObject(url, String.class, params); log.info("【GET/HTTP请求信息】,请求地址:{},请求参数:{},返回结果:{}", url, params,result); return result; } catch (Exception e) { log.error("【GET/HTTP请求信息】异常,重试count:{},请求地址:{},请求参数:{},异常信息:{}", i, url, params,e); e.printStackTrace(); } } return result; } /** * https 请求 GET * * @param url 地址 * @param params 参数 * @return String 类型 */ public static String getHttps(String url, JSONObject params) { String result = getHttps(url, params, READ_TIMEOUT, CONNEC_TIMEOUT, RETRY_COUNT); 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 { log.info("【GET/HTTPS请求信息】,请求地址:{},请求参数:{}", url, params); result = restTemplate.getForObject(url, String.class, params); log.info("【GET/HTTPS请求信息】,请求地址:{},请求参数:{},返回结果:{}", url, params,result); return result; } catch (Exception e) { log.error("【GET/HTTPS请求信息】异常,重试count:{},请求地址:{},请求参数:{},异常信息:{}", i, url, params,e); e.printStackTrace(); } } return result; } /** * http 请求 post/JSON * * @param url 地址 * @param params 参数 * @return String 类型 */ public static String postHttp(String url, JSONObject params, Map headersMap) { String result = postHttp(url, params,headersMap, READ_TIMEOUT, CONNEC_TIMEOUT, RETRY_COUNT); return result; } /** * http请求 post/JSON * * @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 { log.info("【POST/HTTP请求信息】,请求地址:{},请求参数:{}", url, params); result = restTemplate.postForObject(url, requestEntity, String.class); log.info("【POST/HTTP请求信息】,请求地址:{},请求参数:{},返回结果:{}", url, params,result); return result; } catch (Exception e) { log.error("【POST/HTTP请求信息】异常,重试count:{},请求地址:{},请求参数:{},异常信息:{}", i, url, params,e); e.printStackTrace(); } } return result; } /** * http请求 post/MAP * * @param url 地址 * @param params 参数 * @return String 类型 */ public static String postHttp(String url, MultiValueMap params, Map headersMap) { String result = postHttp(url, params,headersMap, READ_TIMEOUT, CONNEC_TIMEOUT, RETRY_COUNT); return result; } /** * http 普通请求 post/MAP * @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); // json utf-8 格式 String result = null; // 返回值类型; for (int i = 1; i <= retryCount; i++) { try { log.info("【POST/HTTP请求信息】,请求地址:{},请求参数:{}", url, params); result = restTemplate.postForObject(url, requestEntity, String.class); log.info("【POST/HTTP请求信息】,请求地址:{},请求参数:{},返回结果:{}", url, params,result); return result; } catch (Exception e) { log.error("【POST/HTTP请求信息】异常,重试count:{},请求地址:{},请求参数:{},异常信息:{}", i, url, params,e); e.printStackTrace(); } } return result; } /** * https 普通请求 post/JSON * * @param url 地址 * @param params 参数 * @return String 类型 */ public static String postHttps(String url, JSONObject params, Map headersMap) { String result = postHttps(url, params,headersMap, READ_TIMEOUT, CONNEC_TIMEOUT, RETRY_COUNT); return result; } /** * https 普通请求 post/JSON * @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 { log.info("【POST/HTTPS请求信息】,请求地址:{},请求参数:{}", url, params); result = restTemplate.postForObject(url, requestEntity, String.class); log.info("【POST/HTTPS请求信息】,请求地址:{},请求参数:{},返回结果:{}", url, params,result); return result; } catch (Exception e) { log.error("【POST/HTTPS请求信息】异常,重试count:{},请求地址:{},请求参数:{},异常信息:{}", i, url, params,e); e.printStackTrace(); } } return result; } /** * @Title: URL拼接 * @MethodName: expandURL * @param url * @param jsonObject * @Return java.lang.String * @Exception * @Description: * * @author: 王延飞 * @date: 2021/1/4 15:30 */ 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(); } } } }
import org.springframework.http.client.SimpleClientHttpRequestFactory; import javax.net.ssl.*; import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.Socket; import java.security.cert.X509Certificate; /** * @Title: Https 绕过 * @Description: * * @Copyright 2020-2021 - Powered By 研发中心 * @author: 王延飞 * @date: 2020/9/10 0010 9:01 * @version V1.0 */ public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) { try { if (!(connection instanceof HttpsURLConnection)) { throw new RuntimeException("An instance of HttpsURLConnection is expected"); } HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); httpsConnection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); super.prepareConnection(httpsConnection, httpMethod); } catch (Exception e) { e.printStackTrace(); } } /** * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section) */ // SSLSocketFactory用于创建 SSLSockets private static class MyCustomSSLSocketFactory extends SSLSocketFactory { private final SSLSocketFactory delegate; public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { this.delegate = delegate; } // 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。 // 这些默认的服务的最低质量要求保密保护和服务器身份验证 @Override public String[] getDefaultCipherSuites() { return delegate.getDefaultCipherSuites(); } // 返回的密码套件可用于SSL连接启用的名字 @Override public String[] getSupportedCipherSuites() { return delegate.getSupportedCipherSuites(); } @Override public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException { final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } private Socket overrideProtocol(final Socket socket) { if (!(socket instanceof SSLSocket)) { throw new RuntimeException("An instance of SSLSocket is expected"); } ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"}); return socket; } } }
参考链接






















