一、问题出现

通过RestTemplate发送Http接口调用时,对于请求需要携带请求头时,需要额外再进行配置。

二、解决方案

给请求携带请求头,有两种实现的方式:

方式一:在每次发送请求时,构建一个HttpEntity对象,传入请求参数与请求头。

方式二:通过配置RestTemplate,使通过RestTemplate调用的http请求都携带上请求头。

三、代码实现

方式一:在每次发送请求时,构建一个HttpEntity对象,传入请求参数与请求头。

以下是通过postForObject为例实现

@GetMapping("/test")
public Map<String, Object> getBlobMetadataList(){
    // 构建你的请求头
    HttpHeaders headers = new HttpHeaders();
    headers.set("xxx","xxx");
    headers.set("Content-Type", "application/json");
    // 构建你的请求体参数
    HashMap<String, String> map = HashMap<>();
    map.put("yourParma", "youValue");
    // 组合请求头与请求体参数
    HttpEntity<String> requestEntity = new HttpEntity<>(JSONObject.toJSONString(map), headers);
    // path -> 请求地址,requestEntity -> 请求参数相关对象,HashMap.class -> 返回结果映射类型
    return restTemplate.postForObject(path, requestEntity, HashMap.class);
}

方式二:通过配置RestTemplate,使通过RestTemplate调用的http请求都携带上请求头。

对于一些共同的请求头,如果在每一次发送请求调用时都去重头构建一个HttpEntity请求头,在代码上重复不说,看着也不舒服,以下为配置统一的请求头,让每次请求都自动携带上请求头,一劳永逸!

/**
 * RestTemplate配置类
 */
@Slf4j
@Configuration
public class RestTemplateConfig {

    /**
     * 常用远程调用RestTemplate
     * @return restTemplate
     */
    @Bean("restTemplate")
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate();
        // 设置通用的请求头
        HeaderRequestInterceptor myHeader = new HeaderRequestInterceptor("xxx", "xxx");              
        List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
        interceptors.add(myHeader);
        restTemplate.setInterceptors(interceptors);
        return restTemplate;
    }

   /**
     * 拦截器类,为restTemplatep后续调用请求时携带请求头
     */
    public static class HeaderRequestInterceptor implements ClientHttpRequestInterceptor{

        private final String header;

        private final String value;

        public HeaderRequestInterceptor(String header, String value){
            this.header = header;
            this.value = value;
        }

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            request.getHeaders().set(header, value);
            return execution.execute(request, body);
        }

    }



}

通过以上的方式,在你需要的地放去添加或者修改请求头。

四、源码分析

1、不管是post、get、execute函数发送http请求,最终都是通过调用doExecute函数。

protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
			@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {

		Assert.notNull(url, "URI is required");
		Assert.notNull(method, "HttpMethod is required");
		ClientHttpResponse response = null;
		try {
            // 在此处构建请求
			ClientHttpRequest request = createRequest(url, method);
			if (requestCallback != null) {
				requestCallback.doWithRequest(request);
			}
			response = request.execute();
			handleResponse(url, method, response);
			return (responseExtractor != null ? responseExtractor.extractData(response) : null);
		}
		catch (IOException ex) {
			String resource = url.toString();
			String query = url.getRawQuery();
			resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
			throw new ResourceAccessException("I/O error on " + method.name() +
					" request for \"" + resource + "\": " + ex.getMessage(), ex);
		}
		finally {
			if (response != null) {
				response.close();
			}
		}
	}

因为我们想要为每一个http请求添加上请求头,那么这个操作应该是要在创建一个请求时来进行的,那么我们将目标定位到createRequest函数。

2、接下来我们观察下createRequest的具体实现.。

createRquest函数的具体的位置在HttpAccess中,且他被InterceptingHttpAccessor所继承,而RestTemplate又继承InterceptingHttpAccessor。

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations{}

public abstract class InterceptingHttpAccessor extends HttpAccessor {
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
		ClientHttpRequest request = getRequestFactory().createRequest(url, method);
		if (logger.isDebugEnabled()) {
			logger.debug("HTTP " + method.name() + " " + url);
		}
		return request;
	}

3、可以看到我们将要进入到getRequestFactory().createRequest函数中,我们先看InterceptingHttpAccessor的getRequestFactory函数

@Nullable
	private volatile ClientHttpRequestFactory interceptingRequestFactory;

@Override
	public ClientHttpRequestFactory getRequestFactory() {
		List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
		if (!CollectionUtils.isEmpty(interceptors)) {
			ClientHttpRequestFactory factory = this.interceptingRequestFactory;
			if (factory == null) {
				factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
				this.interceptingRequestFactory = factory;
			}
			return factory;
		}
		else {
			return super.getRequestFactory();
		}
	}

以上出现一个判断,如果我们传入了List<ClientHttpRequestInterceptor>,那么就创建一个InterceptingClientHttpRequestFactory,否则就用HttpAccess中默认的SimpleClientHttpRequestFactory去创建请求。

那么在此,我们需要先搞清楚ClientHttpRequestInterceptor的作用,以便去区分到底使用哪一个factory。

接下来进入ClientHttpRequestInterceptor去看看是长什么样的

public interface ClientHttpRequestInterceptor {

	ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
			throws IOException;

}

可以看到它就是一个接口,那就意味着这个接口将会被调用,再仔细看看参数,传入了HttpRequest request,那是不是意味着我们可以根据这个request对http请求进行下封装处理?

4、本着猜测的想法,那么选择进行InterceptingClientHttpRequestFactory的createRequest去一探究竟。

public class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {

	private final List<ClientHttpRequestInterceptor> interceptors;


	@Override
	protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
		return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
	}

}

在createRequest中是创建一个InterceptingClientHttpRequest对象,接下来看看他所调用的构造函数。

class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {

	private final ClientHttpRequestFactory requestFactory;

	private final List<ClientHttpRequestInterceptor> interceptors;

	private HttpMethod method;

	private URI uri;


	protected InterceptingClientHttpRequest(ClientHttpRequestFactory requestFactory,
			List<ClientHttpRequestInterceptor> interceptors, URI uri, HttpMethod method) {

		this.requestFactory = requestFactory;
		this.interceptors = interceptors;
		this.method = method;
		this.uri = uri;
	}


	@Override
	public HttpMethod getMethod() {
		return this.method;
	}

	@Override
	public String getMethodValue() {
		return this.method.name();
	}

	@Override
	public URI getURI() {
		return this.uri;
	}

	@Override
	protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
		InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
		return requestExecution.execute(this, bufferedOutput);
	}


	private class InterceptingRequestExecution implements ClientHttpRequestExecution {

		private final Iterator<ClientHttpRequestInterceptor> iterator;

		public InterceptingRequestExecution() {
			this.iterator = interceptors.iterator();
		}

		@Override
		public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
			if (this.iterator.hasNext()) {
				ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
				return nextInterceptor.intercept(request, body, this);
			}
			else {
				HttpMethod method = request.getMethod();
				Assert.state(method != null, "No standard HTTP method");
				ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
				request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
				if (body.length > 0) {
					if (delegate instanceof StreamingHttpOutputMessage) {
						StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
						streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
					}
					else {
						StreamUtils.copy(body, delegate.getBody());
					}
				}
				return delegate.execute();
			}
		}
	}

}

由上可以看出,目前追到了构造函数就停止了,但这里需要额外关心executeInternal和execute函数。后续会回到execute函数。该函数的主要作用是遍历所有的ClientHttpRequestInterceptor,将HttpRequest传入进行处理。

5、接着一直返回到RestTemplate,回到doExecute函数

response = request.execute();

在这个地方有这么一行调动方法,是不是很眼熟?没错,就刚刚上一步里的excute,点击进去,可以看到虽然不是直接调用ClientHttpRequestInterceptor的execute,但是是通过其间接继承的抽象类AbstarctClientHttpRequest中的execute去调用到executeInternal,最终遍历到所有的ClientHttpRequestInterceptor去执行。

6、走到这里,就结束啦!

五、总结

以上流程很绕,我写的也不好,我是菜鸟一枚,也在慢慢学习哦。

六、参考地址

RestTemplate 统一添加 Header_文盲青年的博客-CSDN博客_resttemplate添加headerhttps://blog.csdn.net/qq_35890572/article/details/118673860?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165059126916780265487400%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165059126916780265487400&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-5-118673860.142%5Ev9%5Epc_search_result_cache,157%5Ev4%5Econtrol&utm_term=RestTemplate%E8%AE%BE%E7%BD%AE%E8%AF%B7%E6%B1%82%E5%A4%B4&spm=1018.2226.3001.4187

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