阅读量:0
RequestContextHolder多线程获取不到request对象,调用feign接口时,在Feign中的RequestInterceptor也获取不到HttpServletRequest问题解决方案。
1.RequestContextHolder多线程获取不到request对象
异常信息,报错如下:
2024-07-09 22:06:32.320 [pool-5-thread-2] ERROR com.mergeplus.handler.ObjectHandler:227 - class: interface org.jeecg.common.system.api.client.SysFeignClient, methodName=mergeRegion, error: {} java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:313) at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:329) at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:324) at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:283) at jdk.proxy2/jdk.proxy2.$Proxy179.getHeaderNames(Unknown Source) at com.mergeplus.handler.ObjectHandler.lambda$doHandler$0(ObjectHandler.java:155) at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1768) at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833)
异常错误截图:
2.解决方案
// 设置子线程共享(重点) final RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); merges.stream().map(e -> CompletableFuture.supplyAsync(() -> { JSONObject returnMap = null; try { if (e.getClientBean() != null) { // 设置当前线程的 RequestAttributes(重点) RequestContextHolder.setRequestAttributes(attributes); Object returnValue = e.getMethod().invoke(e.getClientBean(), jsonObject.get(e.getSourceKey())); if (returnValue == null) { return e; } returnMap = JSON.parseObject(JSON.toJSONString(returnValue)); if (returnMap == null || returnMap.isEmpty()) { return e; } result.put(e.getTargetKey(), returnMap.get(jsonObject.get(e.getSourceKey()))); } else { if (e.getUrl() == null || e.getUrl().trim().length() == 0) { return e; } //设置Http的Header HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); //设置访问参数 HashMap<String, Object> params = new HashMap<>(); if (e.getKey() == null || e.getKey().trim().length() == 0) { params.put(e.getSourceKey(), jsonObject.get(e.getSourceKey())); } //设置访问的Entity HttpEntity entity = new HttpEntity<>(params, headers); // todo ResponseEntity<Object> exchange = restTemplate.exchange(e.getUrl(), HttpMethod.GET, entity, Object.class); if (exchange == null) { return e; } Object body = exchange.getBody(); if (body == null) { return e; } returnMap = JSON.parseObject(JSON.toJSONString(body)); if (returnMap == null || returnMap.isEmpty()) { return e; } result.put(e.getTargetKey(), returnMap.get(jsonObject.get(e.getSourceKey()))); // if (body instanceof Map) { // returnMap = (Map) body; // if (returnMap == null || returnMap.isEmpty()) { // return e; // } // result.put(e.getTargetKey(), returnMap.get(jsonObject.get(e.getSourceKey()))); // } } } catch (Exception ex) { log.error("class: {}, methodName={}, error: {}", e.getClientBeanClazz(), e.getMethod().getName(), ex); } finally { RequestContextHolder.resetRequestAttributes(); // 记得在最后重置请求属性(重点) } return e; }, executor)).toList().stream().map(CompletableFuture::join).collect(Collectors.toList());
FeignInterceptorConfig代码
package org.jeecg.config; import java.io.IOException; import java.util.*; import feign.RequestTemplate; import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.jeecg.common.config.mqtoken.UserTokenContext; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.util.DateUtils; import org.jeecg.common.util.PathMatcherUtil; import org.jeecg.common.util.SpringContextUtils; import org.jeecg.common.util.TokenUtils; import org.jeecg.config.sign.interceptor.SignAuthConfiguration; import org.jeecg.config.sign.util.HttpUtils; import org.jeecg.config.sign.util.SignUtil; import org.springframework.beans.factory.ObjectFactory; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.cloud.openfeign.FeignAutoConfiguration; 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.annotation.Primary; import org.springframework.context.annotation.Scope; import org.springframework.http.MediaType; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import feign.Feign; import feign.Logger; import feign.RequestInterceptor; import feign.codec.Decoder; import feign.codec.Encoder; import feign.form.spring.SpringFormEncoder; import lombok.extern.slf4j.Slf4j; /** * @Description: FeignConfig * @author: JeecgBoot */ @ConditionalOnClass(Feign.class) @AutoConfigureBefore(FeignAutoConfiguration.class) @Slf4j @Configuration public class FeignInterceptorConfig { @Bean public RequestInterceptor requestInterceptor() { return requestTemplate -> { ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (requestAttributes == null) { return; } //1.获取请求对象 HttpServletRequest request = requestAttributes.getRequest(); Enumeration<String> headerNames = request.getHeaderNames(); if (headerNames == null || !headerNames.hasMoreElements()) { return; } //2.获取请求对象中的所有的头信息(网关传递过来的) while (headerNames.hasMoreElements()) { String name = headerNames.nextElement();//头的名称 // 跳过content-length,不然可能会报too many bites written问题 if ("content-length".equalsIgnoreCase(name)) { continue; } String value = request.getHeader(name);//头名称对应的值 // System.out.println("name:" + name + "::::::::value:" + value); //3.将头信息传递给feign (restTemplate) requestTemplate.header(name,value); // if (CommonConstant.X_ACCESS_TOKEN.equals(name) && StringUtils.isBlank(value)) { // String token = UserTokenContext.getToken(); // requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token); // } } //================================================================================================================ //针对特殊接口,进行加签验证 ——根据URL地址过滤请求 【字典表参数签名验证】 // todo if (PathMatcherUtil.matches(Arrays.asList(PathMatcherUtil.SIGN_URL_LIST),requestTemplate.path())) { // if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.SIGN_URL_LIST),requestTemplate.path())) { try { log.info("============================ [begin] fegin api url ============================"); log.info(requestTemplate.path()); log.info(requestTemplate.method()); String queryLine = requestTemplate.queryLine(); String questionMark="?"; if(queryLine!=null && queryLine.startsWith(questionMark)){ queryLine = queryLine.substring(1); } log.info(queryLine); if(requestTemplate.body()!=null){ log.info(new String(requestTemplate.body())); } SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method()); String sign = SignUtil.getParamsSign(allParams); log.info(" Feign request params sign: {}",sign); log.info("============================ [end] fegin api url ============================"); requestTemplate.header(CommonConstant.X_SIGN, sign); requestTemplate.header(CommonConstant.X_TIMESTAMP, String.valueOf(System.currentTimeMillis())); } catch (IOException e) { e.printStackTrace(); } } //================================================================================================================ }; } }
3.相关大数据学习demo地址:
https://github.com/carteryh/big-data