解决springcloudgateway获取body内容并修改的问题之前写过⼀篇⽂章,如何获取body的内容。
确实能够获取所有body的内容了,不过今天终端同学调试接⼝的时候和我说,遇到了400的问题,报错是这样的HTTP method names must be tokens,搜了⼀下,都是说https引起的。可我的项⽬还没⽤https,排除了。
想到是不是因为修改了body内容导致的问题,试着不修改body的内容,直接传给微服务,果然没有报错了。
问题到,那就好办了,肯定是我新构建的REQUEST对象缺胳膊少腿了,搜索⼀通之后发现⼀篇⼤⽜写的⽂章:
这⾥要再次表扬⼀下古哥,同样是中⽂⽂章,度娘却搜不到
不过⽂章中的spring cloud版本是
Spring Cloud: Greenwich.RC2
我本地是最新的Release版本RS3,并不能完全照搬过来,不过算是给了很⼤的启发(如何获取body以及重构)
下⾯给出我的代码
⽹关中对body内容进⾏解密然后验签
/**
* @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) {
if (Request().getMethod().equals(HttpMethod.POST)) {
//重新构造request,参考ModifyRequestBodyGatewayFilterFactory
ServerRequest serverRequest = ate(exchange, HandlerStrategies.withDefaults().messageReaders());
MediaType mediaType = Request().getHeaders().getContentType();
//重点
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
//因为约定了终端传参的格式,所以只考虑json的情况,如果是表单传参,请⾃⾏发挥
if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON_UTF8.isCompatibleWith(mediaType)) {
JSONObject jsonObject = JO(body);
String paramStr = String("param");
String newBody;
try{
newBody = verifySignature(paramStr);
}catch (Exception e){
return Message());
}
return Mono.just(newBody);
}
pty();
});
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
HttpHeaders headers = new HttpHeaders();
headers.Request().getHeaders());
//猜测这个就是之前报400错误的元凶,之前修改了body但是没有重新写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 = Request().getQueryParams();
if (!CollectionUtils.isEmpty(map)) {
String paramStr = First("param");
try{
verifySignature(paramStr);
}catch (Exception e){
return Message());
}
}
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 = Attribute("startTime");
if (startTime != null){
long executeTime = (System.currentTimeMillis() - startTime);
log.info("耗时:{}ms" , executeTime);
log.info("状态码:{}" , Response().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 = JO(dParamStr);
if (!String("signature").equals(signature)) {
throw new Exception("签名不匹配!");
}
JSONString();
}
private Mono processError(String message) {
/*Response().setStatusCode(HttpStatus.UNAUTHORIZED);
Response().setComplete();*/
<(message);
(new Exception(message));
}
ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, MyCachedBodyOutputMessage outputMessage) {
return new Request()) {
public HttpHeaders getHeaders() {
long contentLength = ContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.Headers());
if (contentLength > 0L) {
httpHeaders.setContentLength(contentLength);
} else {
httpHeaders.set("Transfer-Encoding", "chunked");
}
return httpHeaders;
}
public Flux<DataBuffer> getBody() {
Body();
}
};
}
}
代码到这⾥就结束了,希望看到的朋友可以少⾛点弯路,少踩点坑。
补充知识:springcloud gateway之addRequestParameter详细使⽤及踩坑注意
SpringCloud的⽹关gateway提供了多个内置Filter,其中addRequestHeader是添加header的,这个⽆坑,⽐较简单。还有⼀个添加参数的,addRequestParameter,这个就有点问题了。具体往下看。
版本如下,请注意Springboot版本,这是本篇Post请求异常的关键。
spring怎么读取配置
1 对应的uri只能是get请求
这个是8888端⼝的服务
如果发起Get请求到⽹关,那么可以正常请求,⼀切OK。此时,调⽤发起⽅和最终的服务提供⽅都是Get请求,没有问题。
如果发起的请求是Get,但是服务提供⽅是如下的Post。
注意,这⾥我⽤了PostMapping,然后分别启动两个⼯程,再访问localhost:8080/addParam,⽽后会报错,这个也可以理解。
但是,如果调⽤发起⽅和服务提供⽅都是Post请求,理论上应该也是OK的。
但是事实上不是的
⽹关程序会报错如下:
这个就很尴尬了,作为⼀个⽹关,居然在代理⾮Get请求时出现异常,必然是不能容忍的。
经过⼀番探索,发现这是Springboot不同版本的原因导致,在Springboot2.0.5之前,不存在该问题,之后就有这种问题了。需要加以注意,解决⽅案会在下⼀篇写。
2 添加的参数value值必须合法(不能含有空格)
上⾯已经知道了,addRequestParameter对应的后端请求是Get型,那么明显添加的parameter只能是Get请求⽀持的,能在浏览器地址栏直接敲上去合法的。
这⾥,我将value的值变成带空格的,然后去访问后端的服务。
然后会发现控制台报错,Invalid URI query。这是因为get请求的value值不能含有⾮法字符.
同理
像这样的,后台接收的是
如果是这样的参数
后台这样
结果是:
这样就可以添加多个parameter了。
同时添加header和parameter
结束了addRequestParameter的说明,我们可以来看看,假如某个path,既想addHeader,⼜想addParameter,⽽系统的这两个⽅法,都是⼀个path只能搭配⼀个add的filter,即便写了两个也不⽣效,如

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。