spring提供的RestTemplate的header格式

闲聊:
这次需求需要后台服务以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的设置

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章