Spring-Cloud-Gateway修改请求(json,form带文件请求)参数,返回值参数

新项目需要在getway统一做入参、出参加解密,记录日志。记录一下form,x-www-form-urlencoded , json 这几种修改数据的方式。
gateway做拦截器是实现GlobalFilter接口,修改json方式网上有很多文章,后来又想研究研究能不能实现修改form-data参数,以及文件请求,后者文章不多大部分是怎么读数据的教学。现在发一下我的实现方式。
使用的gateway版本是2.2.1.RELEASE cloud版本Greenwich.SR2


   @Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)   {
	    Mono<Void> mono = chain.filter(exchange);
		ServerHttpRequest request = exchange.getRequest();
		MediaType contentType = request.getHeaders().getContentType();

	    if (Objects.nonNull(contentType) && Objects.nonNull(exchange.getRequest().getMethod())
			&& exchange.getRequest().getMethod().equals(HttpMethod.POST)) {
		    if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
			    // json 请求体处理
			    mono = this.transferBody(exchange, chain);
		    }else if(MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)){
		    	// multipart/form-data处理
				mono = this.fileRequest(contentType,exchange,chain);
			}else if(MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)){
		    	// x-www-form-urlencoded 格式处理
				mono = this.xwFromBody(exchange,chain);
			}
	    }else{
			if(exchange.getRequest().getMethod().equals(HttpMethod.GET)){
				Map<String, String> queryParams = exchange.getRequest().getQueryParams().toSingleValueMap();
				log.info("queryParams:{}", queryParams);
			}
		}
	    return mono;
	}

修改json数据

/**
	 * 修改修改body参数
	 */
	private Mono<Void> transferBody(ServerWebExchange exchange, GatewayFilterChain chain) {
	ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
	Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(Mono::just);

	BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
	HttpHeaders headers = new HttpHeaders();
	headers.putAll(exchange.getRequest().getHeaders());
	headers.remove(HttpHeaders.CONTENT_LENGTH);
	MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headers);
	ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate();
	requestBuilder.headers(k -> k.remove("Content-length"));

	Mono mono = bodyInserter.insert(outputMessage, new BodyInserterContext())
			.then(Mono.defer(() -> {
				//解决body内数据过长读取不完整的问题
				Flux<DataBuffer> body = outputMessage.getBody();
				DataBufferHolder holder = new DataBufferHolder();
				try{
					body.subscribe(dataBuffer -> {
						int len = dataBuffer.readableByteCount();
						holder.length = len;
						byte[] bytes = new byte[len];
						dataBuffer.read(bytes);
						DataBufferUtils.release(dataBuffer);
						String oldBody = new String(bytes, StandardCharsets.UTF_8);
						JsonNode jsonNode = objectMapper.readTree(in);
						//到这可以读取数据,做校验之类的
					    //JsonNode token = oldDataJSON.get("token");
						jsonNode .set("test","修改数据");
						DataBuffer data = outputMessage.bufferFactory().allocateBuffer();
						data.write(jsonNode.toString().getBytes(StandardCharsets.UTF_8));
						holder.length = data.readableByteCount();
						holder.dataBuffer=data;
					});

				}catch (Exception e){
					if(e.getCause() instanceof ServiceException){
						ServiceException e1 = (ServiceException) e.getCause();
						return handleFailedRequest(exchange, JSONObject.toJSONString(CommonResponse.error(e1.getCode(), e1.getMessage())));
					}
					return handleFailedRequest(exchange, JSONObject.toJSONString(CommonResponse.error(SYSTEM_ERROR.getCode(), SYSTEM_ERROR.getMessage())));
				}


				ServerHttpRequestDecorator decorator = newDecorator(exchange,holder.length, Flux.just(holder.dataBuffer));
				return chain.filter(exchange.mutate().request(decorator).build());

			}));

		return mono;
	}

修改form表单参数,一般需要上传文件的请求用这个。数据读取都可以用json请求那个方法获取,但是带文件的请求用那个读了以后文件数据就变了,修改数据以后文件没法正常存储,所以按以下方式,直接读取字节然后拼接要改的数据

