Spring WebFlux ServerResponse重定向返回200及序列化异常问题
Spring WebFlux接口重定向异常分析
问题复现代码
@RestController public class GicarController { @PostMapping("/login") public Mono<ServerResponse> gicar(@RequestHeader("GICAR_ID") String gicarId) { return ServerResponse.temporaryRedirect(URI.create("/me")).build(); } }
实际异常表现
- 调用
/login端点未返回预期3xx重定向状态,实际返回200 OK,响应头为text/event-stream;charset=UTF-8类型,curl报transfer closed with outstanding read data remaining传输错误 - 服务端日志抛出Jackson序列化异常:
2022-06-27 13:11:19.931 ERROR 79654 --- [or-http-epoll-2] r.n.http.server.HttpServerOperations : [9750a9d8-1, L:/127.0.0.1:8080 - R:/127.0.0.1:33150] Error finishing response. Closing connection org.springframework.core.codec.CodecException: Type definition error: [simple type, class org.springframework.web.reactive.function.server.DefaultServerResponseBuilder$WriterFunctionResponse]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.web.reactive.function.server.DefaultServerResponseBuilder$WriterFunctionResponse and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
根因分析
两个问题本质是同一个错误触发的连锁反应:
- 编程模型混用导致重定向逻辑失效
ServerResponse是Spring WebFlux函数式Web端点(RouterFunction/HandlerFunction)专属的响应类型,仅在函数式编程模型下会被框架识别,自动处理状态码、响应头、重定向逻辑。当前代码使用@RestController属于注解式编程模型,框架不会解析ServerResponse内部封装的3xx状态码、Location响应头等重定向配置,只会把返回的Mono<ServerResponse>当成普通响应式返回值,默认按SSE规则处理,直接返回默认的200 OK状态码。 - 序列化异常触发原因
注解式模型下,框架会尝试用Jackson将返回的ServerResponse实例序列化为JSON响应体,但ServerResponse的内部实现类WriterFunctionResponse没有公共属性、也没有对应的Jackson序列化器,直接触发InvalidDefinitionException,导致响应写入流程中断、连接被强制关闭,最终curl端收到不完整响应报传输错误。
解决方案
根据开发习惯二选一即可:
方案1:保留@RestController注解式写法,改用ResponseEntity
注解式模型下实现重定向使用标准ResponseEntity即可,不需要引入函数式模型专属的ServerResponse,修正代码与原逻辑行为完全一致(307临时重定向,不会修改请求方法):
@RestController public class GicarController { @PostMapping("/login") public Mono<ResponseEntity<Void>> gicar(@RequestHeader("GICAR_ID") String gicarId) { return Mono.just(ResponseEntity .status(HttpStatus.TEMPORARY_REDIRECT) .location(URI.create("/me")) .build()); } }
方案2:改用WebFlux函数式端点写法,适配ServerResponse
如果要使用ServerResponse,就不要用@RestController注解,改为函数式路由+处理器的标准写法:
// 业务处理器 @Component public class GicarHandler { public Mono<ServerResponse> gicar(ServerRequest request) { String gicarId = request.headers().firstHeader("GICAR_ID"); return ServerResponse.temporaryRedirect(URI.create("/me")).build(); } } // 路由配置 @Configuration public class RouterConfig { @Bean public RouterFunction<ServerResponse> gicarRoute(GicarHandler handler) { return RouterFunctions.route(RequestPredicates.POST("/login"), handler::gicar); } }
内容的提问来源于stack exchange,提问作者Jordi
相关产品推荐
相关产品推荐

