解決spring cloud gateway 獲取body內(nèi)容并修改的問題
之前寫過一篇文章,如何獲取body的內(nèi)容。
Spring Cloud Gateway獲取body內(nèi)容,不影響GET請求
確實能夠獲取所有body的內(nèi)容了,不過今天終端同學調(diào)試接口的時候和我說,遇到了400的問題,報錯是這樣的HTTP method names must be tokens,搜了一下,都是說https引起的。可我的項目還沒用https,排除了。
想到是不是因為修改了body內(nèi)容導致的問題,試著不修改body的內(nèi)容,直接傳給微服務,果然沒有報錯了。
問題找到,那就好辦了,肯定是我新構(gòu)建的REQUEST對象缺胳膊少腿了,搜索一通之后發(fā)現(xiàn)一篇大牛寫的文章:
Spring Cloud Gateway(讀取、修改 Request Body)
這里要再次表揚一下古哥,同樣是中文文章,度娘卻搜不到
不過文章中的spring cloud版本是
Spring Cloud: Greenwich.RC2
我本地是最新的Release版本RS3,并不能完全照搬過來,不過算是給了很大的啟發(fā)(如何獲取body以及重構(gòu))
下面給出我的代碼
網(wǎng)關中對body內(nèi)容進行解密然后驗簽
/** * @author tengdj * @date 2019/8/13 11:08 * 設備接口驗簽,解密 **/ @Slf4j public class TerminalSignFilter implements GatewayFilter, Ordered { private static final String AES_SECURTY = "XXX"; private static final String MD5_SALT = "XXX"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { exchange.getAttributes().put("startTime", System.currentTimeMillis()); if (exchange.getRequest().getMethod().equals(HttpMethod.POST)) { //重新構(gòu)造request,參考ModifyRequestBodyGatewayFilterFactory ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders()); MediaType mediaType = exchange.getRequest().getHeaders().getContentType(); //重點 Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> { //因為約定了終端傳參的格式,所以只考慮json的情況,如果是表單傳參,請自行發(fā)揮 if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON_UTF8.isCompatibleWith(mediaType)) { JSONObject jsonObject = JSONUtil.toJO(body); String paramStr = jsonObject.getString("param"); String newBody; try{ newBody = verifySignature(paramStr); }catch (Exception e){ return processError(e.getMessage()); } return Mono.just(newBody); } return Mono.empty(); }); BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class); HttpHeaders headers = new HttpHeaders(); headers.putAll(exchange.getRequest().getHeaders()); //猜測這個就是之前報400錯誤的元兇,之前修改了body但是沒有重新寫content length headers.remove("Content-Length"); //MyCachedBodyOutputMessage 這個類完全就是CachedBodyOutputMessage,只不過CachedBodyOutputMessage不是公共的 MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headers); return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> { ServerHttpRequest decorator = this.decorate(exchange, headers, outputMessage); return returnMono(chain, exchange.mutate().request(decorator).build()); })); } else { //GET 驗簽 MultiValueMap<String, String> map = exchange.getRequest().getQueryParams(); if (!CollectionUtils.isEmpty(map)) { String paramStr = map.getFirst("param"); try{ verifySignature(paramStr); }catch (Exception e){ return processError(e.getMessage()); } } return returnMono(chain, exchange); } } @Override public int getOrder() { return 1; } private Mono<Void> returnMono(GatewayFilterChain chain,ServerWebExchange exchange){ return chain.filter(exchange).then(Mono.fromRunnable(()->{ Long startTime = exchange.getAttribute("startTime"); if (startTime != null){ long executeTime = (System.currentTimeMillis() - startTime); log.info("耗時:{}ms" , executeTime); log.info("狀態(tài)碼:{}" , Objects.requireNonNull(exchange.getResponse().getStatusCode()).value()); } })); } private String verifySignature(String paramStr) throws Exception{ log.info("密文{}", paramStr); String dParamStr; try{ dParamStr = AESUtil.decrypt(paramStr, AES_SECURTY); }catch (Exception e){ throw new Exception("解密失?。?); } log.info("解密得到字符串{}", dParamStr); String signature = SignUtil.sign(dParamStr, MD5_SALT); log.info("重新加密得到簽名{}", signature); JSONObject jsonObject1 = JSONUtil.toJO(dParamStr); if (!jsonObject1.getString("signature").equals(signature)) { throw new Exception("簽名不匹配!"); } return jsonObject1.toJSONString(); } private Mono processError(String message) { /*exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete();*/ log.error(message); return Mono.error(new Exception(message)); } ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, MyCachedBodyOutputMessage outputMessage) { return new ServerHttpRequestDecorator(exchange.getRequest()) { public HttpHeaders getHeaders() { long contentLength = headers.getContentLength(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); if (contentLength > 0L) { httpHeaders.setContentLength(contentLength); } else { httpHeaders.set("Transfer-Encoding", "chunked"); } return httpHeaders; } public Flux<DataBuffer> getBody() { return outputMessage.getBody(); } }; } }
代碼到這里就結(jié)束了,希望看到的朋友可以少走點彎路,少踩點坑。
補充知識:springcloud gateway之a(chǎn)ddRequestParameter詳細使用及踩坑注意
SpringCloud的網(wǎng)關gateway提供了多個內(nèi)置Filter,其中addRequestHeader是添加header的,這個無坑,比較簡單。還有一個添加參數(shù)的,addRequestParameter,這個就有點問題了。具體往下看。
版本如下,請注意Springboot版本,這是本篇Post請求異常的關鍵。
1 對應的uri只能是get請求
看一個簡單的示例,addRequestParameter,我們匹配/addParam請求,并將請求轉(zhuǎn)發(fā)至http://localhost:8888/header
這個是8888端口的服務
如果發(fā)起Get請求到網(wǎng)關,那么可以正常請求,一切OK。此時,調(diào)用發(fā)起方和最終的服務提供方都是Get請求,沒有問題。
如果發(fā)起的請求是Get,但是服務提供方是如下的Post。
注意,這里我用了PostMapping,然后分別啟動兩個工程,再訪問localhost:8080/addParam,而后會報錯,這個也可以理解。
但是,如果調(diào)用發(fā)起方和服務提供方都是Post請求,理論上應該也是OK的。
但是事實上不是的
網(wǎng)關程序會報錯如下:
這個就很尷尬了,作為一個網(wǎng)關,居然在代理非Get請求時出現(xiàn)異常,必然是不能容忍的。
經(jīng)過一番探索,發(fā)現(xiàn)這是Springboot不同版本的原因?qū)е?,在Springboot2.0.5之前,不存在該問題,之后就有這種問題了。需要加以注意,解決方案會在下一篇寫。
2 添加的參數(shù)value值必須合法(不能含有空格)
上面已經(jīng)知道了,addRequestParameter對應的后端請求是Get型,那么明顯添加的parameter只能是Get請求支持的,能在瀏覽器地址欄直接敲上去合法的。
這里,我將value的值變成帶空格的,然后去訪問后端的服務。
然后會發(fā)現(xiàn)控制臺報錯,Invalid URI query。這是因為get請求的value值不能含有非法字符.
同理
像這樣的,后臺接收的是
如果是這樣的參數(shù)
后臺這樣
結(jié)果是:
這樣就可以添加多個parameter了。
同時添加header和parameter
結(jié)束了addRequestParameter的說明,我們可以來看看,假如某個path,既想addHeader,又想addParameter,而系統(tǒng)的這兩個方法,都是一個path只能搭配一個add的filter,即便寫了兩個也不生效,如
結(jié)果就只有header被打印了
那么就是想同時添加header和parameter該怎么辦呢。
貌似通過java代碼是無法實現(xiàn)了,好在可以通過yml配置來實現(xiàn)。
spring: cloud: gateway: routes: - id: header uri: http://localhost:8888/header filters: - AddRequestHeader=NewHeader, Bar - AddRequestParameter=NewParam, Param predicates: - Path=/header
在yml就可以在filters里,添加多個filter了,注意不要寫錯了filter的名字。
可以看到結(jié)果
發(fā)現(xiàn)header和param都傳過來了。
以上這篇解決spring cloud gateway 獲取body內(nèi)容并修改的問題就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
- 詳解Spring Cloud Gateway 限流操作
- spring cloud gateway使用 uri: lb://方式配置時,服務名的特殊要求
- 基于Nacos實現(xiàn)Spring Cloud Gateway實現(xiàn)動態(tài)路由的方法
- springcloud gateway如何實現(xiàn)路由和負載均衡
- springcloud gateway聚合swagger2的方法示例
- 詳解SpringCloud Gateway之過濾器GatewayFilter
- Spring Cloud Gateway全局異常處理的方法詳解
- spring cloud gateway整合sentinel實現(xiàn)網(wǎng)關限流
- Spring?Cloud?Gateway編碼實現(xiàn)任意地址跳轉(zhuǎn)
相關文章
MybatisPlus多數(shù)據(jù)源及事務解決思路
這篇文章主要介紹了MybatisPlus多數(shù)據(jù)源及事務解決思路,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01idea普通javaweb項目如何部署到tomcat(讀取web.xml文件)
這篇文章主要介紹了idea普通javaweb項目如何部署到tomcat(讀取web.xml文件),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08Java畢業(yè)設計實戰(zhàn)之校園一卡通系統(tǒng)的實現(xiàn)
這是一個使用了java+Springboot+Maven+mybatis+Vue+mysql+wd開發(fā)的校園一卡通系統(tǒng),是一個畢業(yè)設計的實戰(zhàn)練習,具有校園一卡通系統(tǒng)該有的所有功能,感興趣的朋友快來看看吧2022-01-01解決java.util.NoSuchElementException異常正確方法
java.util.NoSuchElementException是Java中的一種異常,表示在迭代器或枚舉中找不到元素,這篇文章主要給大家介紹了關于解決java.util.NoSuchElementException異常的相關資料,需要的朋友可以參考下2023-11-11必須詳細與全面的Java開發(fā)環(huán)境搭建圖文教程
本篇文章內(nèi)容包括:Linux理論與實操,MySQL實操,JDK實操,Tomcat實操和Tomcat實操,需要的朋友可以參考下2019-11-11使用Spring Cache和Redis實現(xiàn)查詢數(shù)據(jù)緩存
在現(xiàn)代應用程序中,查詢緩存的使用已經(jīng)變得越來越普遍,它不僅能夠顯著提高系統(tǒng)的性能,還能提升用戶體驗,在這篇文章中,我們將探討緩存的基本概念、重要性以及如何使用Spring Cache和Redis實現(xiàn)查詢數(shù)據(jù)緩存,需要的朋友可以參考下2024-07-07