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的設置

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