【Spring Cloud】Feign传递HttpServletRequest

当使用Feign进行服务间调用时,由于尝试从HttpServletRequest中获取已经读取过的token,导致JsonMappingException。解决方法包括创建Feign配置类实现RequestInterceptor,重写apply方法以添加请求头,修改Feign接口的注解添加请求头参数,并在调用方注入FeignClient并传入请求头参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        这里我的业务场景是:在请求头中获取服务端登录时传给客户端的token,并且客户端将token放在请求头中。以至于我需要在参数传递上传入HttpServletRequest。如果你非要向我一样传入HttpServletRequest对象那么就往下看,当然你如果可以改成其他参数传递就不需要往下看了。

使用Feign所需要的必需知识别忘记:

1、需要在调用方以及被调用方以及Feign接口模块引入依赖

2、被调用方与调用方的主启动类均需要加上@EnableFeignClients注解

3、被调用方的注解后面还需要加扫描路径,对于我本人项目则是@EnableFeignClients(basePackages = "com.chf")

4、被调用方的控制层(Controller)的方法需要是public。因为写在Feign接口中的方法就是默认的public static final。所以说两个的访问权限修饰符必须是一致的。

5、将被调用方的方法copy到Feign接口中,这里需要注意三点:

一、在@RequestMapper/REST风格的请求路径中需要被调用方的全路径名

二、Feign接口需要两个注解:@Component以及@FeignClient

三、@FeignClient注解中需要加上被调用方的服务名称("spring:application:name")

feign.codec.EncodeException: Could not write JSON: getInputStream() has already been called for this request; nested exception is com.fasterxml.jackson.databind.JsonMappingException: getInputStream() has already been called for this request (through reference chain: org.apache.catalina.connector.RequestFacade["reader"])
	at feign.ReflectiveFeign$BuildEncodedTemplateFromArgs.resolve(ReflectiveFeign.java:376)
	at feign.ReflectiveFeign$BuildTemplateByResolvingArgs.create(ReflectiveFeign.java:224)
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:75)
	at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
	at com.sun.proxy.$Proxy81.concertCount(Unknown Source)
	at com.chf.service.impl.ApiEChartsServiceImpl.ticketCount(ApiEChartsServiceImpl.java:25)
	at com.chf.controller.api.ApiEChartsController.ticketCount(ApiEChartsController.java:25)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: getInputStream() has already been called for this request; nested exception is com.fasterxml.jackson.databind.JsonMappingException: getInputStream() has already been called for this request (through reference chain: org.apache.catalina.connector.RequestFacade["reader"])
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:296)
	at org.springframework.http.converter.AbstractGenericHttpMessageConverter.writeInternal(AbstractGenericHttpMessageConverter.java:113)
	at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:227)
	at org.springframework.cloud.openfeign.support.SpringEncoder.encode(SpringEncoder.java:114)
	at feign.ReflectiveFeign$BuildEncodedTemplateFromArgs.resolve(ReflectiveFeign.java:372)
	... 56 more
Caused by: com.fasterxml.jackson.databind.JsonMappingException: getInputStream() has already been called for this request (through reference chain: org.apache.catalina.connector.RequestFacade["reader"])
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394)
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:353)
	at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:316)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:729)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:166)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
	at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1433)
	at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:921)
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:287)
	... 60 more
Caused by: java.lang.IllegalStateException: getInputStream() has already been called for this request
	at org.apache.catalina.connector.Request.getReader(Request.java:1208)
	at org.apache.catalina.connector.RequestFacade.getReader(RequestFacade.java:504)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:688)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:721)
	... 66 more

        报这个错误的原因主要是:Feign没有接受HttpServletRequest参数。以下三点是处理这个问题的解决办法。第一:在Feign模块中写一个配置类实现RequestInterceptor重写apply()方法。

二、修改Feign接口中的两处地方。三、在调用方调用Feign接口地方改为请求头中的参数。

@Component
public class FeignConfig implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        requestTemplate.header("token",request.getHeader("token"));
    }
}
@Component
// 第一处更改的位置:加上configuration
@FeignClient(value = "service:application:name",configuration = FeignConfig.class)
public interface FeignClient {

    @GetMapping("全路径名")
    // 第二处更改的位置:加上@RequestHeader注解并且带上需要的请求头类型
    Integer count(@RequestHeader("token") String token);
}
@Service
public class AServiceImpl implements AService {

