I am concerned, you can get the latest knowledge, the classic face questions as well as micro-technology sharing service
In the micro-service, rest
service call each other is very common, how do we gracefully called, in fact, the Spring framework uses RestTemplate
can be gracefully class rest
service to call each other, it simplifies and http
communication services, unified RESTful
standards, encapsulates http
link, the operation is simple to use, you can also customize the required definition of RestTemplate mode. among them:
RestTemplate
DefaultHttpMessageConverter
instanceHTTP
message intoPOJO
or from aPOJO
convertedHTTP
message. By default master is registeredmime
type of converter, but can alsosetMessageConverters
register a custom converter.RestTemplate
Use the defaultDefaultResponseErrorHandler
of 40XBad Request
or 50Xinternal
abnormalerror
error information capture.RestTemplate
You can also use interceptorinterceptor
, carried out on the request link tracking, and unified head settings.
Which RestTemplate
also defines a number of REST
methods of interactive resources, most of which corresponds to HTTP
the method, as follows:
method | Resolve |
---|---|
delete() | Perform HTTP DELETE operation on a specific resource URL |
exchange() | Perform a specific HTTP method in the URL, return ResponseEntity contained objects |
execute() | A method performed on a specific HTTP URL, returns an object body obtained from the response map |
getForEntity() | Sending an HTTP GET request, it returns a response body ResponseEntity contains objects mapped into |
getForObject() | Sending an HTTP GET request, it returns a request will be mapped to a target body |
postForEntity () | POST data to a URL, returns an object comprising ResponseEntity |
postForObject() | POST data to a URL, the response returns the object matching body is formed |
headForHeaders() | Sending HTTP HEAD request, it returns an HTTP header containing a specific resource URL |
optionsForAllow() | Sending HTTP OPTIONS request, and returning an Allow header information of a particular URL |
postForLocation () | POST data to a URL, returns the URL of the newly created resource |
put() | PUT resource to a specific URL |
1. RestTemplate source
1.1 default call link
restTemplate
When an API call, the default call chain:
###########1.使用createRequest创建请求########
resttemplate->execute()->doExecute()
HttpAccessor->createRequest()
//获取拦截器Interceptor,InterceptingClientHttpRequestFactory,SimpleClientHttpRequestFactory
InterceptingHttpAccessor->getRequestFactory()
//获取默认的SimpleBufferingClientHttpRequest
SimpleClientHttpRequestFactory->createRequest()
#######2.获取响应response进行处理###########
AbstractClientHttpRequest->execute()->executeInternal()
AbstractBufferingClientHttpRequest->executeInternal()
###########3.异常处理#####################
resttemplate->handleResponse()
##########4.响应消息体封装为java对象#######
HttpMessageConverterExtractor->extractData()
复制代码
1.2 restTemplate->doExecute()
In the default call chain, restTemplate
an API call will call doExecute
a method, this method is mainly the following steps may be performed:
1) using the createRequest
create request, the fetch response
2) determines whether the response abnormality exception handling
3) the response message encapsulated as java object body
@Nullable
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 {
//使用createRequest创建请求
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
//获取响应response进行处理
response = request.execute();
//异常处理
handleResponse(url, method, response);
//响应消息体封装为java对象
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();
}
}
}
复制代码
1.3 InterceptingHttpAccessor->getRequestFactory()
In the default call chain, InterceptingHttpAccessor的getRequestFactory()
the method, if no interceptor
interceptor, would return to the default SimpleClientHttpRequestFactory
, otherwise, return InterceptingClientHttpRequestFactory
to requestFactory
, can resttemplate.setInterceptors
set a custom interceptor interceptor
.
//Return the request factory that this accessor uses for obtaining client request handles.
public ClientHttpRequestFactory getRequestFactory() {
//获取拦截器interceptor(自定义的)
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();
}
}
复制代码
Then call SimpleClientHttpRequestFactory的createRequest
to create a connection:
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name());
if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
}
else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
}
}
复制代码
1.4 resttemplate->handleResponse()
In the default call chain resttemplate的handleResponse
, handling response, including exception handling, exception handling can call and setErrorHandler
set a custom method ErrorHandler
, implemented abnormality determination process and the response to the request. Customize the ErrorHandler
need to achieve ResponseErrorHandler
the interface, but Spring boot
also provides a default implementation DefaultResponseErrorHandler
, it is also possible to achieve their class through inheritance ErrorHandler
.
DefaultResponseErrorHandler
The default of 40X Bad Request
or 50X internal
abnormal error
error information capture. If you want to catch exceptions thrown by the information service itself, it requires self-realization through RestTemplate
the ErrorHandler
.
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);
}
}
复制代码
1.5 HttpMessageConverterExtractor->extractData()
In the default call chain, HttpMessageConverterExtractor
is extractData
carried out as a response message for encapsulating java
an object, you need to use message
the converter can be increased by adding a custom way messageConverter
: first acquiring a conventional messageConverter
, custom then messageConverter
added.
According to restTemplate
the setMessageConverters
source code available, using additional embodiment prevents the original messageConverter
is lost, the source:
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
//检验
validateConverters(messageConverters);
// Take getMessageConverters() List as-is when passed in here
if (this.messageConverters != messageConverters) {
//先清除原有的messageConverter
this.messageConverters.clear();
//后加载重新定义的messageConverter
this.messageConverters.addAll(messageConverters);
}
}
复制代码
HttpMessageConverterExtractor的extractData
Source:
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
//获取到response的ContentType类型
MediaType contentType = getContentType(responseWrapper);
try {
//依次循环messageConverter进行判断是否符合转换条件,进行转换java对象
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
//会根据设置的返回类型responseType和contentType参数进行匹配,选择合适的MessageConverter
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);
}
}
}
}
.....
}
复制代码
The relationship between contentType and messageConverter 1.6
In HttpMessageConverterExtractor
the extractData
process seen, based on contentType
the responseClass
selection messageConverter
is readable, the message conversion. Relationship is as follows:
The class name | Supported JavaType | Supported MediaType |
---|---|---|
ByteArrayHttpMessageConverter | byte[] | application/octet-stream, */* |
StringHttpMessageConverter | String | text/plain, */* |
ResourceHttpMessageConverter | Resource | */* |
SourceHttpMessageConverter | Source | application/xml, text/xml, application/*+xml |
AllEncompassingFormHttpMessageConverter | Map<K, List<?>> | application/x-www-form-urlencoded, multipart/form-data |
MappingJackson2HttpMessageConverter | Object | application/json, application/*+json |
Jaxb2RootElementHttpMessageConverter | Object | application/xml, text/xml, application/*+xml |
JavaSerializationConverter | Serializable | x-java-serialization;charset=UTF-8 |
FastJsonHttpMessageConverter | Object | */* |
2. springboot integrated RestTemplate
According to the source code analysis study, it can easily, easily to RestTemplate be gracefully used in the project, such as adding custom exception handling, MessageConverter
and interceptors interceptor
. As used herein, examples demo
, please see the following pages.
2.1 Import dependency: (RestTemplate integrated in the Web Start)
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
复制代码
. 2.2 RestTemplat located:
- Use
ClientHttpRequestFactory
property RestTemplat configuration parameters, for exampleConnectTimeout
,ReadTimeout
; - Add custom
interceptor
interceptors and exception handling; - Additional
message
converter; - Configure a custom exception handler.
@Configuration
public class RestTemplateConfig {
@Value("${resttemplate.connection.timeout}")
private int restTemplateConnectionTimeout;
@Value("${resttemplate.read.timeout}")
private int restTemplateReadTimeout;
@Bean
//@LoadBalanced
public RestTemplate restTemplate( ClientHttpRequestFactory simleClientHttpRequestFactory) {
RestTemplate restTemplate = new RestTemplate();
//配置自定义的message转换器
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
messageConverters.add(new CustomMappingJackson2HttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
//配置自定义的interceptor拦截器
List<ClientHttpRequestInterceptor> interceptors=new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(new HeadClientHttpRequestInterceptor());
interceptors.add(new TrackLogClientHttpRequestInterceptor());
restTemplate.setInterceptors(interceptors);
//配置自定义的异常处理
restTemplate.setErrorHandler(new CustomResponseErrorHandler());
restTemplate.setRequestFactory(simleClientHttpRequestFactory);
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory reqFactory= new SimpleClientHttpRequestFactory();
reqFactory.setConnectTimeout(restTemplateConnectionTimeout);
reqFactory.setReadTimeout(restTemplateReadTimeout);
return reqFactory;
}
}
复制代码
2.3. Component (a custom exception handler, interceptor interceptor, message converter)
Custom interceptor
interceptors to achieve ClientHttpRequestInterceptor
Interface
- Custom
TrackLogClientHttpRequestInterceptor
, recordsresttemplate
ofrequest
andresponse
information can be tracked analysis; - Custom
HeadClientHttpRequestInterceptor
, the parameter setting request header. API to send various requests, many requests need to use similar or identical Http Header. If you regard the request before eachHeader
fillHttpEntity/RequestEntity
, such code would seem rather redundant, you can set up a unified interceptor.
TrackLogClientHttpRequestInterceptor:
/**
* @Auther: ccww
* @Date: 2019/10/25 22:48,记录resttemplate访问信息
* @Description: 记录resttemplate访问信息
*/
@Slf4j
public class TrackLogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
trackRequest(request,body);
ClientHttpResponse httpResponse = execution.execute(request, body);
trackResponse(httpResponse);
return httpResponse;
}
private void trackResponse(ClientHttpResponse httpResponse)throws IOException {
log.info("============================response begin==========================================");
log.info("Status code : {}", httpResponse.getStatusCode());
log.info("Status text : {}", httpResponse.getStatusText());
log.info("Headers : {}", httpResponse.getHeaders());
log.info("=======================response end=================================================");
}
private void trackRequest(HttpRequest request, byte[] body)throws UnsupportedEncodingException {
log.info("======= request begin ========");
log.info("uri : {}", request.getURI());
log.info("method : {}", request.getMethod());
log.info("headers : {}", request.getHeaders());
log.info("request body : {}", new String(body, "UTF-8"));
log.info("======= request end ========");
}
}
复制代码
HeadClientHttpRequestInterceptor:
@Slf4j
public class HeadClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
log.info("#####head handle########");
HttpHeaders headers = httpRequest.getHeaders();
headers.add("Accept", "application/json");
headers.add("Accept-Encoding", "gzip");
headers.add("Content-Encoding", "UTF-8");
headers.add("Content-Type", "application/json; charset=UTF-8");
ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);
HttpHeaders headersResponse = response.getHeaders();
headersResponse.add("Accept", "application/json");
return response;
}
}
复制代码
Custom exception handler, inheritable DefaultResponseErrorHandler
or implemented ResponseErrorHandler
interfaces:
- Implement a custom
ErrorHandler
idea is a corresponding exception handling strategy according to the response message body, the other abnormalities parentDefaultResponseErrorHandler
for processing. - Custom
CustomResponseErrorHandler
conduct 30x exception handling
CustomResponseErrorHandler:
/**
* @Auther: Ccww
* @Date: 2019/10/28 17:00
* @Description: 30X的异常处理
*/
@Slf4j
public class CustomResponseErrorHandler extends DefaultResponseErrorHandler {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = response.getStatusCode();
if(statusCode.is3xxRedirection()){
return true;
}
return super.hasError(response);
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = response.getStatusCode();
if(statusCode.is3xxRedirection()){
log.info("########30X错误,需要重定向!##########");
return;
}
super.handleError(response);
}
}
复制代码
Custom message reformer
/**
* @Auther: Ccww
* @Date: 2019/10/29 21:15
* @Description: 将Content-Type:"text/html"转换为Map类型格式
*/
public class CustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
public CustomMappingJackson2HttpMessageConverter() {
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaType.TEXT_PLAIN);
mediaTypes.add(MediaType.TEXT_HTML); //加入text/html类型的支持
setSupportedMediaTypes(mediaTypes);// tag6
}
}
复制代码
Finally, the public may be concerned about [No.] Ccww notes, study together. Plus group, will share dry goods, as well as learning video collection every day!