//修改form参数
	private Mono<Void> fileRequest(MediaType contentType,ServerWebExchange exchange, GatewayFilterChain chain){
		return DataBufferUtils.join(exchange.getRequest().getBody())
				.flatMap(dataBuffer -> {
					byte[] bytes = new byte[dataBuffer.readableByteCount()];
					dataBuffer.read(bytes);
					DataBufferUtils.release(dataBuffer);
					//  new String(bytes); 
					//添加一些自定义参数,或者校验
					String oldBody = addPara(contentType.toString(), new String(bytes));
					
					byte[] bytes1 = oldBody.getBytes();
					//这里截取数组是因为 form请求体数据末尾有--/r/n,这里需要截一段然后拼接自定义的数据。数据格式可以看文章末尾的链接
					byte[] bytes2 = byteMerger(Arrays.copyOf(bytes,bytes.length-4), bytes1);
					Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
						DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes2);
						DataBufferUtils.retain(buffer);
						return Mono.just(buffer);
					});
					ServerHttpRequestDecorator mutatedRequest = newDecorator(exchange,bytes2.length,cachedFlux);
					ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
					return ServerRequest.create(mutatedExchange, MESSAGE_READERS)
							.bodyToMono(byte[].class)
							.then(chain.filter(mutatedExchange));
				});
	}
	/**
	 * 修改form参数
	 * @param contentType 请求类型
	 * @param bodyString  请求body信息
	 */
	@SneakyThrows
	public static String addPara(String contentType, String bodyString) {
		StringBuffer stringBuffer = new StringBuffer();

		String boundary = contentType.substring(contentType.lastIndexOf("boundary=") + 9);//获取随机字符传信息
		String boundary_end = StrUtil.format("--{}--\r\n", boundary);
		Map<String, Object> formMap = Maps.newHashMap();
		/**
		 *
		 * 根据自己需求进行对bodyString信息修改,例如下面,根据boundary对传入的bodyString进行了分割
		 *  String[] split = bodyString.split(boundary);
		 *  然后将修改完后的信息封装到formMap中,需要注意的是,file文件需要以new FileResource(file, fileName)的形式当作value放到formMap中
		 */
		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);
				System.out.println("name:"+name+" value:"+value);
//				formMap.put(name,value);
			}
		}

		formMap.put("test","添加自定义参数");
		formMap.put("tcu",22222);
		Integer count =0;
		for (Map.Entry<String, Object> entry : formMap.entrySet()) {
			stringBuffer.append(appendPart(boundary, entry.getKey(), entry.getValue(),count));
			count++;
		}
		stringBuffer.append(boundary_end);//拼接结束信息
		log.info(stringBuffer.toString());

		return stringBuffer.toString();
	}

修改x-www-form-urlencoded参数

	/**
	 * 修改 x-www-form-urlencoded参数
	 * @param exchange
	 * @param chain
	 * @return
	 */
	private Mono<Void> xwFromBody(ServerWebExchange exchange, GatewayFilterChain chain){
		ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
		Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(Mono::just);
		BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
		HttpHeaders headers = new HttpHeaders();
		headers.putAll(exchange.getRequest().getHeaders());
		headers.remove(HttpHeaders.CONTENT_LENGTH);
		MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headers);
		ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate();
		requestBuilder.headers(k -> k.remove("Content-length"));

		return bodyInserter.insert(outputMessage, new BodyInserterContext())
				.then(Mono.defer(() -> {
					Flux<DataBuffer> body = outputMessage.getBody();
					DataBufferHolder holder = new DataBufferHolder();
					changeParamByXwForm(body,holder,outputMessage);
					ServerHttpRequestDecorator decorator = newDecorator(exchange,holder.length, Flux.just(holder.dataBuffer));
					return chain.filter(exchange.mutate().request(decorator).build());
				}));
	}
	//修改 x-www-form-urlencoded参数
	private void changeParamByXwForm(Flux<DataBuffer> body,DataBufferHolder holder,MyCachedBodyOutputMessage outputMessage){
		body.subscribe(dataBuffer -> {
			int len = dataBuffer.readableByteCount();
			holder.length = len;
			byte[] bytes = new byte[len];
			dataBuffer.read(bytes);
			DataBufferUtils.release(dataBuffer);
			String oldBody = new String(bytes, StandardCharsets.UTF_8);
			//直接拼接要改的数据就行
			oldBody+="&tcu=123888";
			DataBuffer data = outputMessage.bufferFactory().allocateBuffer();
			data.write(oldBody.getBytes(StandardCharsets.UTF_8));
			holder.length = data.readableByteCount();
			holder.dataBuffer=data;
		});
	}

修改请求参数完整代码 ReadAndChangeResFilter .java

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.resource.MultiResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Maps;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;