    @Autowired
    private FeignClient feignClient;

    @Override
    public Integer personCount(HttpServletRequest request) {
        String token = request.getHeader("token");
        return feignClient.count(token);
    }
}

<think>好的,我现在需要回答用户关于Spring Boot核心注解、JDBC集成以及Spring Cloud中Zuul网关的问题。首先,我需要回忆一下Spring Boot的核心注解,比如@SpringBootApplication、@RestController、@Service等,这些都是开发中常用的。然后关于JDBC集成,用户可能需要了解如何配置数据源,使用JdbcTemplate,或者Spring Data JPA的相关内容。接下来是Zuul网关,作为Spring Cloud的一部分,Zuul的作用是路由和过滤请求,需要说明如何配置和使用。 用户可能还需要具体的代码示例,比如如何在Spring Boot应用中连接数据库,或者如何设置Zuul的路由规则。同时,用户提到微服务架构,可能需要解释Spring Boot和Spring Cloud在其中的角色,比如如何通过Zuul实现API网关,以及与其他组件如Eureka、Feign的协作。 还要注意引用用户提供的参考内容,例如在讲解微服务架构时,可以引用[^1]中的内容说明Spring Boot和Spring Cloud的关系。在提到配置管理时,参考[^2]中的配置示例。关于Feign的拦截器,可以参考[^3]中的代码示例,不过Zuul和Feign都属于Spring Cloud的不同模块,需要明确区分它们的作用。 可能需要检查是否有遗漏的注解或配置步骤,确保示例代码的正确性。比如,使用JDBC时需要添加相关依赖,配置数据源信息,然后在代码中注入JdbcTemplate。对于Zuul,需要添加Zuul的依赖,启用@EnableZuulProxy,并在配置文件中定义路由规则。 还需要考虑用户的学习路径,先掌握Spring Boot的基础,再学习如何集成数据库,最后进入微服务架构中的网关部分。所以回答的结构应该按照这个顺序展开,逐步引导用户理解和实践。 最后,生成相关问题时要覆盖核心注解、JDBC集成、Zuul配置以及微服务架构的其他组件,帮助用户深入学习和解决问题。</think>以下内容将分步骤讲解Spring Boot核心注解、JDBC集成及Zuul网关的实现: # 一、Spring Boot核心注解 1. **@SpringBootApplication** 组合注解,包含@Configuration + @EnableAutoConfiguration + @ComponentScan,是主启动类的标志性注解 ```java @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 2. **@RestController** 组合@Controller和@ResponseBody,用于构建RESTful API ```java @RestController public class UserController { @GetMapping("/users") public List<User> getUsers() { // 业务逻辑 } } ``` 3. **@Service/@Repository** 分层架构注解,分别标注服务层和数据访问层组件 # 二、JDBC集成示例 1. **添加依赖** ```xml <!-- pom.xml --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> ``` 2. **配置数据源** ```properties # application.properties spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password= ``` 3. **使用JdbcTemplate** ```java @Repository public class UserRepository { @Autowired private JdbcTemplate jdbcTemplate; public List<User> findAll() { return jdbcTemplate.query( "SELECT * FROM users", (rs, rowNum) -> new User( rs.getInt("id"), rs.getString("name") ) ); } } ``` # 三、Zuul网关实现 1. **创建Zuul服务** ```xml <!-- 添加依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> ``` 2. **启用网关功能** ```java @EnableZuulProxy @SpringBootApplication public class ZuulGatewayApplication { public static void main(String[] args) { SpringApplication.run(ZuulGatewayApplication.class, args); } } ``` 3. **路由配置** ```yaml # application.yml zuul: routes: user-service: path: /api/users/** serviceId: user-service order-service: path: /api/orders/** url: http://localhost:8082/ ``` 4. **过滤器示例** ```java public class AuthFilter extends ZuulFilter { @Override public String filterType() { return "pre"; // 前置过滤器 } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); // 实现鉴权逻辑 return null; } } ``` # 四、微服务架构实践 在Spring Cloud架构中,Zuul通常与Eureka服务注册中心配合使用实现动态路由,配合Hystrix实现熔断保护[^2]。配置中心可采用Spring Cloud Config实现集中管理,如引用中展示的配置方式。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值