package com.vci.starter.web.autoconfigure;
|
|
import com.alibaba.fastjson.JSONObject;
|
import com.vci.starter.web.constant.TokenKeyConstant;
|
import com.vci.starter.web.pagemodel.SessionInfo;
|
import com.vci.starter.web.util.VciBaseUtil;
|
import feign.*;
|
import feign.form.spring.SpringFormEncoder;
|
import feign.okhttp.OkHttpClient;
|
import okhttp3.ConnectionPool;
|
import org.slf4j.MDC;
|
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
|
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
|
import org.springframework.cloud.openfeign.support.SpringDecoder;
|
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.StringHttpMessageConverter;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
|
import javax.annotation.PreDestroy;
|
import java.nio.charset.StandardCharsets;
|
import java.util.Map;
|
import java.util.concurrent.TimeUnit;
|
|
/**
|
* feign客户端配置
|
* @author weidy
|
* @date 2019/11/5 10:23 AM
|
*/
|
@AutoConfigureAfter(AppAutoConfigure.class)
|
@Configuration
|
public class FeignConfig implements RequestInterceptor {
|
|
/**
|
* 使用okHttpClient来传输数据
|
*/
|
private okhttp3.OkHttpClient okHttpClient;
|
|
/**
|
* 当前应用程序的私钥
|
*/
|
@Value("${app.privateTokenKey:}")
|
private String privateTokenKey;
|
|
/**
|
* 客户端连接池
|
* @param httpClientProperties http链接配置
|
* @param connectionPoolFactory 连接池工程
|
* @return 连接池
|
*/
|
@Bean
|
@ConditionalOnMissingBean({ConnectionPool.class})
|
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
Long timeToLive = httpClientProperties.getTimeToLive();
|
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
|
}
|
|
/**
|
* 初始化okHttp的客户端对象
|
* @param httpClientFactory 客户端链接工程
|
* @param connectionPool 连接池
|
* @param httpClientProperties 客户端链接配置
|
* @return
|
*/
|
@Bean
|
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
|
Boolean followRedirects = httpClientProperties.isFollowRedirects();
|
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
|
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
|
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).connectTimeout((long)connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects).readTimeout(30*60,TimeUnit.SECONDS).connectionPool(connectionPool).build();
|
return this.okHttpClient;
|
}
|
|
/**
|
* 销毁之前关闭okHttp
|
*/
|
@PreDestroy
|
public void destroy() {
|
if (this.okHttpClient != null) {
|
this.okHttpClient.dispatcher().executorService().shutdown();
|
this.okHttpClient.connectionPool().evictAll();
|
}
|
|
}
|
|
/**
|
* 配置feign的客户端类
|
* @param client 客户端链接对象
|
* @return 客户端连接对象
|
*/
|
@Bean
|
@ConditionalOnMissingBean({Client.class})
|
public Client feignClient(okhttp3.OkHttpClient client) {
|
return new OkHttpClient(client);
|
}
|
|
/**
|
* 设置请求的连接时间和读取的时间
|
* @param client 链接的客户端
|
* @return 链接的客户端
|
*/
|
@Bean
|
public Request.Options feignOptions(okhttp3.OkHttpClient client) {
|
return new Request.Options(client.connectTimeoutMillis(), client.readTimeoutMillis());
|
}
|
|
/**
|
* 配置feign的日志等级
|
* @return 日志等级
|
*/
|
@Bean
|
Logger.Level feignLoggerLevel() {
|
//这里记录所有,根据实际情况选择合适的日志level
|
return Logger.Level.FULL;
|
}
|
|
/**
|
* 将fastjson设置到decoder中,解码器
|
* @return decoder
|
*/
|
@Bean
|
public ResponseEntityDecoder feignDecoder(){
|
HttpMessageConverter fastJsonConverter = SpringMVCConfig.createFastJsonConverter();
|
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(new StringHttpMessageConverter(StandardCharsets.UTF_8),fastJsonConverter);
|
return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
|
}
|
|
/**
|
* 将fastjson设置到encoder中,编码器
|
* @return encoder
|
*/
|
@Bean
|
public SpringFormEncoder feignEncoder(){
|
HttpMessageConverter fastJsonConverter = SpringMVCConfig.createFastJsonConverter();
|
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(new StringHttpMessageConverter(StandardCharsets.UTF_8),fastJsonConverter);
|
return new SpringFormEncoder(new SpringEncoder(objectFactory));
|
}
|
|
|
/**
|
* 在发送请求的时候,设置拦截器,将用户的token和当前服务的私钥传递过去,并且传递当前服务的名称用于追溯日志
|
* @param requestTemplate 请求的目标
|
*/
|
@Override
|
public void apply(RequestTemplate requestTemplate) {
|
//如果是GET,但是body里有内容
|
Request.Body body = requestTemplate.requestBody();
|
if(RequestMethod.GET.toString().equalsIgnoreCase(requestTemplate.method())
|
&& body != null && body.length() > 0){
|
//主要是列表查询和树查询这几个地方,支持map的传输
|
String bodyString = new String(body.asBytes(),requestTemplate.requestCharset());
|
try{
|
JSONObject jsonObject = JSONObject.parseObject(bodyString);
|
for(String key : jsonObject.keySet()){
|
Object value = jsonObject.get(key);
|
if(value == null){
|
requestTemplate.query(key,"");
|
}else{
|
if(value instanceof Map){
|
//说明这个属性是map
|
Map mapValues = (Map)value;
|
for(Object mapKey : mapValues.keySet()){
|
Object mapQueryValue = mapValues.get(mapKey);
|
String mapQueryValueString = "";
|
if(mapQueryValue == null){
|
mapQueryValueString = "";
|
}else{
|
mapQueryValueString = VciBaseUtil.getStringValueFromObject(mapQueryValue);
|
}
|
requestTemplate.query(key + "[\"" + mapKey.toString() + "\"]",mapQueryValueString);
|
}
|
}else{
|
requestTemplate.query(key,VciBaseUtil.getStringValueFromObject(value));
|
}
|
}
|
}
|
requestTemplate.body(Request.Body.empty());
|
}catch (Throwable e){
|
//可能不是json的类型
|
}
|
}
|
|
//需要传递用户的token
|
//系统的token,用于权限验证
|
//日志的链接ID
|
SessionInfo sessionInfo = VciBaseUtil.getCurrentUserSessionInfoNotException();
|
if(sessionInfo != null){
|
requestTemplate.header(TokenKeyConstant.USER_TOKEN_KEY,sessionInfo.getToken());
|
}else{
|
requestTemplate.header(TokenKeyConstant.USER_TOKEN_KEY,"");
|
}
|
requestTemplate.header(TokenKeyConstant.SYSTEM_PRIVATE_KEY,getPrivateTokenKey());
|
|
String logTraceId = MDC.get(TokenKeyConstant.TRACE_ID);
|
if(logTraceId == null){
|
logTraceId = "";
|
}
|
requestTemplate.header(TokenKeyConstant.LOG_TRACE_ID_KEY,logTraceId);
|
//设置语言
|
requestTemplate.header(TokenKeyConstant.LANGUAGE_KEY,LocaleContextHolder.getLocale().toLanguageTag());
|
}
|
|
public String getPrivateTokenKey() {
|
return privateTokenKey;
|
}
|
|
public void setPrivateTokenKey(String privateTokenKey) {
|
this.privateTokenKey = privateTokenKey;
|
}
|
|
}
|