SpringBoot 使用 RestTemplate 出现乱码

本文介绍了解决Spring Boot项目中调用百度AI接口出现乱码的问题,详细分析了乱码产生的原因及解决方法,包括对响应结果进行编码转换以及修改RestTemplate的默认编码。


1. 问题描述

项目中用到了百度的AI接口,我们使用Spring封装的RestTemplate 工具进行Http请求,出现了乱码问题,百度API文档写的是该接口响应的内容是UTF-8编码。

如上图所示,百度AI接口返回的信息并没有指定编码(其实是UTF-8的编码格式,只是没指定)。所以Spring Boot就以默认的ISO-8859-1对返回的数据流进行了编码,转换为String,这时其实已经乱码了,接着因为我的开发环境是UTF-8的,所以打印出来全是乱码。转码过程大概是这样的:(UTF-8—>字节数据—>ISO-8859-1—>UTF-8

{
    "log_id":7806616030292411514,
    "items":[
        {
            "sentiment":2,
            "abstract":"<span>东西很好</span>吃",
            "prop":"东西",
            "begin_pos":0,
            "end_pos":12,
            "adj":"很好"
        },
        {
            "sentiment":2,
            "abstract":"<span>服务也很不错</span>",
            "prop":"服务",
            "begin_pos":0,
            "end_pos":18,
            "adj":"好"
        }
    ]
}

2. 简单处理

对返回信息进行编码转换。转换方法:对响应结果result进行ISO-8859-1解码和UTF-8编码即可。转码过程大概是这样的:(UTF-8—>字节数据—>ISO-8859-1—>字节数据—>UTF-8

//工具类中注入静态成员属性。
private static RestTemplate restTemplate;
@Autowired
public void setRestTemplate(RestTemplate restTemplate) {
     BaiduUtils.restTemplate = restTemplate;
}
//方法代码
String url = SERVER_COMMENT_TAG.replace("{TOKEN}", token);
HttpHeaders headers = new HttpHeaders();
MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
headers.setContentType(mediaType);
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", type);
jsonObject.put("text", comment);
HttpEntity<String> paramEntity = new HttpEntity<>(jsonObject.toJSONString(), headers);
//这里提交的参数是String类型的
String baiduResult = restTemplate.postForObject(url, paramEntity, String.class);
//这里进行编码转换
String result = new String(baiduResult.getBytes("ISO-8859-1"), "UTF-8");
log.info(result );

打印结果

{
    "log_id":7806616030292411514,
    "items":[
        {
            "sentiment":2,
            "abstract":"<span>东西很好</span>吃",
            "prop":"东西",
            "begin_pos":0,
            "end_pos":12,
            "adj":"很好"
        },
        {
            "sentiment":2,
            "abstract":"<span>服务也很不错</span>",
            "prop":"服务",
            "begin_pos":0,
            "end_pos":18,
            "adj":"好"
        }
    ]
}

3. 源码解读

RestTemplate类的部分源码:

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {

	private static boolean romePresent;
	private static final boolean jaxb2Present;
	private static final boolean jackson2Present;
	private static final boolean jackson2XmlPresent;
	private static final boolean jackson2SmilePresent;
	private static final boolean jackson2CborPresent;
	private static final boolean gsonPresent;
	private static final boolean jsonbPresent;

	static {
		ClassLoader classLoader = RestTemplate.class.getClassLoader();
		romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
		jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)  && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
		jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
		jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
		jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
		gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
		jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
	}

	private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();

	//。。。。。。省略代码
	
	/**
	 * 使用默认设置创建 RestTemplate 的新实例。
	 * 默认初始化 HttpMessageConverter HttpMessageConverters。
	 */
	public RestTemplate() {
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		// 处理 String 类型的参数
		this.messageConverters.add(new StringHttpMessageConverter());
		this.messageConverters.add(new ResourceHttpMessageConverter(false));
		try {
			this.messageConverters.add(new SourceHttpMessageConverter<>());
		}
		catch (Error err) {
			// 忽略没有TransformerFactory实现可用的情况
		}
		this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
		if (romePresent) {
			this.messageConverters.add(new AtomFeedHttpMessageConverter());
			this.messageConverters.add(new RssChannelHttpMessageConverter());
		}
		if (jackson2XmlPresent) {
			this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
		}
		else if (jaxb2Present) {
			this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
		}
		if (jackson2Present) {
			// 处理 JSON 类型数据
			this.messageConverters.add(new MappingJackson2HttpMessageConverter());
		}
		else if (gsonPresent) {
			this.messageConverters.add(new GsonHttpMessageConverter());
		}
		else if (jsonbPresent) {
			this.messageConverters.add(new JsonbHttpMessageConverter());
		}
		if (jackson2SmilePresent) {
			this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
		}
		if (jackson2CborPresent) {
			this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
		}
		this.uriTemplateHandler = initUriTemplateHandler();
	}
	//。。。。。。省略代码
}

通过跟踪断点,简单分析下RestTemplate内部源码,发现以 JavaBean,Map,JSONObject 格式分别提交数据时,RestTemplate底层均采用了MappingJackson2HttpMessageConverter来处理请求。而以String格式提交数据时,底层其实采用的是StringHttpMessageConverter来处理请求。

打开StringHttpMessageConverter源码,发现该构造器默认的字符集是ISO-8859-1

public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {

	/**
	 * 转换器使用的默认字符集。
	 */
	public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
	@Nullable
	private volatile List<Charset> availableCharsets;
	private boolean writeAcceptCharset = true;
	/**
	 * 使用"ISO-8859-1"作为默认字符集的默认构造函数。
	 * @see #StringHttpMessageConverter(Charset)
	 */
	public StringHttpMessageConverter() {
		this(DEFAULT_CHARSET);
	}
	/**
	 * 如果所请求的内容类型没有指定,则接受要使用的默认字符集的构造函数。
	 */
	public StringHttpMessageConverter(Charset defaultCharset) {
		super(defaultCharset, MediaType.TEXT_PLAIN, MediaType.ALL);
	}
	//。。。。。。省略代码
}

4. 解决方法

直接在List<HttpMessageConverter<?>>中对StringHttpMessageConverter设置编码,并进行覆盖。

public class RestUtil {
	private final static RestTemplate restTemplate;
    static {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        //做一些其他设置
        requestFactory.setConnectTimeout(3000);
        requestFactory.setReadTimeout(3000);
        restTemplate = new RestTemplate(requestFactory);
        //解决乱码问题,在列表中的对应位置直接覆盖
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
    }
    //工具方法
}
public class RestUtil {
	private final static RestTemplate restTemplate;
    static {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        //做一些其他设置
        requestFactory.setConnectTimeout(3000);
        requestFactory.setReadTimeout(3000);
        restTemplate = new RestTemplate(requestFactory);
        //解决乱码问题,遍历寻找,进行覆盖
        List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
	    for (HttpMessageConverter<?> httpMessageConverter : list) {
	        if(httpMessageConverter instanceof StringHttpMessageConverter) {
	            ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(StandardCharsets.UTF_8);
	            break;
	        }
	    }
    }
    //工具方法
}

RestTemplate提供的构建器RestTemplateBuilder也提供了设置MessageConverter的方法,所以可以在构建时设置特殊的编码就可以解决问题了。

public class RestUtil {
	private final static RestTemplate restTemplate;
	static {
	    //解决乱码问题,遍历寻找,进行覆盖
	    StringHttpMessageConverter messageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
	    //添加额外的HttpMessageConverter,在构建器上配置的任何转换器都将替换RestTemplate的默认转换器。
	    restTemplate = new RestTemplateBuilder().additionalMessageConverters(messageConverter).build();
	}
	//工具方法
}

5. 简单使用

RestTemplate底层是通过HttpURLConnection实现的。
(1)getForObject

RestTemplate restTemplate = new RestTemplate(https://blog.csdn.net/mryang125/article/details/80955558);
String url = "http://localhost:8080/user/{id}";
UserVo userVo = restTemplate.getForObject(url, UserVo.class, id);

第一个参数表示URL,第二个参数表示返回类型,第三个参数表示URI中对应的参数,是一个可变长参数,可按顺序写多个参数。

(2)getForEntity

RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/users/{userName}/{note}/{start}/{limit}";
//使用map封装多个参数
Map<String, Object> params = new HashMap<>();
params.put("userName", userName);
params.put("note", note);
params.put("start", start);
params.put("limit", limit);
ResponseEntity<List> responseEntity = restTemplate.getForEntity(url, List.class, params);
List<UserVo> userVos = responseEntity.getBody();

(3)postForObject

RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/user";
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
//创建请求实体对象
HttpEntity<UserVo> request = new HttpEntity<>(newUserVo, headers);
User user = restTemplate.postForObject(url, request, User.class);

(4)postForEntity

RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/user";
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
//创建请求实体对象
HttpEntity<UserVo> request = new HttpEntity<>(newUserVo, headers);
//请求服务器
ResponseEntity<User> responseEntity = restTemplate.postForEntity(url, request, User.class);
//获取响应体
User user = responseEntity.getBody();
//获取响应头
HttpHeaders respHeaders = responseEntity.getHeaders();
//获取响应属性
List<String> success = respHeaders.get("success");
//获取响应状态码
int statusCode = responseEntity.getStatusCodeValue();

(5)delete

RestTemplate restTemplate = new RestTemplate();
restTemplate.delete("http://localhost:8080/use/{id}", id);

(6)exchange
RestTemplate还提供了一个exchange方法,该方法比上面的方法灵活,可以通过制定参数实现各种Http请求。下面列出Spring提供的八种方法。

<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException;

<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;

<T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType) throws RestClientException;

<T> ResponseEntity<T> exchange(String url,HttpMethod method, HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException;

<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException;

<T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType) throws RestClientException;

<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType) throws RestClientException;