import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.*;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.core.Ordered;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.byaero.utm.common.constant.CommonErrorCodeEnum.SYSTEM_ERROR;
import static com.byaero.utm.common.constant.CommonErrorCodeEnum.TOKEN_NOT_EXISTENT_ERROR;

@Slf4j
@Component
public class ReadAndChangeResFilter implements GlobalFilter, Ordered {

	private static final String CONTENT_DISPOSITION_TEMPLATE = "Content-Disposition: form-data; name=\"{}\"\r\n\r\n";
	private static final String CONTENT_DISPOSITION_FILE_TEMPLATE = "Content-Disposition: form-data; name=\"{}\"; filename=\"{}\"\r\n";

	private static final String CONTENT_TYPE_MULTIPART_PREFIX = ContentType.MULTIPART.getValue() + "; boundary=";
	private static final String CONTENT_TYPE_FILE_TEMPLATE = "Content-Type: {}\r\n\r\n";

	private final static String TOKEN_USERINFO_CACHE_FLAG = "TI-%s";
	private static final List<HttpMessageReader<?>> MESSAGE_READERS = HandlerStrategies.withDefaults().messageReaders();

	@Autowired
	private RedisUtil redisUtil;
	@Autowired
	private UserConvert userConvert;
	@Autowired
	private ObjectMapper objectMapper;
	@Autowired
	private UserManager userManager;


    @Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)   {
	    Mono<Void> mono = chain.filter(exchange);
		ServerHttpRequest request = exchange.getRequest();
		MediaType contentType = request.getHeaders().getContentType();

		final GatewayContext gatewayContext = new GatewayContext();

//		ServerWebExchangeUtils.putUriTemplateVariables();
	    if (Objects.nonNull(contentType) && Objects.nonNull(exchange.getRequest().getMethod())
			&& exchange.getRequest().getMethod().equals(HttpMethod.POST)) {
		    if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
			    // json 请求体处理
			    mono = this.transferBody(exchange, chain);
		    }else if(MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)){
		    	// multipart/form-data处理
				mono = this.fileRequest(contentType,exchange,chain);
			}else if(MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)){
		    	// x-www-form-urlencoded 格式处理
				mono = this.xwFromBody(exchange,chain);
			}
	    }else{
			if(exchange.getRequest().getMethod().equals(HttpMethod.GET)){
				Map<String, String> queryParams = exchange.getRequest().getQueryParams().toSingleValueMap();
				log.info("queryParams:{}", queryParams);
			}
		}
	    return mono;
	}
	//修改form参数
	private Mono<Void> fileRequest(MediaType contentType,ServerWebExchange exchange, GatewayFilterChain chain){
		return DataBufferUtils.join(exchange.getRequest().getBody())
				.flatMap(dataBuffer -> {
					byte[] bytes = new byte[dataBuffer.readableByteCount()];
					dataBuffer.read(bytes);
					DataBufferUtils.release(dataBuffer);
					String oldBody = addPara(contentType.toString(), new String(bytes));

					byte[] bytes1 = oldBody.getBytes();
					byte[] bytes2 = byteMerger(Arrays.copyOf(bytes,bytes.length-4), bytes1);
					Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
						DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes2);
						DataBufferUtils.retain(buffer);
						return Mono.just(buffer);
					});
					ServerHttpRequestDecorator mutatedRequest = newDecorator(exchange,bytes2.length,cachedFlux);
					ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
					return ServerRequest.create(mutatedExchange, MESSAGE_READERS)
							.bodyToMono(byte[].class)
							.then(chain.filter(mutatedExchange));
				});
	}
	public static byte[] byteMerger(byte[] bt1, byte[] bt2){
		byte[] bt3 = new byte[bt1.length+bt2.length];
		System.arraycopy(bt1, 0, bt3, 0, bt1.length);
		System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
		return bt3;
	}

	//修改 x-www-form-urlencoded参数
	private void changeParamByXwForm(Flux<DataBuffer> body,DataBufferHolder holder,MyCachedBodyOutputMessage outputMessage){
		body.subscribe(dataBuffer -> {
			int len = dataBuffer.readableByteCount();
			holder.length = len;
			byte[] bytes = new byte[len];
			dataBuffer.read(bytes);
			DataBufferUtils.release(dataBuffer);
			String oldBody = new String(bytes, StandardCharsets.UTF_8);
			oldBody+="&test=修改数据";
			DataBuffer data = outputMessage.bufferFactory().allocateBuffer();
			data.write(oldBody.getBytes(StandardCharsets.UTF_8));
			holder.length = data.readableByteCount();
			holder.dataBuffer=data;
		});
	}

	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE;
	}

	/**
	 * 修改 x-www-form-urlencoded参数
	 * @param exchange
	 * @param chain
	 * @return
	 */
	private Mono<Void> xwFromBody(ServerWebExchange exchange, GatewayFilterChain chain){
		ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
		Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(Mono::just);
		BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
		HttpHeaders headers = new HttpHeaders();
		headers.putAll(exchange.getRequest().getHeaders());
		headers.remove(HttpHeaders.CONTENT_LENGTH);
		MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headers);
		ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate();
		requestBuilder.headers(k -> k.remove("Content-length"));

		return bodyInserter.insert(outputMessage, new BodyInserterContext())
				.then(Mono.defer(() -> {
					Flux<DataBuffer> body = outputMessage.getBody();
					DataBufferHolder holder = new DataBufferHolder();
					changeParamByXwForm(body,holder,outputMessage);
					ServerHttpRequestDecorator decorator = newDecorator(exchange,holder.length, Flux.just(holder.dataBuffer));
					return chain.filter(exchange.mutate().request(decorator).build());
				}));
	}

	/**
	 * 修改修改body参数
	 */
	private Mono<Void> transferBody(ServerWebExchange exchange, GatewayFilterChain chain) {
	ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
	Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(Mono::just);

	BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
	HttpHeaders headers = new HttpHeaders();
	headers.putAll(exchange.getRequest().getHeaders());
	headers.remove(HttpHeaders.CONTENT_LENGTH);
	MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headers);
	ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate();
	requestBuilder.headers(k -> k.remove("Content-length"));

	Mono mono = bodyInserter.insert(outputMessage, new BodyInserterContext())
			.then(Mono.defer(() -> {
				//解决body内数据过长读取不完整的问题
				Flux<DataBuffer> body = outputMessage.getBody();
				DataBufferHolder holder = new DataBufferHolder();
				try{
					body.subscribe(dataBuffer -> {
						int len = dataBuffer.readableByteCount();
						holder.length = len;
						byte[] bytes = new byte[len];
						dataBuffer.read(bytes);
						DataBufferUtils.release(dataBuffer);
						String oldBody = new String(bytes, StandardCharsets.UTF_8);
						JsonNode jsonNode = readNode(oldBody);
					 	checkTokenAndSaveUser((ObjectNode)jsonNode);
						DataBuffer data = outputMessage.bufferFactory().allocateBuffer();
						data.write(jsonNode.toString().getBytes(StandardCharsets.UTF_8));
						holder.length = data.readableByteCount();
						holder.dataBuffer=data;
					});

				}catch (Exception e){
					if(e.getCause() instanceof ServiceException){
						ServiceException e1 = (ServiceException) e.getCause();
						return handleFailedRequest(exchange, JSONObject.toJSONString(CommonResponse.error(e1.getCode(), e1.getMessage())));
					}
					return handleFailedRequest(exchange, JSONObject.toJSONString(CommonResponse.error(SYSTEM_ERROR.getCode(), SYSTEM_ERROR.getMessage())));
				}


				ServerHttpRequestDecorator decorator = newDecorator(exchange,holder.length, Flux.just(holder.dataBuffer));
				return chain.filter(exchange.mutate().request(decorator).build());

			}));

		return mono;
	}

	private void checkTokenAndSaveUser(ObjectNode oldDataJSON) throws ServiceException{
		JsonNode token = oldDataJSON.get("token");
		if(token==null){
			throw new ServiceException(TOKEN_NOT_EXISTENT_ERROR.getCode(), TOKEN_NOT_EXISTENT_ERROR.getMessage());
		}
		Object userIdCacheKey = redisUtil.get(token.asText());
		if(userIdCacheKey!=null){
			Object cacheInfo = redisUtil.get(userIdCacheKey+"");
			UserCacheBO cacheDataObj = null;
			if(cacheInfo!=null){
				cacheDataObj = (UserCacheBO) cacheInfo;
			}else{
				cacheDataObj = userManager.flushUserCache(userIdCacheKey+"",token.asText());
			}
			UserBO userBO = userConvert.cacheToBo(cacheDataObj);
			try {
				String s = JSON.toJSONString(userBO);
				JsonNode node = objectMapper.readTree(s);
				oldDataJSON.set("userBO",node);
			} catch (IOException e) {
				e.printStackTrace();
				throw new IllegalStateException(e);
			}
		}else{
			throw new ServiceException(TOKEN_NOT_EXISTENT_ERROR.getCode(), TOKEN_NOT_EXISTENT_ERROR.getMessage());
		}
	}


	private Mono<Void> handleFailedRequest(ServerWebExchange exchange, String message) {
		byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
		DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
		exchange.getResponse().setStatusCode(HttpStatus.OK);
		return exchange.getResponse().writeWith(Flux.just(buffer));
	}

	private JsonNode readNode(String in) {
		try {
			return objectMapper.readTree(in);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	private class DataBufferHolder {
		DataBuffer dataBuffer;
		int length;
	}

	/**
	 * 修改form参数
	 * @param contentType 请求类型
	 * @param bodyString  请求body信息
	 */
	@SneakyThrows
	public static String addPara(String contentType, String bodyString) {
		StringBuffer stringBuffer = new StringBuffer();

		String boundary = contentType.substring(contentType.lastIndexOf("boundary=") + 9);//获取随机字符传信息
		String boundary_end = StrUtil.format("--{}--\r\n", boundary);
		Map<String, Object> formMap = Maps.newHashMap();
		/**
		 *
		 * 根据自己需求进行对bodyString信息修改,例如下面,根据boundary对传入的bodyString进行了分割
		 *  String[] split = bodyString.split(boundary);
		 *  然后将修改完后的信息封装到formMap中,需要注意的是,file文件需要以new FileResource(file, fileName)的形式当作value放到formMap中
		 */
		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);
				System.out.println("name:"+name+" value:"+value);
//				formMap.put(name,value);
			}
		}

		formMap.put("ft",11111);
		formMap.put("tcu",22222);
		Integer count =0;
		for (Map.Entry<String, Object> entry : formMap.entrySet()) {
			stringBuffer.append(appendPart(boundary, entry.getKey(), entry.getValue(),count));
			count++;
		}
		stringBuffer.append(boundary_end);//拼接结束信息
		log.info(stringBuffer.toString());

		return stringBuffer.toString();
	}

	public ServerHttpRequestDecorator newDecorator(ServerWebExchange exchange,long dataLength,Flux<DataBuffer> body){
		return new ServerHttpRequestDecorator(
				exchange.getRequest()) {
			@Override
			public HttpHeaders getHeaders() {
			//数据长度变了以后 需要修改header里的数据,不然接收数据时会异常
			//我看别人说删除会自动补充数据长度,但我这个版本不太行
//				long contentLength = headers.getContentLength();
				HttpHeaders httpHeaders = new HttpHeaders();
				httpHeaders.putAll(super.getHeaders());
//				if (contentLength > 0) {
					httpHeaders.setContentLength(dataLength);
//				} else {
//					httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
//				}
				return httpHeaders;
			}

			@Override
			public Flux<DataBuffer> getBody() {
				return body;
			}
		};
	}


	/**
	 * 添加Multipart表单的数据项
	 *
	 * @param boundary      随机串信息
	 * @param formFieldName 表单名
	 * @param value         值,可以是普通值、资源(如文件等)
	 */
	private static String appendPart(String boundary, String formFieldName, Object value,Integer count) throws IORuntimeException {
		StringBuffer stringBuffer = new StringBuffer();
		// 多资源
		if (value instanceof MultiResource) {
			for (Resource subResource : (MultiResource) value) {
				appendPart(boundary, formFieldName, subResource,count);
			}
			return stringBuffer.toString();
		}

		if(count!=0){
			stringBuffer.append("--").append(boundary).append(StrUtil.CRLF);
		}else{
			stringBuffer.append(StrUtil.CRLF);
//			stringBuffer.append(boundary).append(StrUtil.CRLF);
		}

		if (value instanceof Resource) {
			// 文件资源(二进制资源)
			final Resource resource = (Resource) value;
			final String fileName = resource.getName();
			stringBuffer.append(StrUtil.format(CONTENT_DISPOSITION_FILE_TEMPLATE, formFieldName, ObjectUtil.defaultIfNull(fileName, formFieldName)));
			// 根据name的扩展名指定互联网媒体类型,默认二进制流数据
			stringBuffer.append(StrUtil.format(CONTENT_TYPE_FILE_TEMPLATE, HttpUtil.getMimeType(fileName, "application/octet-stream")));
		} else {
			// 普通数据
			stringBuffer.append(StrUtil.format(CONTENT_DISPOSITION_TEMPLATE, formFieldName)).append(value);
		}
		stringBuffer.append(StrUtil.CRLF);
		return stringBuffer.toString();
	}
}


