org.onetwo.common.spring.rest.ExtRestTemplate Maven / Gradle / Ivy
package org.onetwo.common.spring.rest;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.onetwo.common.apiclient.RequestContextData;
import org.onetwo.common.apiclient.RestExecutor;
import org.onetwo.common.apiclient.convertor.ApiclientJackson2HttpMessageConverter;
import org.onetwo.common.apiclient.convertor.ApiclientJackson2XmlMessageConverter;
import org.onetwo.common.jackson.JacksonXmlMapper;
import org.onetwo.common.jackson.JsonMapper;
import org.onetwo.common.log.JFishLoggerFactory;
import org.onetwo.common.reflect.BeanToMapConvertor;
import org.onetwo.common.spring.utils.EnhanceBeanToMapConvertor.EnhanceBeanToMapBuilder;
import org.onetwo.common.utils.CUtils;
import org.onetwo.common.utils.ParamUtils;
import org.slf4j.Logger;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import net.jodah.typetools.TypeResolver;
public class ExtRestTemplate extends RestTemplate implements RestExecutor {
static private final Logger logger = JFishLoggerFactory.getLogger(ExtRestTemplate.class);
// private BeanToMapConvertor beanToMapConvertor = BeanToMapBuilder.newBuilder().enableUnderLineStyle().build();
private BeanToMapConvertor beanToMapConvertor = EnhanceBeanToMapBuilder.enhanceBuilder()
.enableUnderLineStyle()
.enableJsonPropertyAnnotation()
.build();
@SuppressWarnings("rawtypes")
private ExtRestErrorHandler extErrorHandler;
private Type extErrorResultType;
private Charset charset = FormHttpMessageConverter.DEFAULT_CHARSET;
private JsonMapper printMapper = JsonMapper.defaultMapper()
.addMixIns(IgnoreIOClassMixin.class,
File.class,
FileSystemResource.class,
InputStream.class);
public ExtRestTemplate(){
this(RestUtils.isOkHttp3Present()?new OkHttp3ClientHttpRequestFactory():null);
}
public void setExtErrorHandler(ExtRestErrorHandler> extErrorHandler) {
// this.extErrorResultType = GenericTypeResolver.resolveTypeArgument(ExtRestErrorHandler.class, extErrorHandler.getClass());
this.extErrorResultType = TypeResolver.resolveRawArgument(ExtRestErrorHandler.class, extErrorHandler.getClass());
this.extErrorHandler = extErrorHandler;
}
public ExtRestTemplate(ClientHttpRequestFactory requestFactory){
super();
/*CUtils.findByClass(this.getMessageConverters(), MappingJackson2HttpMessageConverter.class)
.ifPresent(p->{
MappingJackson2HttpMessageConverter convertor = p.getValue();
convertor.setObjectMapper(JsonMapper.IGNORE_NULL.getObjectMapper());
convertor.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,
MediaType.APPLICATION_JSON_UTF8,
MediaType.TEXT_PLAIN));
});*/
CUtils.replaceOrAdd(getMessageConverters(), MappingJackson2HttpMessageConverter.class, new ApiclientJackson2HttpMessageConverter());
CUtils.replaceOrAdd(getMessageConverters(), MappingJackson2XmlHttpMessageConverter.class, new ApiclientJackson2XmlMessageConverter());
applyDefaultCharset();
if(requestFactory!=null){
this.setRequestFactory(requestFactory);
// this.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
}
this.setErrorHandler(new OnExtRestErrorHandler());
}
private void applyDefaultCharset() {
for (HttpMessageConverter> candidate : this.getMessageConverters()) {
if (candidate instanceof AbstractHttpMessageConverter) {
AbstractHttpMessageConverter> converter = (AbstractHttpMessageConverter>) candidate;
if (converter.getDefaultCharset() != null) {
converter.setDefaultCharset(this.charset);
}
}
}
}
public final ExtRestTemplate addMessageConverters(HttpMessageConverter>... elements){
getMessageConverters().addAll(Arrays.asList(elements));
return this;
}
public final ExtRestTemplate replaceOrAddMessageConverter(Class extends HttpMessageConverter>> targetClass, HttpMessageConverter> element){
CUtils.replaceOrAdd(getMessageConverters(), targetClass, element);
return this;
}
public void setBeanToMapConvertor(BeanToMapConvertor beanToMapConvertor) {
this.beanToMapConvertor = beanToMapConvertor;
}
@Override
public ResponseEntity execute(RequestContextData context) {
try {
RestExecuteThreadLocal.set(context);
return doExecute(context);
} finally {
RestExecuteThreadLocal.remove();
}
}
protected ResponseEntity doExecute(RequestContextData context) {
RequestCallback rc = null;
HttpMethod method = context.getHttpMethod();
ResponseExtractor> responseExtractor = null;
HttpHeaders headers = context.getHeaders();
HttpEntity> requestEntity = null;
if(method==HttpMethod.GET) {
// rc = super.acceptHeaderRequestCallback(context.getResponseType());
// responseExtractor = responseEntityExtractor(context.getResponseType());
//根据consumers 设置header,以指定messageConvertor
// headers = new HttpHeaders();
// context.acceptHeaderCallback(headers);
requestEntity = new HttpEntity<>(headers);
rc = super.httpEntityCallback(requestEntity, context.getResponseType());
responseExtractor = responseEntityExtractor(context.getResponseType());
}else if(RestUtils.isRequestBodySupportedMethod(method)){
// headers = new HttpHeaders();
//根据consumers 设置header,以指定messageConvertor
// context.acceptHeaderCallback(headers);
// Object requestBody = context.getRequestBodySupplier().get();
// Object requestBody = context.getRequestBodySupplier().getRequestBody(context);
Object requestBody = context.getRequestBody();
logData(requestBody, headers);
requestEntity = new HttpEntity<>(requestBody, headers);
rc = super.httpEntityCallback(requestEntity, context.getResponseType());
responseExtractor = responseEntityExtractor(context.getResponseType());
}else{
throw new RestClientException("unsupported method: " + method);
}
// if(context.hasApiRequestCallback()){
// rc = wrapRequestCallback(context, rc);
// }
rc = wrapRequestCallback(context, rc);
return execute(context.getRequestUrl(), method, rc, responseExtractor, context.getUriVariables());
}
private void logData(Object requestBody, HttpHeaders headers) {
if(isLogableObject(requestBody) && logger.isDebugEnabled()){
//打印时不能使用toJson,会破坏某些特殊对象,比如resource
try {
if (requestBody instanceof MultiValueMap) {
MultiValueMap, ?> mm = (MultiValueMap, ?>) requestBody;
boolean logable = mm.entrySet().stream().flatMap(entry -> entry.getValue().stream()).allMatch(obj -> {
return isLogableObject(obj);
});
if (!logable) {
logger.debug("requestBody is not printable!");
return ;
}
}
MediaType ct = headers.getContentType();
if (ct.isCompatibleWith(MediaType.APPLICATION_XML)) {
JacksonXmlMapper printMapper = JacksonXmlMapper.defaultMapper();
logger.debug("requestBody for xml: {}", printMapper.toXml(requestBody));
} else {
logger.debug("requestBody for json: {}", printMapper.toJson(requestBody));
}
} catch (Exception e) {
// ignore
logger.debug("requestBody {} : {}", requestBody.getClass(), requestBody);
}
}
}
protected boolean isLogableObject(Object body) {
return body!=null &&
!Resource.class.isInstance(body) &&
!File.class.isInstance(body) &&
!InputStream.class.isInstance(body);
}
@Override
protected T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor) throws RestClientException {
RequestContextData ctx = RestExecuteThreadLocal.get();
if(ctx!=null && logger.isDebugEnabled()){
logger.debug("rest requestId[{}] : {} - {}", ctx.getRequestId(), method, url);
}
return super.doExecute(url, method, requestCallback, responseExtractor);
}
public RequestCallback wrapRequestCallback(RequestContextData context, RequestCallback acceptHeaderRequestCallback) {
return new ProcessApiClientRequestCallback(context, acceptHeaderRequestCallback);
}
public ResponseEntity getForEntity(String url, Class responseType, RequestCallback requestCallback, Map uriVariables) throws RestClientException {
ResponseExtractor> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
public T post(String url, Object request, Class responseType){
ResponseEntity response = postForEntity(url, RestUtils.createFormEntity(request, beanToMapConvertor), responseType);
if(HttpStatus.OK.equals(response.getStatusCode())){
return response.getBody();
}
throw new RestClientException("invoke rest interface["+url+"] error: " + response);
}
public T post(String url, Object request, MediaType mediaType, Class responseType){
ResponseEntity response = postForEntity(url, RestUtils.createHttpEntity(request, mediaType), responseType);
if(HttpStatus.OK.equals(response.getStatusCode())){
return response.getBody();
}
throw new RestClientException("invoke rest interface["+url+"] error: " + response);
}
@SuppressWarnings("unchecked")
public Map getAsMap(String url, Object request){
return get(url, request, HashMap.class);
}
public T get(String url, Object request, Class responseType){
String paramString = RestUtils.propertiesToParamString(request);
// 使用toMultiValueMap,spring不会把参数值List展开获取,而是直接toString,从而导致参数错误
// MultiValueMap urlVariables = RestUtils.toMultiValueMap(request, beanToMapConvertor);
Map urlVariables = beanToMapConvertor.toMap(request);
if (logger.isDebugEnabled()) {
logger.debug("url Variables: {}", urlVariables);
}
String atualUrl = ParamUtils.appendParamString(url, paramString);
return getForObject(atualUrl, responseType, urlVariables);
}
public T get(String url, Class responseType, Object...urlVariables){
ResponseEntity response = getForEntity(url, responseType, urlVariables);
if(HttpStatus.OK.equals(response.getStatusCode())){
return response.getBody();
}
throw new RestClientException("invoke rest interface["+url+"] error: " + response);
}
protected static class ProcessApiClientRequestCallback implements RequestCallback {
private final RequestCallback acceptHeaderRequestCallback;
// private final Consumer callback;
private RequestContextData context;
public ProcessApiClientRequestCallback(RequestContextData context, RequestCallback acceptHeaderRequestCallback) {
super();
this.acceptHeaderRequestCallback = acceptHeaderRequestCallback;
this.context = context;
// this.callback = context.getHeaderCallback();
}
@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
//再次回调header callback,此时为实际请求的header,前一次调用为匹配messageConverter
// this.callback.accept(request.getHeaders());
// this.context.acceptRequestCallback(request.getHeaders());
request.getHeaders().putAll(context.getHeaders());
// this.context.acceptRequestCallback(request);
/*if(ReflectUtils.getFieldsAsMap(acceptHeaderRequestCallback.getClass()).containsKey("requestEntity")){
HttpEntity> requestEntity = (HttpEntity>) ReflectUtils.getFieldValue(acceptHeaderRequestCallback, "requestEntity");
if(requestEntity!=null){
this.callback.accept(requestEntity.getHeaders());
}
}*/
if(logger.isDebugEnabled()){
logger.debug("requestId[{}] header: {}", context.getRequestId(), request.getHeaders());
}
this.acceptHeaderRequestCallback.doWithRequest(request);
}
}
@SuppressWarnings("unchecked")
//AccessTokenErrorHandler
public class OnExtRestErrorHandler extends DefaultResponseErrorHandler {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
ResponseExtractor> responseExtractor = null;
if(extErrorResultType!=null){
responseExtractor = responseEntityExtractor(extErrorResultType);
}
if (responseExtractor != null) {
ResponseEntity> errorData = responseExtractor.extractData(response);
extErrorHandler.onError(errorData.getBody());
}else{
super.handleError(response);
}
}
}
public void setCharset(Charset charset) {
this.charset = charset;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy