美文网首页
Spring Cloud Gateway 动态修改请求参数解决

Spring Cloud Gateway 动态修改请求参数解决

作者: 干货满满张哈希 | 来源:发表于2021-10-05 15:26 被阅读0次

    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 修改。

    相关文章

      网友评论

          本文标题:Spring Cloud Gateway 动态修改请求参数解决

          本文链接:https://www.haomeiwen.com/subject/wbdgnltx.html