spring cloud gateway中如何讀取請求參數(shù)
更新時間:2021年07月15日 11:23:48 作者:影落離風
這篇文章主要介紹了spring cloud gateway中如何讀取請求參數(shù)的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
spring cloud gateway讀取請求參數(shù)
1. 我的版本:
- spring-cloud:Hoxton.RELEASE
- spring-boot:2.2.2.RELEASE
- spring-cloud-starter-gateway
2. 請求日志
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author MinWeikai
* @date 2019-12-20 18:09:39
*/
@Slf4j
@Component
public class LoggerFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String method = request.getMethodValue();
if (HttpMethod.POST.matches(method)) {
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
String bodyString = new String(bytes, StandardCharsets.UTF_8);
logtrace(exchange, bodyString);
exchange.getAttributes().put("POST_BODY", bodyString);
DataBufferUtils.release(dataBuffer);
Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
DataBuffer buffer = exchange.getResponse().bufferFactory()
.wrap(bytes);
return Mono.just(buffer);
});
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
return chain.filter(exchange.mutate().request(mutatedRequest)
.build());
});
} else if (HttpMethod.GET.matches(method)) {
Map m = request.getQueryParams();
logtrace(exchange, m.toString());
}
return chain.filter(exchange);
}
/**
* 日志信息
*
* @param exchange
* @param param 請求參數(shù)
*/
private void logtrace(ServerWebExchange exchange, String param) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String path = serverHttpRequest.getURI().getPath();
String method = serverHttpRequest.getMethodValue();
String headers = serverHttpRequest.getHeaders().entrySet()
.stream()
.map(entry -> " " + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]")
.collect(Collectors.joining("\n"));
log.info("\n" + "---------------- ---------------- ---------------->>\n" +
"HttpMethod : {}\n" +
"Uri : {}\n" +
"Param : {}\n" +
"Headers : \n" +
"{}\n" +
"\"<<---------------- ---------------- ----------------"
, method, path, param, headers);
}
}
3. 測試輸出,我這邊測試沒有問題,日志正常輸出

