文章目录
- 前言
- 1. RestTemplate请求整体过程
- 2. httpRequest 创建
- 3. doWithRequest
- 4. execute
- 5. http响应解析
前言
目前Spring RestTemplate是常用的http请求工具类,本文简单Spring RestTemplate的请求过程。
1. RestTemplate请求整体过程
接下来以ResponseEntity exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
ParameterizedTypeReference responseType, Object… uriVariables)方法为起点,揭开http请求过程。
public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
Type type = responseType.getType();
RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
}
http的整体流程如下所示,首先创建httpRequest,其次处理httpRequest,最重要就是执行http请求,最后就是处理http响应。
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 {
// 创建httpRequest
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
// HttpEntityRequestCallback#doWithRequest
requestCallback.doWithRequest(request);
}
// http执行
response = request.execute();
// http响应处理
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();
}
}
}
2. httpRequest 创建
Restemplate继承了InterceptingHttpAccessor,使用的是InterceptingClientHttpRequestFactory创建httpRequest。
public ClientHttpRequestFactory getRequestFactory() {
// ClientHttpRequestInterceptor 支持拓展
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
// 创建默认的HttpRequestFactory
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();
}
}
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}
3. doWithRequest
调用HttpEntityRequestCallback的doWithRequest,若body为null,主要是设置header。若body补位null,则主要是通过根据body的类型匹配HttpMessageConverter,将body序列化为httpRequest中
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
// 调用父类的doWithRequest方法
super.doWithRequest(httpRequest);
Object requestBody = this.requestEntity.getBody();
if (requestBody == null) {
HttpHeaders httpHeaders = httpRequest.getHeaders();
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
// 将HttpEntity中的header复制到httpRequest中
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach((key, values) -> httpHeaders.put(key, new ArrayList<>(values)));
}
// 设置ContentLength
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();
MediaType requestContentType = requestHeaders.getContentType();
// getMessageConverters()中的MessageConverters是在RestTemplate构造函数中添加的
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
// 依次轮询每个messageConverter判断是否支持写当前httpRequestBody
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<Object> genericConverter =
(GenericHttpMessageConverter<Object>) messageConverter;
if (genericConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
// header复制
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach((key, values) -> httpHeaders.put(key, new ArrayList<>(values)));
}
// debug级别打印body信息
logBody(requestBody, requestContentType, genericConverter);
// body序列化写入httpRequest
genericConverter.write(requestBody, requestBodyType, requestContentType, httpRequest);
return;
}
}
else if (messageConverter.canWrite(requestBodyClass, requestContentType)) {
// header复制
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach((key, values) -> httpHeaders.put(key, new ArrayList<>(values)));
}
// debug日志级别打印body信息
logBody(requestBody, requestContentType, messageConverter);
// body序列化写入httpRequest
((HttpMessageConverter<Object>) messageConverter).write(
requestBody, requestContentType, httpRequest);
return;
}
}
String message = "No HttpMessageConverter for " + requestBodyClass.getName();
if (requestContentType != null) {
message += " and content type \"" + requestContentType + "\"";
}
throw new RestClientException(message);
}
}
4. execute
前面说到默认创建的是InterceptingClientHttpRequest,接下来看看InterceptingClientHttpRequest的继承关系如下:
实际执行http请求就是executeInternal,但是从InterceptingClientHttpRequest的名字可以看出来,这个httpRequest实际上是一个调用链模式,可以根据需要拓展不同的HttpRequest,实现不同的功能,如常用的LoadBalancerInterceptor,也就是利用这个拓展能力实现LoadBalancer。
@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
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
else {
HttpMethod method = request.getMethod();
Assert.state(method != null, "No standard HTTP method");
// 创建最终执行http请求的ClientHttpRequest,factory默认为SimpleClientHttpRequestFactory,创建SimpleBufferingClientHttpRequest
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
// header全部复制
request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
// body复制
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());
}
}
// 最终http请求
return delegate.execute();
}
}
}
SimpleBufferingClientHttpRequest的继承关系
SimpleBufferingClientHttpRequest实际执行方法
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
// 复制header,是否全部复制由系统参数sun.net.http.allowRestrictedHeaders决定
// connection 为HttpURLConnection
addHeaders(this.connection, headers);
// JDK <1.8 doesn't support getOutputStream with HTTP DELETE
if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {
this.connection.setDoOutput(false);
}
if (this.connection.getDoOutput() && this.outputStreaming) {
this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
}
// http连接服务端
this.connection.connect();
if (this.connection.getDoOutput()) {
// 若有body则发送整个请求,并获取请求结果
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
}
else {
// 发送http请求
this.connection.getResponseCode();
}
return new SimpleClientHttpResponse(this.connection);
}
5. http响应解析
首先调用handleResponse处理异常情况,再调用ResponseEntityResponseExtractor中的HttpMessageConverterExtractor对响应进行装换。
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
ResponseErrorHandler errorHandler = getErrorHandler();
boolean hasError = errorHandler.hasError(response);
if (logger.isDebugEnabled()) {
try {
int code = response.getRawStatusCode();
HttpStatus status = HttpStatus.resolve(code);
logger.debug("Response " + (status != null ? status : code));
}
catch (IOException ex) {
// ignore
}
}
if (hasError) {
errorHandler.handleError(url, method, response);
}
}
private class ResponseEntityResponseExtractor<T> implements ResponseExtractor<ResponseEntity<T>> {
@Nullable
private final HttpMessageConverterExtractor<T> delegate;
public ResponseEntityResponseExtractor(@Nullable Type responseType) {
if (responseType != null && Void.class != responseType) {
this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
}
else {
this.delegate = null;
}
}
@Override
public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
if (this.delegate != null) {
T body = this.delegate.extractData(response);
return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body);
}
else {
return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build();
}
}
}
public T extractData(ClientHttpResponse response) throws IOException {
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
MediaType contentType = getContentType(responseWrapper);
try {
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericMessageConverter =
(GenericHttpMessageConverter<?>) messageConverter;
if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
if (logger.isDebugEnabled()) {
ResolvableType resolvableType = ResolvableType.forType(this.responseType);
logger.debug("Reading to [" + resolvableType + "]");
}
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
}
}
if (this.responseClass != null) {
if (messageConverter.canRead(this.responseClass, contentType)) {
if (logger.isDebugEnabled()) {
String className = this.responseClass.getName();
logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
}
return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
}
}
}
}
catch (IOException | HttpMessageNotReadableException ex) {
throw new RestClientException("Error while extracting response for type [" +
this.responseType + "] and content type [" + contentType + "]", ex);
}
throw new UnknownContentTypeException(this.responseType, contentType,
responseWrapper.getRawStatusCode(), responseWrapper.getStatusText(),
responseWrapper.getHeaders(), getResponseBody(responseWrapper));
}