package com.example.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.*;
@Slf4j
public class RestClientUtil {
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public RestClientUtil(RestTemplate restTemplate, ObjectMapper objectMapper) {
this.restTemplate = Objects.requireNonNull(restTemplate, "restTemplate不能为null");
this.objectMapper = Objects.requireNonNull(objectMapper, "objectMapper不能为null");
}
public RequestBuilder request() {
return new RequestBuilder();
}
public class RequestBuilder {
private HttpMethod method = HttpMethod.GET;
private String url;
private Map<String, ?> urlParams = Collections.emptyMap();
private final Map<String, String> headers = new LinkedHashMap<>();
private Object body;
private MediaType contentType;
private TypeReference<?> responseType;
private final MultiValueMap<String, String> formParams = new LinkedMultiValueMap<>();
private RequestBuilder() {
headers.put(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
headers.put(HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.name());
}
public RequestBuilder method(HttpMethod method) {
this.method = Optional.ofNullable(method).orElse(HttpMethod.GET);
return this;
}
public RequestBuilder url(String url) {
this.url = url;
return this;
}
public RequestBuilder urlParams(Map<String, ?> urlParams) {
if (urlParams != null && !urlParams.isEmpty()) {
this.urlParams = urlParams;
}
return this;
}
public RequestBuilder addHeader(String name, String value) {
if (name != null && value != null) {
headers.put(name, value);
}
return this;
}
public RequestBuilder headers(Map<String, String> headers) {
if (headers != null && !headers.isEmpty()) {
this.headers.putAll(headers);
}
return this;
}
public RequestBuilder body(Object body) {
this.body = body;
return this;
}
public RequestBuilder contentType(MediaType contentType) {
this.contentType = contentType;
return this;
}
public <T> RequestBuilder responseType(TypeReference<T> responseType) {
this.responseType = responseType;
return this;
}
public RequestBuilder formParam(String key, String value) {
if (key != null && value != null) {
formParams.add(key, value);
}
return this;
}
public <T> T execute() {
validateParams();
// 如果有form参数,强制使用form请求体,Content-Type自动切换
if (!formParams.isEmpty()) {
contentType = MediaType.APPLICATION_FORM_URLENCODED;
body = formParams;
}
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setAll(headers);
if (contentType != null) {
httpHeaders.setContentType(contentType);
}
httpHeaders.setAcceptCharset(Collections.singletonList(StandardCharsets.UTF_8));
HttpEntity<?> httpEntity;
try {
httpEntity = buildHttpEntity(httpHeaders);
} catch (JsonProcessingException e) {
log.error("请求体序列化异常", e);
throw new RestClientException("请求体序列化异常", e);
}
logRequest(httpHeaders);
try {
ResponseEntity<String> response = restTemplate.exchange(url, method, httpEntity, String.class, urlParams);
logResponse(response);
return handleResponse(response);
} catch (Exception e) {
log.error("请求执行异常", e);
throw new RestClientException("请求执行异常", e);
}
}
private void validateParams() {
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("请求URL不能为空");
}
if (responseType == null) {
throw new IllegalArgumentException("必须设置响应类型 responseType");
}
}
private HttpEntity<?> buildHttpEntity(HttpHeaders httpHeaders) throws JsonProcessingException {
if (body == null) {
return new HttpEntity<>(httpHeaders);
}
if (MediaType.APPLICATION_JSON.includes(contentType)) {
String json = body instanceof String ? (String) body : objectMapper.writeValueAsString(body);
return new HttpEntity<>(json, httpHeaders);
}
if (MediaType.APPLICATION_FORM_URLENCODED.includes(contentType)) {
if (!(body instanceof MultiValueMap)) {
throw new IllegalArgumentException("Form请求体必须是 MultiValueMap 类型");
}
return new HttpEntity<>(body, httpHeaders);
}
if (MediaType.TEXT_PLAIN.includes(contentType)) {
return new HttpEntity<>(Objects.toString(body), httpHeaders);
}
return new HttpEntity<>(body, httpHeaders);
}
private void logRequest(HttpHeaders httpHeaders) {
log.info("=== RestClientUtil 请求开始 ===");
log.info("HTTP方法: {}", method);
log.info("请求URL: {}", url);
log.info("URL参数: {}", urlParams);
log.info("请求头: {}", httpHeaders);
log.info("请求体: {}", body);
log.info("Content-Type: {}", contentType);
}
private void logResponse(ResponseEntity<String> response) {
log.info("响应状态码: {}", response.getStatusCodeValue());
if (response.hasBody()) {
log.info("响应体: {}", response.getBody());
} else {
log.warn("响应无内容");
}
}
private <T> T handleResponse(ResponseEntity<String> response) throws JsonProcessingException {
if (!response.getStatusCode().is2xxSuccessful()) {
throw new RestClientException("HTTP请求失败,状态码:" + response.getStatusCodeValue());
}
String respBody = response.getBody();
if (respBody == null || respBody.isEmpty()) {
return null;
}
@SuppressWarnings("unchecked")
T result = (T) objectMapper.readValue(respBody, responseType);
return result;
}
}
}
package com.example.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
@Slf4j
@Configuration
public class RestTemplateConfig {
/**
* 创建一个信任所有证书(包括自签名证书)的RestTemplate,注意:生产环境请谨慎使用!
*/
@Bean
public RestTemplate restTemplateTrustAll() {
try {
TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
// 超时配置,可根据需求调整
requestFactory.setConnectTimeout(5000);
requestFactory.setReadTimeout(10000);
log.info("创建支持自签名证书的RestTemplate成功");
return new RestTemplate(requestFactory);
} catch (Exception e) {
log.error("创建支持自签名证书的RestTemplate失败", e);
throw new IllegalStateException("初始化RestTemplate失败", e);
}
}
}
import com.example.util.RestClientUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@Service
public class ApiService {
private final RestClientUtil restClientUtil;
public ApiService(RestTemplate restTemplate, ObjectMapper objectMapper) {
this.restClientUtil = new RestClientUtil(restTemplate, objectMapper);
}
public Map<String, Object> callApi() {
return restClientUtil.request()
.method(HttpMethod.POST)
.url("https://your-api.example.com/api/login")
.formParam("username", "admin")
.formParam("password", "123456")
// 如果自定义headers
.addHeader("X-Custom-Header", "value")
.responseType(new TypeReference<>() {})
.execute();
}
}