gateway網關轉發(fā)請求添加參數(shù)
在繼承AbstractGatewayFilterFactory的過濾器中
GET請求添加參數(shù)
// 參考api文檔中GatewapFilter中“添加請求參數(shù)攔截器”:AddRequestParameterGatewayFilterFactory.java
//記錄日志
//logger.info("全局參數(shù)處理: {} url:{} 參數(shù):{}",method.toString(),serverHttpRequest.getURI().getRawPath(),newRequestQueryParams.toString());
// 獲取原參數(shù)
URI uri = serverHttpRequest.getURI();
StringBuilder query = new StringBuilder();
String originalQuery = uri.getRawQuery();
if (org.springframework.util.StringUtils.hasText(originalQuery)) {
query.append(originalQuery);
if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
query.append('&');
}
}
// 添加查詢參數(shù)
query.append(ServiceConstants.COMMON_PARAMETER_ENTERPRISEID+"="+authenticationVO.getEnterpriseId()
+"&"+ServiceConstants.COMMON_PARAMETER_USERID+"="+authenticationVO.getUserId());
// 替換查詢參數(shù)
URI newUri = UriComponentsBuilder.fromUri(uri)
.replaceQuery(query.toString())
.build(true)
.toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(request).build());
POST請求添加參數(shù)
//從請求里獲取Post請求體
String bodyStr = resolveBodyFromRequest(serverHttpRequest);
String userId = "123";
// 這種處理方式,必須保證post請求時,原始post表單必須有數(shù)據(jù)過來,不然會報錯
if (StringUtils.isEmpty(bodyStr)) {
logger.error("請求異常:{} POST請求必須傳遞參數(shù)", serverHttpRequest.getURI().getRawPath());
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.BAD_REQUEST);
return response.setComplete();
}
//application/x-www-form-urlencoded和application/json才添加參數(shù)
//其他上傳文件之類的,不做參數(shù)處理,因為文件流添加參數(shù),文件原格式就會出問題了
/* if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType)) {
// 普通鍵值對,增加參數(shù)
bodyStr = String.format(bodyStr+"&%s=%s&%s=%s",ServiceConstants.COMMON_PARAMETER_ENTERPRISEID,authenticationVO.getEnterpriseId()
,ServiceConstants.COMMON_PARAMETER_USERID,authenticationVO.getUserId());
}*/
// 新增body參數(shù)
if (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType)) {
JSONObject jsonObject = new JSONObject(bodyStr);
jsonObject.put("userId", userId);
bodyStr = jsonObject.toString();
}
//記錄日志
logger.info("全局參數(shù)處理: {} url:{} 參數(shù):{}", method.toString(), serverHttpRequest.getURI().getRawPath(), bodyStr);
//下面的將請求體再次封裝寫回到request里,傳到下一級,否則,由于請求體已被消費,后續(xù)的服務將取不到值
URI uri = serverHttpRequest.getURI();
URI newUri = UriComponentsBuilder.fromUri(uri).build(true).toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
// 定義新的消息頭
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
// 添加消息頭
// headers.set(ServiceConstants.SHIRO_SESSION_PRINCIPALS,GsonUtils.toJson(authenticationVO));
// 由于修改了傳遞參數(shù),需要重新設置CONTENT_LENGTH,長度是字節(jié)長度,不是字符串長度
int length = bodyStr.getBytes().length;
headers.remove(HttpHeaders.CONTENT_LENGTH);
headers.setContentLength(length);
// 設置CONTENT_TYPE
if (StringUtils.isEmpty(contentType)) {
headers.set(HttpHeaders.CONTENT_TYPE, contentType);
}
// 由于post的body只能訂閱一次,由于上面代碼中已經訂閱過一次body。所以要再次封裝請求到request才行,不然會報錯請求已經訂閱過
request = new ServerHttpRequestDecorator(request) {
@Override
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength > 0) {
httpHeaders.setContentLength(contentLength);
} else {
// TODO: this causes a 'HTTP/1.1 411 Length Required' on httpbin.org
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
}
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
};
//封裝request,傳給下一級
request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(bodyStr.length()));
return chain.filter(exchange.mutate().request(request).build());
/**
* 從Flux<DataBuffer>中獲取字符串的方法
* @return 請求體
*/
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
//獲取請求體
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
bodyRef.set(charBuffer.toString());
});
//獲取request body
return bodyRef.get();
}
/**
* 字符串轉DataBuffer
* @param value
* @return
*/
private DataBuffer stringBuffer(String value) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
關于HashMap 并發(fā)時會引起死循環(huán)的問題解析
JDK1.8之前采用頭插,即在鏈表結構上每次都把數(shù)據(jù)放在鏈表頭部。JDK1.8采用尾插方法,很多朋友在學習Java并發(fā)容器和框架時,看到為什么要使用ConcurrentHashMap時不知道究其原因,今天小編通過本文給大家介紹下HashMap 并發(fā)死循環(huán)問題,一起看看吧2021-05-05
spring boot+自定義 AOP 實現(xiàn)全局校驗的實例代碼
最近公司重構項目,重構為最熱的微服務框架 spring boot, 重構的時候遇到幾個可以統(tǒng)一處理的問題。這篇文章主要介紹了spring boot+自定義 AOP 實現(xiàn)全局校驗 ,需要的朋友可以參考下2019-04-04
springboot異步處理@NotBlank或@NotNull注釋校驗不生效問題
這篇文章主要介紹了springboot異步處理@NotBlank或@NotNull注釋校驗不生效問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
JAVA實現(xiàn)監(jiān)測tomcat是否宕機及控制重啟的方法
這篇文章主要介紹了JAVA實現(xiàn)監(jiān)測tomcat是否宕機及控制重啟的方法,可實現(xiàn)有效的檢測及控制tomcat服務器運行,具有一定參考借鑒價值,需要的朋友可以參考下2015-08-08
Java中wait與sleep的區(qū)別講解(wait有參及無參區(qū)別)
這篇文章主要介紹了Java中wait與sleep的講解(wait有參及無參區(qū)別),通過代碼介紹了wait()?與wait(?long?timeout?)?區(qū)別,wait(0)?與?sleep(0)區(qū)別,需要的朋友可以參考下2022-04-04
Spring多定時任務@Scheduled執(zhí)行阻塞問題解決
這篇文章主要介紹了Spring多定時任務@Scheduled執(zhí)行阻塞問題解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05

