Spring RestTemplate请求过程
创始人
2024-03-29 04:27:32
0

文章目录

  • 前言
  • 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  ResponseEntity exchange(String url, HttpMethod method, @Nullable HttpEntity requestEntity,ParameterizedTypeReference responseType, Object... uriVariables) throws RestClientException {Type type = responseType.getType();RequestCallback requestCallback = httpEntityCallback(requestEntity, type);ResponseExtractor> responseExtractor = responseEntityExtractor(type);return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
}

http的整体流程如下所示,首先创建httpRequest,其次处理httpRequest,最重要就是执行http请求,最后就是处理http响应。

protected  T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,@Nullable ResponseExtractor responseExtractor) throws RestClientException {Assert.notNull(url, "URI is required");Assert.notNull(method, "HttpMethod is required");ClientHttpResponse response = null;try {// 创建httpRequestClientHttpRequest request = createRequest(url, method);if (requestCallback != null) {// HttpEntityRequestCallback#doWithRequestrequestCallback.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 interceptors = getInterceptors();if (!CollectionUtils.isEmpty(interceptors)) {ClientHttpRequestFactory factory = this.interceptingRequestFactory;if (factory == null) {// 创建默认的HttpRequestFactoryfactory = 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)));}// 设置ContentLengthif (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判断是否支持写当前httpRequestBodyif (messageConverter instanceof GenericHttpMessageConverter) {GenericHttpMessageConverter genericConverter =(GenericHttpMessageConverter) 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序列化写入httpRequestgenericConverter.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) 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 iterator;public InterceptingRequestExecution() {this.iterator = interceptors.iterator();}@Overridepublic ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {if (this.iterator.hasNext()) {// 调用链依次调用ClientHttpRequestInterceptorClientHttpRequestInterceptor 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,创建SimpleBufferingClientHttpRequestClientHttpRequest 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 为HttpURLConnectionaddHeaders(this.connection, headers);// JDK <1.8 doesn't support getOutputStream with HTTP DELETEif (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 implements ResponseExtractor> {@Nullableprivate final HttpMessageConverterExtractor delegate;public ResponseEntityResponseExtractor(@Nullable Type responseType) {if (responseType != null && Void.class != responseType) {this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);}else {this.delegate = null;}}@Overridepublic ResponseEntity 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));}

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...