You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

根因分析

两个问题本质是同一个错误触发的连锁反应:

  1. 编程模型混用导致重定向逻辑失效
    ServerResponse是Spring WebFlux函数式Web端点(RouterFunction/HandlerFunction)专属的响应类型,仅在函数式编程模型下会被框架识别,自动处理状态码、响应头、重定向逻辑。当前代码使用@RestController属于注解式编程模型,框架不会解析ServerResponse内部封装的3xx状态码、Location响应头等重定向配置,只会把返回的Mono<ServerResponse>当成普通响应式返回值,默认按SSE规则处理,直接返回默认的200 OK状态码。
  2. 序列化异常触发原因
    注解式模型下,框架会尝试用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 09:51:46