MyCachedBodyOutputMessage.java

import java.util.function.Supplier;
import org.reactivestreams.Publisher;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class MyCachedBodyOutputMessage implements ReactiveHttpOutputMessage {
    private final DataBufferFactory bufferFactory;
    DataBuffer dataBuffer;
    private final HttpHeaders httpHeaders;
    private Flux<DataBuffer> body = Flux.error(new IllegalStateException("The body is not set. Did handling complete with success?"));

    public MyCachedBodyOutputMessage(ServerWebExchange exchange, HttpHeaders httpHeaders) {
        this.bufferFactory = exchange.getResponse().bufferFactory();
        this.httpHeaders = httpHeaders;
    }
    @Override
    public void beforeCommit(Supplier<? extends Mono<Void>> action) {
    }
    @Override
    public boolean isCommitted() {
        return false;
    }
    @Override
    public HttpHeaders getHeaders() {
        return this.httpHeaders;
    }
    @Override
    public DataBufferFactory bufferFactory() {
        return this.bufferFactory;
    }

    public Flux<DataBuffer> getBody() {
        return this.body;
    }
    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        this.body = Flux.from(body);
        return Mono.empty();
    }

    @Override
    public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
        return this.writeWith(Flux.from(body).flatMap((p) -> {
            return p;
        }));
    }
    @Override
    public Mono<Void> setComplete() {
        return this.writeWith(Flux.empty());
    }
}