<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType) throws RestClientException;

下面写一个使用例子:

RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/user";
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
//创建请求实体对象
HttpEntity<UserVo> request = new HttpEntity<>(newUserVo, headers);
//请求服务器
ResponseEntity<User> responseEntity = restTemplate.exchange(url,  HttpMethod.POST, request, User.class);
//获取响应体
User user = responseEntity.getBody();
//获取响应头
HttpHeaders respHeaders = responseEntity.getHeaders();
//获取响应属性
List<String> success = respHeaders.get("success");
//获取响应状态码
int statusCode = responseEntity.getStatusCodeValue();

//获取资源
String url1 = "http://localhost:8080/user/{id}";
ResponseEntity<User> responseEntity1 = restTemplate.exchange(url1,  HttpMethod.GET, null, User.class, id);
//获取响应体
User user1 = responseEntity1.getBody();

6. 使用泛型接收响应示例

这里添加一个返回类型中泛型的使用方法:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

//创建请求实体对象,这里将参数转换为JSON字符串了
HttpEntity<String> request = new HttpEntity<>(paramJsonStr, headers);
//请求服务器
RestTemplate restTemplate = new RestTemplate();
String url = "http://www.baidu.com/task";
ResponseEntity<String> responseEntity = null;
try {
	//统一使用String接收响应,再用Jackson转为对应的实体类
    responseEntity = restTemplate.postForEntity(url, request, String.class);
//这里使用try...catch是因为有可能因为网络原因出现错误,RestClientException 的子类 ResourceAccessException 异常
} catch (RestClientException e) {
    e.printStackTrace();
}

