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 包括:
但是對於這些報錯的請求,我們發現,發送的請求的原始 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;
}
}
注意點是:
- 我們需要將這個 Filter 放在最開始的位置,保證后續的 Filter 的 URI 是正確的,以免有的 Filter 拿 Fragment 做文章。
- 如果我們只關心轉發的請求是正確的,那我們只替換 URI 即可,即覆蓋 getURI 方法。
- 連 getQueryParams 也覆蓋的原因,是后續的 Filter 可能也會對 QueryParams 做一些操作,我們要保證准確性。
- 只覆蓋 getQueryParams,並不會修改后續轉發到具體的微服務的請求的 QueryParams,這個只能通過覆蓋 getURI 修改。
微信搜索“我的編程喵”關注公眾號,每日一刷,輕松提升技術,斬獲各種offer: