All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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> 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