一、问题出现
通过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、走到这里,就结束啦!
五、总结
以上流程很绕,我写的也不好,我是菜鸟一枚,也在慢慢学习哦。
六、参考地址