SCG(Spring Cloud Gateway)就我个人理解,是想让开发者把它作为一个较为简单的网关框架,只需简单在yml文件中写几个配置项就可以运行。所以它不大推荐在网关这一层获取body数据或者做一下复杂的业务处理。故而在实际编写代码中,获取queryParam很容易,但body数据就比较麻烦了,如果要修改就更麻烦。在本篇文章主要讨论如何获取请求方式中的参数。
SCG获取参数一般有两种方式:

  1. 通过Filter过滤器
  2. 通过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;  
    }
}