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));}

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...