由于requestBody中的数据默认只能读取一次,如果在filter中读取后,那controller层就拿不到参数了,创建一个预处理Filter,把exchange和request都复制出来,把参数啥的放进去然后传递复制出来的exchange和request,后面执行的filter取的都是复制出来的request中的数据,controller层接收参数就不会有问题了。预处理Filter代码如下:


import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
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;

/**
 * 最先执行的filter
 * 后面的filter可以取到requestBody数据,也不会影响controller层数据的接收
 */
@Order(Integer.MIN_VALUE)
@Component
public class PreHandleFilter implements GlobalFilter {

	public static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";

	@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 Flux<DataBuffer> getBody() {
						return cachedFlux;
					}
				};
				exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, cachedFlux);

				return chain.filter(exchange.mutate().request(mutatedRequest).build());
			});
		}

	}
}

后面的Filter中取requestBody用下面的方法:

/**
 * 从requestBody中取出json或者form-data数据
 */
private String getRequestBodyFormRequest(ServerHttpRequest request) {
    Flux<DataBuffer> body = serverHttpRequest.getBody();
    StringBuilder sb = new StringBuilder();
    body.subscribe(buffer -> {
        byte[] bytes = new byte[buffer.readableByteCount()];
        buffer.read(bytes);
        String bodyString = new String(bytes, StandardCharsets.UTF_8);
        sb.append(bodyString);
    });
    return sb.toString();
}

取出来的json格式数据是正常的,直接解析用就可以。

但是form-data数据是浏览器发起请求的所有数据,示例如下:

----------------------------656104841052683944615106
Content-Disposition: form-data; name="name"

jason.zheng
----------------------------656104841052683944615106
Content-Disposition: form-data; name="addr"

jinan
----------------------------656104841052683944615106--

所以,需要解析上面的参数名和参数值。方法也写好了,直接拿去用吧:

/**
 * 解析从requestBody中获取的form-data数据,转换为json格式
 */
private String handleFormData(String str, ServerHttpRequest request) {
    String contentType = request.getHeaders().getContentType().toString();
	String sep = "--" + contentType.replace("multipart/form-data;boundary=", "");
	String[] strs = str.split("\r\n");
	boolean bankRow = false;// 空行
	boolean keyRow = true;// name=xxx行
	boolean append = false;// 内容是否拼接换行符
	Map<String, String> params = new LinkedHashMap<>();// 这里保证接收顺序,所以用linkedhashmap
	String s = null, key = null;
	StringBuffer sb = new StringBuffer();
	for (int i = 1, len = strs.length - 1; i < len; i++) {
		s = strs[i];
		if (keyRow) {
			key = s.replace("Content-Disposition: form-data; name=", "");
			key = key.substring(1, key.length() - 1);
			keyRow = false;
			bankRow = true;
			sb = new StringBuffer();
			append = false;
			continue;
		}
		if (sep.equals(s)) {
			keyRow = true;
			if (null != key) {
				params.put(key, sb.toString());
			}
			append = false;
			continue;
		}
		if (bankRow) {
			bankRow = false;
			append = false;
			continue;
		}
		if (append) {
			sb.append("\r\n");
		}
		sb.append(s);
		append = true;
	}
	if (null != key) {
		params.put(key, sb.toString());
	}
	return JSON.toJSONString(params);// 这里是alibaba的fastjson,需要引入依赖
}

上面方法得出的数据示例为:{"name":"jason.zheng","addr":"jinan"}

-----------------完!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