修改返回值代码


import com.google.common.base.Joiner;
import com.google.common.base.Throwables;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPOutputStream;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR;


@Component
public class ResponseFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(ResponseFilter.class);
    private static Joiner joiner = Joiner.on("");
//    @Autowired
//    private IgnoreWhiteProperties ignoreWhite;
    /**
     * 约定的压缩格式
     */
    private final static String GZIP = "gzip";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        String acceptEncoding = exchange.getRequest().getHeaders().getFirst("Accept-Encoding");

        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();

        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) {
                    // 获取ContentType,判断是否返回JSON格式数据
                    String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
                    if (StringUtils.isNotBlank(originalResponseContentType) && originalResponseContentType.contains("application/json")) {
                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                        //(返回数据内如果字符串过大,默认会切割)解决返回体分段传输
                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                            List<String> list = new ArrayList<>();
                            dataBuffers.forEach(dataBuffer -> {
                                try {
                                    byte[] content = new byte[dataBuffer.readableByteCount()];
                                    dataBuffer.read(content);
                                    DataBufferUtils.release(dataBuffer);
                                    list.add(new String(content, StandardCharsets.UTF_8));
                                } catch (Exception e) {
                                    log.info("加载Response字节流异常,失败原因:{}", Throwables.getStackTraceAsString(e));
                                }
                            });
                            String responseData = joiner.join(list);
                            System.out.println("responseData:"+responseData);
                            String responseDataEncode = null;
//                            String responseDataEncode = AESUtil.aesEncodeByKey(responseData, "MP5v0^zee5Qlgq5V");
                            if(responseDataEncode==null){
                                return bufferFactory.wrap(responseData.getBytes(StandardCharsets.UTF_8));
                            }
                            responseDataEncode =  responseDataEncode.replaceAll("\r\n", "").replaceAll("\n","");
                            byte[] uppedContent = new String(responseDataEncode.getBytes(), StandardCharsets.UTF_8).getBytes();
                            originalResponse.getHeaders().setContentLength(uppedContent.length);

                            if(!StringUtils.isAnyBlank(acceptEncoding)){
                                assert acceptEncoding != null;
                                //是否支持压缩
                                if(acceptEncoding.contains(GZIP)){
                                    //支持压缩
                                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
                                    //压缩输出流中的数据
                                    GZIPOutputStream gout;
                                    try {
                                        gout = new GZIPOutputStream(bout);
                                        gout.write(uppedContent);
                                        gout.close();
                                        uppedContent = bout.toByteArray();
                                        originalResponse.getHeaders().setContentLength(uppedContent.length);
                                        originalResponse.getHeaders().set("content-encoding", GZIP);
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                }else{
                                    originalResponse.getHeaders().setContentLength(uppedContent.length);
                                }
                            }
                            return bufferFactory.wrap(uppedContent);
                        }));
                    }
                }
                return super.writeWith(body);
            }

        };

        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

}

参考文章:https://blog.csdn.net/Hardworking666/article/details/123833192

你可能感兴趣的:(spring,gateway,json,spring,cloud)