Spring Cloud Gateway 动态修改请求参数解决 # URL 编码错误传参问题

Spring Cloud Gateway 动态修改请求参数解决 # URL 编码错误传参问题

继实现动态修改请求 Body 以及重试带 Body 的请求之后,我们又遇到了一个小问题。最近很多接口,收到了错误的参数,在接口层报的错是:

class org.springframework.web.method.annotation.MethodArgumentTypeMismatchException, Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: "10#scrollTop=8178" 

例如上面这个报错即本来应该是一个数字,结果收到的是 10#scrollTop=8178 导致转换异常。

正常的请求,是可以带 # 的,# 后面的部分属于 fragment。一个 URI 包括:
image

但是对于这些报错的请求,我们发现,发送的请求的原始 URI 中, # 被错误的 URL 编码了,变成了 %23,例如上面的请求,发到后端的是:

https://zhxhash@example.com:8081/test/service?id=test&number=10%23segment1

这样,后端解析到的 number 的值,就是 number=10#segment1,这样就会发生开头提到的报错。

由于前端没能复现这个问题,并且问题集中于某几个系统的浏览器版本,这个问题只能通过后台网关做修改解决。

我们的网关使用的是 Spring Cloud Gateway,我们可以针对全局请求添加全局 Filter,动态修正 URI,解决这个问题,代码如下:

@Log4j2
@Component
public class QueryNormalizationFilter implements GlobalFilter, Ordered {
    @Override
    @SneakyThrows
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String originUriString = exchange.getRequest().getURI().toString();
        if (originUriString.contains("%23")) {
            //将编码后的 %23 替换为 #,重新用这个字符串生成 URI
            URI replaced = new URI(originUriString.replace("%23", "#"));
            return chain.filter(
                    exchange.mutate()
                            .request(
                                    new ServerHttpRequestDecorator(exchange.getRequest()) {
                                        /**
                                         * 这个是影响转发到后台服务的 uri
                                         *
                                         * @return
                                         */
                                        @Override
                                        public URI getURI() {
                                            return replaced;
                                        }

                                        /**
                                         * 修改这个主要为了后面的 Filter 获取查询参数是准确的
                                         *
                                         * @return
                                         */
                                        @Override
                                        public MultiValueMap<String, String> getQueryParams() {
                                            return UriComponentsBuilder.fromUri(replaced).build().getQueryParams();
                                        }
                                    }
                            ).build()
            );
        } else {
            return chain.filter(exchange);
        }
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

注意点是:

  1. 我们需要将这个 Filter 放在最开始的位置,保证后续的 Filter 的 URI 是正确的,以免有的 Filter 拿 Fragment 做文章。
  2. 如果我们只关心转发的请求是正确的,那我们只替换 URI 即可,即覆盖 getURI 方法。
  3. 连 getQueryParams 也覆盖的原因,是后续的 Filter 可能也会对 QueryParams 做一些操作,我们要保证准确性。
  4. 只覆盖 getQueryParams,并不会修改后续转发到具体的微服务的请求的 QueryParams,这个只能通过覆盖 getURI 修改。

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在Spring Cloud Gateway中添加POST请求参数,你需要使用Spring Cloud Gateway的过滤器。下面是一个示例: ```java @Component public class AddParameterGatewayFilterFactory extends AbstractGatewayFilterFactory<AddParameterGatewayFilterFactory.Config> { public AddParameterGatewayFilterFactory() { super(Config.class); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest().mutate(). header(config.getHeaderName(), config.getHeaderValue()).build(); return chain.filter(exchange.mutate().request(request).build()); }; } public static class Config { private String headerName; private String headerValue; public String getHeaderName() { return headerName; } public void setHeaderName(String headerName) { this.headerName = headerName; } public String getHeaderValue() { return headerValue; } public void setHeaderValue(String headerValue) { this.headerValue = headerValue; } } } ``` 在这个示例中,我们创建了一个过滤器工厂类 `AddParameterGatewayFilterFactory` 以添加POST请求参数。该过滤器将添加一个名为 `headerName` 的HTTP头和值为 `headerValue` 的HTTP头,这两个参数可以从配置文件或其他地方加载。 要使用此过滤器,请将以下内容添加到Spring Cloud Gateway的配置文件中: ```yaml spring: cloud: gateway: routes: - id: add_parameter_route uri: http://example.com predicates: - Path=/example/** filters: - AddParameter=headerName=foo,headerValue=bar ``` 在这个示例中,我们定义了一个名为 `add_parameter_route` 的路由,它将匹配所有以 `/example/` 开头的路径,并将其转发到 `http://example.com`。我们还将 `AddParameter` 过滤器添加到此路由中,并将 `headerName` 设置为 `foo`,`headerValue` 设置为 `bar`。 现在,当客户端向Spring Cloud Gateway发送POST请求时,将自动添加 `foo: bar` HTTP头。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值