if (responseEntity != null) {
    //获取响应体
    String body = responseEntity.getBody();
    //使用Jackson转为对应的实体类,这里的Result中使用到了泛型,用来应对多种格式
    ObjectMapper mapper = new ObjectMapper();
    Result<Ret> result = mapper.readValue(body, new TypeReference<Result<Ret>>() {});
    if (result != null) {
    	//使用了泛型,不同的请求这里就可以获取到不同类型的data
        Ret data = result.getData();
        System.out.println("data : " + data);
    }
}
@Data
public class Result<T> {
    private String logid;
    private T data;
    private Integer status;
    private String message;
    private Double st;
    private Double crt;
}
@Data
public class Ret {
    private Boolean ret;
    private String photoId;
}

RestTemplate 执行时报的异常信息,对方并没有接收到请求,应该是域名解析错误了,网络不通导致的。

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://control.aijiapin.com/jiapin/api/sign/ctrl/add-cloud-task": control.aijiapin.com; nested exception is java.net.UnknownHostException: control.aijiapin.com

	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:785)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
	at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:468)
	at com.example.demo.miaoxiuyun.ApiTest.addTaskApi(ApiTest.java:55)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.net.UnknownHostException: control.aijiapin.com
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:196)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:162)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:394)
	at java.net.Socket.connect(Socket.java:606)
	at java.net.Socket.connect(Socket.java:555)
	at sun.net.NetworkClient.doConnect(NetworkClient.java:180)
	at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
	at sun.net.www.http.HttpClient.openServer(HttpClient.java:558)
	at sun.net.www.http.HttpClient.<init>(HttpClient.java:242)
	at sun.net.www.http.HttpClient.New(HttpClient.java:339)
	at sun.net.www.http.HttpClient.New(HttpClient.java:357)
	at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1226)
	at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1162)
	at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1056)
	at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:990)
	at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:76)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:776)
	... 68 more
