从Zuul迁移到Spring Cloud Gateway:ServerHttpRequest转HttpServletRequest问题
问题原因
你遇到的ClassCastException是因为测试中Mock的是ServerHttpRequest通用接口实例,而非Servlet环境特有的ServletServerHttpRequest实现类。只有当Spring Cloud Gateway运行在Tomcat这类Servlet容器中时,exchange.getRequest()才会返回ServletServerHttpRequest实例,直接强转非该类型的实例必然触发转换异常。
解决方案
方案1:测试时正确Mock Servlet环境的请求/响应
修改测试代码,直接Mock Servlet环境专属的请求、响应实现类,确保强转逻辑能正常执行:
private GatewayFilterChain filterChain private static ServerWebExchange exchange; def setup() { exchange = mock(ServerWebExchange.class) // Mock Servlet环境特有的请求响应实现类 ServletServerHttpRequest servletRequest = mock(ServletServerHttpRequest.class) ServletServerHttpResponse servletResponse = mock(ServletServerHttpResponse.class) // Mock Jakarta规范的请求响应对象 HttpServletRequest jakartaRequest = mock(HttpServletRequest.class) HttpServletResponse jakartaResponse = mock(HttpServletResponse.class) filterChain = mock(GatewayFilterChain.class) when(filterChain.filter(exchange)).thenReturn(Mono.empty()) when(exchange.getRequest()).thenReturn(servletRequest) when(exchange.getResponse()).thenReturn(servletResponse) // 关联到Jakarta规范的对象,适配checkRequest方法参数 when(servletRequest.getServletRequest()).thenReturn(jakartaRequest) when(servletResponse.getServletResponse()).thenReturn(jakartaResponse) }
方案2:在过滤器中安全转换,避免直接强转
为让过滤器代码更健壮,先判断实例类型再执行转换,同时修正请求链流转逻辑(原代码返回Mono.empty()会直接终止请求链,不符合网关过滤器预期):
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 先校验是否为Servlet环境的请求响应 if (!(exchange.getRequest() instanceof ServletServerHttpRequest) || !(exchange.getResponse() instanceof ServletServerHttpResponse)) { log.warn("当前环境不支持Servlet请求响应,跳过授权检查"); return chain.filter(exchange); } ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest)exchange.getRequest(); ServletServerHttpResponse serverHttpResponse = (ServletServerHttpResponse)exchange.getResponse(); if (shouldFilter(exchange)) { try { log.debug("Invoking authorization interceptor"); if (!checkRequest( serverHttpRequest.getServletRequest(), serverHttpResponse.getServletResponse(), null)) { return Mono.error(new RuntimeException("Invalid request")); } return chain.filter(exchange); } catch (Exception exception) { log.error(ERROR_MESSAGE, exception); return Mono.error(exception); } } // 允许未命中过滤规则的请求继续流转 return chain.filter(exchange); }
方案3:通过Exchange属性直接获取Servlet请求(备用)
在Servlet环境下,Spring Cloud Gateway会将Jakarta规范的HttpServletRequest和HttpServletResponse存入ServerWebExchange的属性中,可直接通过属性键获取:
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { HttpServletRequest jakartaRequest = exchange.getAttribute(ServerWebExchangeUtils.CLIENT_REQUEST_ATTR); HttpServletResponse jakartaResponse = exchange.getAttribute(ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR); if (jakartaRequest == null || jakartaResponse == null) { log.warn("无法获取Servlet请求响应,跳过授权检查"); return chain.filter(exchange); } if (shouldFilter(exchange)) { try { log.debug("Invoking authorization interceptor"); if (!checkRequest(jakartaRequest, jakartaResponse, null)) { return Mono.error(new RuntimeException("Invalid request")); } return chain.filter(exchange); } catch (Exception exception) { log.error(ERROR_MESSAGE, exception); return Mono.error(exception); } } return chain.filter(exchange); }
该方式无需强转ServerHttpRequest,代码更简洁,但仅适用于Servlet环境。
内容的提问来源于stack exchange,提问作者cybertextron
相关产品推荐
相关产品推荐

