当我们feign调用提供者需要携带自定义的请求头,或者想继续传递上游服务的请求头信息时,可进行如下方式处理
Configuration
注解标记为配置类header
存进去即可import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
/**
* @author lei
* @version 1.0
* @desc feign 设置请求头
* @date 2020-12-04 13:36
*/
@Configuration
public class FeignRequestConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
template.header(自定义请求头Key, 自定义请求头值);
if (headerNames == null) {
return;
}
//处理上游请求头信息,传递时继续携带
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
template.header(name, values);
}
}
}
实战示例
例如,我们需要获取到从外部请求的用户信息进行数据存储时填充(过网关的则为外部请求,在网关层做了用户JWT解析操作),这个时候呢,需要传递我们的用户信息至下游相关服务
定义拦截器
拦截网关传递的自定义的用户请求头信息
/**
* @author lei
* @version 1.0
* @desc 操作用户拦截器
* @date 2020-12-04 15:36
*/
@Log4j2
public class InterceptorConfig implements HandlerInterceptor {
public static final ThreadLocal<AccessUserInfo> ACCESS_USER_INFO = new ThreadLocal<>();
public static final String ACCESS_USER_INFO_HEADER = "access-user-info";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
AccessUserInfo accessUserInfo = JSON.parseObject(request.getHeader(ACCESS_USER_INFO_HEADER), AccessUserInfo.class);
log.debug("InterceptorConfig::preHandle {} {}", accessUserInfo, request.getRequestURI());
ACCESS_USER_INFO.set(accessUserInfo);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
ACCESS_USER_INFO.remove();
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Do nothing
}
public static String getCurrentUserId() {
return Optional.of(InterceptorConfig.ACCESS_USER_INFO).map(ThreadLocal::get).map(AccessUserInfo::getUserId).orElse(null);
}
public static String getCurrentUserIdAndThrow() {
return Optional.of(InterceptorConfig.ACCESS_USER_INFO).map(ThreadLocal::get).map(AccessUserInfo::getUserId).orElseThrow(() -> new RuntimeException("未获取到用户ID"));
}
}
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new InterceptorConfig()).addPathPatterns("/**");
}
}
自定义请求头传递(OpenFeign透传到下游服务,以便下游服务继续使用该用户信息)
@Component
@Log4j2
public class FeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
Optional.of(InterceptorConfig.ACCESS_USER_INFO)
.map(ThreadLocal::get)
.filter(accessUserInfo -> accessUserInfo.getUserId() != null)
.ifPresent(accessUserInfo -> {
log.debug("FeignInterceptor::apply InterceptorConfig::preHandle {} {}", accessUserInfo, template.toString());
template.header(InterceptorConfig.ACCESS_USER_INFO_HEADER, JSON.toJSONString(accessUserInfo));
});
}
}