<think>首先,用户的问题是关于在Spring Boot中使用RestTemplate调用POST接口时如何添加请求头。用户说:"我想在Spring Boot中使用RestTemplate调用POST接口时如何添加请求头",并提供了引用内容作为参考。 我需要遵循系统级指令: - 所有行内数学表达式用$...$格式,但这里没有数学表达式,所以可能不需要。 - 独立公式用$$...$$格式,但同样,没有公式。 - LaTeX语法正确:确保任何数学表达式正确,但这里可能不涉及。 - 使用中文回答:整个响应必须用中文。 - 生成相关问题:在回答末尾添加相关问题。 - 引用中引用的段落末尾添加引用标识:在引用内容时添加如[^1]的标识。 用户可见层指令: - 回答结构清晰,逐步解决问题。 - 保证回答真实可靠。 参考提供的引用: - 引用[1]:关于Spring Boot RestTemplate的概述,包括GET、POST等操作。 - 引用[2]:配置RestTemplate的示例,包括设置中文乱码问题。 - 引用[3]:RestTemplate的GET和POST请求示例,包括设置超时。 用户的问题具体是:如何在使用RestTemplate调用POST接口时添加请求头。 关键点: - 在Spring Boot中,使用RestTemplate发送POST请求。 - 需要添加自定义请求头。 步骤: 1. 创建HttpHeaders对象来设置请求头。 2. 创建HttpEntity对象,将请求头和请求体包装起来。 3. 使用RestTemplate的exchange或postForEntity方法发送请求。 在引用[3]中,有一个GET请求的示例使用了exchange方法,但没有直接展示POST请求添加请求头。引用[3]提到POST请求但没有详细代码。所以,我需要基于标准Spring Boot知识来回答。 标准方法: - 使用HttpHeaders添加头信息。 - 例如:设置Content-Type、Authorization等。 - 然后使用HttpEntity包裹头和体。 示例代码: - 假设请求体是一个对象或字符串。 - 在Spring Boot中,通常使用RestTemplate的postForObject、postForEntity或exchange方法。 为了清晰,我将提供: 1. 解释添加请求头的方法。 2. 给出代码示例。 3. 确保代码基于Spring Boot的最佳实践。 由于用户指定了中文,整个响应用中文写。 最后,生成相关问题。 结构我的回答: - 开头:直接回答问题。 - 步骤:解释如何添加请求头。 - 代码示例:提供完整的代码片段。 - 注意事项:如有必要。 - 相关问题:在末尾添加。 添加引用标识: - 在引用内容时添加,如[^1]。但用户提供的引用是参考,不是直接引用。所以,如果我在回答中提到那些引用,就添加标识。否则,可能不需要,除非直接引用。 用户说:"上述引用仅供参考",所以我应该基于这些引用构建回答,但不要强制添加标识,除非真正引用。 在回答中,我可以提到参考了这些内容,然后添加标识。 现在,构建回答。</think>### 在Spring Boot中使用RestTemplate添加请求头调用POST接口 在Spring Boot中,使用`RestTemplate`调用POST接口时添加请求头,主要通过`HttpHeaders`和`HttpEntity`来实现。以下是详细步骤和代码示例,确保请求可靠且符合Spring Boot最佳实践[^2]。 #### 步骤说明 1. **创建HttpHeaders对象**:设置所需的请求头,如`Content-Type`、`Authorization`等。 2. **封装HttpEntity对象**:将请求头和请求体(如JSON、表单数据)组合到`HttpEntity`中。 3. **使用RestTemplate发送请求**:调用`exchange()`或`postForEntity()`方法发送POST请求。 4. **处理响应**:解析响应体并处理异常。 #### 完整代码示例 以下是一个Spring Boot服务类示例,展示如何添加请求头调用POST接口。假设目标接口URL为`https://api.example.com/post-endpoint`,请求体为JSON格式。 ```java import org.springframework.http.*; import org.springframework.web.client.RestTemplate; import org.springframework.util.MultiValueMap; import java.util.Collections; @Service public class ApiService { // 注入配置好的RestTemplate Bean(参考引用[2]的配置) @Autowired private RestTemplate restTemplate; public ResponseEntity<String> postWithHeaders(String url, Object requestBody) { // 1. 创建HttpHeaders并添加请求头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); // 设置Content-Type为JSON headers.set("Authorization", "Bearer your_token_here"); // 添加自定义头,如认证令牌 headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); // 设置接受响应类型 // 2. 封装HttpEntity,包含请求头和请求体 HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, headers); try { // 3. 使用exchange方法发送POST请求 ResponseEntity<String> response = restTemplate.exchange( url, HttpMethod.POST, requestEntity, String.class // 指定响应类型为String(可替换为自定义类) ); return response; // 返回响应 } catch (Exception e) { throw new RuntimeException("POST请求失败: " + e.getMessage(), e); } } } ``` #### 关键点解释 - **HttpHeaders**:用于定义所有请求头,支持链式调用如`headers.add("Key", "Value")`。 - **HttpEntity**:将请求体(如Map、POJO或字符串)与请求头绑定。若无需请求体,可传入`null`。 - **exchange()方法**:更灵活,支持指定HTTP方法、URL、实体和响应类型(引用[3]中GET示例的扩展)。 - **RestTemplate配置**:确保在Spring Boot中配置`RestTemplate` Bean(如引用[2]所示),解决中文乱码或超时问题。 #### 注意事项 - **性能优化**:在高并发场景,考虑连接池(如Apache HttpClient或OkHttp)配置RestTemplate(参考引用[2])。 - **错误处理**:添加`RestTemplate`的错误处理器(如`ResponseErrorHandler`)以优雅处理HTTP错误。 - **测试验证**:使用Postman或单元测试验证请求头是否正确传递。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值