SCG(Spring Cloud Gateway)就我個人理解,是想讓開發者把它作為一個較為簡單的網關框架,只需簡單在yml文件中寫幾個配置項就可以運行。所以它不大推薦在網關這一層獲取body數據或者做一下複雜的業務處理。故而在實際編寫代碼中,獲取queryParam很容易,但body數據就比較麻煩 ...
SCG(Spring Cloud Gateway)就我個人理解,是想讓開發者把它作為一個較為簡單的網關框架,只需簡單在yml文件中寫幾個配置項就可以運行。所以它不大推薦在網關這一層獲取body數據或者做一下複雜的業務處理。故而在實際編寫代碼中,獲取queryParam很容易,但body數據就比較麻煩了,如果要修改就更麻煩。在本篇文章主要討論如何獲取請求方式中的參數。
SCG獲取參數一般有兩種方式:
- 通過Filter過濾器
- 通過Predicate斷言
原理都類似,通過事先緩存doby到attribute中,再讀取。至於這兩種區別主要在於緩存方式:filter直接加一層globalFilter即可,而Predicate則需要加一個配置項。具體請看代碼
配置Filter獲取
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
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;
@Component
public class ReadParamFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (exchange.getRequest().getHeaders().getContentType() == null) {
return chain.filter(exchange);
} else {
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
DataBufferUtils.retain(dataBuffer);
Flux<DataBuffer> cachedFlux = Flux
.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public @NonNull Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
exchange.getAttributes().put("cachedRequestBodyObject", cachedFlux);
});
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
缺點:在斷言階段不能獲取參數
配置Predicate獲取
predicate緩存request body需要加一個配置項
spring:
cloud:
gateway:
predicate:
read-body:
enabled: true
另外再加一個讀取body的Predicate類。
## 斷言類
```java
@Component
public class BodyPredicate implements Predicate {
@Override
public boolean test(Object o) {
return true;
}
}
緩存到之後,後邊獲取參數就比較方便了。
獲取參數
@Service
public class ParamFactory {
@Autowired
Map<String, ParamStrategy> getParamFactoryMap;
public ParamStrategy getParamStrategy(HttpMethod requestMethod){
return getParamFactoryMap.get(requestMethod.name());
}
}
獲取參數策略
public abstract class ParamStrategy {
public RequestParamBO analyzeRequestParam(ServerWebExchange exchange) {
return doAnalyzeRequestParam(exchange);
}
/**
* 解析請求數據
*
* @param exchange
* @return
*/
protected abstract RequestParamBO doAnalyzeRequestParam(ServerWebExchange exchange);
/**
* 獲取某個請求參數
*
* @param requestMessage
* @param paramKey
* @param position
* @return
*/
public abstract String getParamValue(RequestMessageBO requestMessage, String paramKey,
String position);
}
get
@Component("GET")
public class GetParamStrategy extends ParamStrategy {
/**
* 解析請求數據
*
* @param exchange@return
*/ @Override
protected RequestParamBO doAnalyzeRequestParam(ServerWebExchange exchange) {
Map<String, String> paramMap = new HashMap<>();
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
if (!queryParams.isEmpty()) {
paramMap =
queryParams.entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
entry -> {
List<String> list =
new ArrayList<>(entry.getValue());
// list包含空數據
list.removeIf(Objects::isNull);
if (list.size() != 0) {
return entry.getValue().get(0);
} else {
return "";
}
}));
}
return RequestParamBO.builder()
.queryParams(paramMap)
.build();
}
@Override
public String getParamValue(RequestMessageBO requestMessage, String paramKey, String position) {
Map<String,String> queryParam = requestMessage.getParam().getQueryParams();
if (CollectionUtils.isEmpty(queryParam)){
return null;
}
return queryParam.get(paramKey);
}
}
post
@Component("POST")
public class PostParamStrategy extends ParamStrategy {
private static final String XW_FORM_PARAM_REGEX = "&";
private static final String XW_KEY_VALUE_REGEX = "=";
/**
* 解析請求數據
*
* @param exchange
* @return
*/
@Override
protected RequestParamBO doAnalyzeRequestParam(ServerWebExchange exchange) {
Map<String, String> paramMap = new HashMap<>();
Map<String, Object> attributes = exchange.getAttributes();
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
RequestParamBO requestParams = new RequestParamBO();
if (CollectionUtil.isNotEmpty(queryParams)) {
paramMap =
queryParams.entrySet().stream()
.collect(
Collectors.toMap(
Map.Entry::getKey,
entry -> {
List<String> list =
new ArrayList<>(entry.getValue());
// list包含空數據
list.removeIf(Objects::isNull);
if (list.size() != 0) {
return entry.getValue().get(0);
} else {
return "";
}
}));
}
requestParams.setQueryParams(paramMap);
MediaType contentType = exchange.getRequest().getHeaders().getContentType();
String body = (String) attributes.get(CACHE_REQUEST_BODY_OBJECT);
if (MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
assert contentType != null;
requestParams.setFormParams(getFormParam(contentType.toString(), body));
} else if (APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
requestParams.setFormParams(getXwFormParam(body));
} else if (APPLICATION_JSON.isCompatibleWith(contentType)) {
//json body就直接作為String處理保存了,
requestParams.setJsonBody(body);
}
return requestParams;
}
@Override
public String getParamValue(RequestMessageBO requestMessage, String paramKey, String position) {
MediaType mediaType = requestMessage.getMediaType();
if (APPLICATION_JSON.isCompatibleWith(mediaType)) {
Object document = Configuration.defaultConfiguration()
.jsonProvider().parse(requestMessage.getParam().getJsonBody());
JSONArray paramValues = Objects.requireNonNull(JsonPath.read(document, position));
return String.valueOf(paramValues.get(0));
}else {
return requestMessage.getParam().getFormParams().get(paramKey);
}
}
//獲取 表單數據
@SneakyThrows
private Map<String, String> getFormParam(String contentType, String bodyString) {
String boundary = contentType.substring(contentType.lastIndexOf("boundary=") + 9);
Map<String, String> formMap = Maps.newHashMap();
String part =
"^\r\nContent-Disposition: form-data; name=\"([^/?]+)\"\r\n\r\n([^/?]+)\r\n--?$";
Pattern r = Pattern.compile(part);
String[] split = bodyString.split(boundary);
for (int x = 1; x < split.length - 1; x++) {
Matcher m = r.matcher(split[x]);
if (m.find()) {
String name = m.group(1);
String value = m.group(2);
formMap.put(name, value);
}
}
return formMap;
}
//獲取xw表單數據
private Map<String, String> getXwFormParam(String bodyStr) {
Map<String, String> paramMap = new HashMap<>();
try {
bodyStr = URLDecoder.decode(bodyStr, "utf-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
String[] params = bodyStr.split(XW_FORM_PARAM_REGEX);
for (String paramKeyValue : params) {
String[] keyValue = paramKeyValue.split(XW_KEY_VALUE_REGEX);
if (keyValue.length == 2) {
paramMap.put(keyValue[0], keyValue[1]);
}
}
return paramMap;
}
}