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: