闲聊:
这次需求需要后台服务以http方式调用另一个服务,我用的spring的RestTemplate,先是通过本地配置host方法成功调成功,但是后面接入公司的mesh网关后(调用方所在服务器不需要配置host),连接失败了,排查了下发现是RestTemplate的header格式为key:[value1,value2]导致,而我们公司的网关对header格式有强要求导致。。。
在使用RestTemplate的时候,除非直接使用execute方法,其他的如post…,get…,exchange,put,patchForObject等方法,其header都会是 key:[value1,value2] 的格式,而不是一般的 key:value 格式
为什么格式会是key:[value1,value2]格式
这些方法最终都是调用的execute方法,在调用前,会对请求参数 RequestCallback 进行配置后再传入execute方法,用postForObject举个例子,httpEntityCallback方法就是配置RequestCallback,再传入到execute方法
@Override
@Nullable
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
而 RequestCallback 定义了 doWithRequest 方法,用于操作 ClientHttpRequest 其中就包含了设置headers
/**
* Callback interface for code that operates on a {@link ClientHttpRequest}. Allows to manipulate the request
* headers, and write to the request body.
*
* <p>Used internally by the {@link RestTemplate}, but also useful for application code.
*
* @author Arjen Poutsma
* @see RestTemplate#execute
* @since 3.0
*/
@FunctionalInterface
public interface RequestCallback {
/**
* Gets called by {@link RestTemplate#execute} with an opened {@code ClientHttpRequest}.
* Does not need to care about closing the request or about handling errors:
* this will all be handled by the {@code RestTemplate}.
* @param request the active HTTP request
* @throws IOException in case of I/O errors
*/
void doWithRequest(ClientHttpRequest request) throws IOException;
}
在调用post…,get…,exchange,put,patchForObject等方法时,会使用默认提供的RequestCallback实现类:
一个无配置header的AcceptHeaderRequestCallback
一个有配置header的HttpEntityRequestCallback,
来关注下有header的,里面header的保存格式:
1.header的参数是从其中的 HttpEntity 取出来的
2.HttpEntity中负责保存header的是HttpHeaders
3.HttpHeaders是实现的MultiValueMap接口(key:[value, value]的格式),
最后,从设置代码中看出
requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values)));
是直接把这个list设置进去,这导致了最后header里面的格式是key:[value, value]
/**
* Request callback implementation that writes the given object to the request stream.
*/
private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback {
private final HttpEntity<?> requestEntity;
public HttpEntityRequestCallback(@Nullable Object requestBody) {
this(requestBody, null);
}
public HttpEntityRequestCallback(@Nullable Object requestBody, @Nullable Type responseType) {
super(responseType);
if (requestBody instanceof HttpEntity) {
this.requestEntity = (HttpEntity<?>) requestBody;
}
else if (requestBody != null) {
this.requestEntity = new HttpEntity<>(requestBody);
}
else {
this.requestEntity = HttpEntity.EMPTY;
}
}
@Override
@SuppressWarnings("unchecked")
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
super.doWithRequest(httpRequest);
Object requestBody = this.requestEntity.getBody();
if (requestBody == null) {
HttpHeaders httpHeaders = httpRequest.getHeaders();
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values)));
}
if (httpHeaders.getContentLength() < 0) {
httpHeaders.setContentLength(0L);
}
}
else {
Class<?> requestBodyClass = requestBody.getClass();
Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
HttpHeaders httpHeaders = httpRequest.getHeaders();
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
...
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values)));
}
...
}
}
}
public class HttpEntity<T> {
/**
* The empty {@code HttpEntity}, with no body or headers.
*/
public static final HttpEntity<?> EMPTY = new HttpEntity<>();
private final HttpHeaders headers;
@Nullable
private final T body;
}
public class HttpHeaders implements MultiValueMap<String, String>, Serializable {
...
}
解决方案
换http工具
1.用apache的HttpClient
2.用okhttp3
自己实现RequestCallback
因为execute方法可以直接传入RequestCallback,因此可以通过自己重写RequestCallback来自己进行header的设置