Spring-Cloud-Gateway获取multipart/form-data时无法正常获取

1 篇文章 0 订阅
1 篇文章 0 订阅

       我们公司最近在升级springCloud2.X,由于Spring-Cloud-Gateway为官方推荐使用,所以最近在研究Spring-Cloud-Gateway,但是在实际开发过程中遇到了种种问题,其中耗时最长的为获取multipart/form-data里面的json字符串,在网上尝试了各种解决方案都无法成功获取,最终在GitHub的issue找到了一个大佬将此问题解决,废话不多说,直接上代码:

package com.XXX.center.gateway.filter;

import java.util.Collections;
import java.util.List;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.server.ServerWebExchange;

import com.XX.center.gateway.entity.GatewayContext;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 获取请求内容.
 * 此版本为是优化后的,在之前的版本中读取文件速度会变得很慢,导致响应时间过长。
 *
 * @author zyb
 */
@Component
public class GatewayContextFilter extends BaseFilter {

    /**
     * default HttpMessageReader.
     */
    private static final List<HttpMessageReader<?>> MESSAGE_READERS = HandlerStrategies.withDefaults().messageReaders();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        final ServerHttpRequest request = exchange.getRequest();
        final String path = request.getPath().pathWithinApplication().value();
        // GatewayContext中目前只有一个String的path和String的requestBody,如果需要别的参数在GatewayContext中追加即可,因为这只是一个实体类,所以就不放源码了,各位需要什么就往里面放什么即可
        final GatewayContext gatewayContext = new GatewayContext();
        gatewayContext.setPath(path);
        // GatewayContext.CACHE_GATEWAY_CONTEXT是一个常量定义,至于常量的值是什么,只要保证key值唯一即可,各位随便定义
        // 此处的作用为将我们需要的值放入Attributes中,以便在别的filter中使用,如果需要在别的地方使用,只需要exchange.getAttributes.get(GatewayContext.CACHE_GATEWAY_CONTEXT)即可
        exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT, gatewayContext);
        return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
            DataBufferUtils.retain(dataBuffer);
            final Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
            final ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                @Override
                public Flux<DataBuffer> getBody() {
                    return cachedFlux;
                }
            };
            final ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
            return cacheBody(mutatedExchange, chain, gatewayContext);
        });
    }

    @SuppressWarnings("unchecked")
    private Mono<Void> cacheBody(ServerWebExchange exchange, GatewayFilterChain chain, GatewayContext gatewayContext) {
        final HttpHeaders headers = exchange.getRequest().getHeaders();
        if (headers.getContentLength() == 0) {
            return chain.filter(exchange);
        }
        final ResolvableType resolvableType;
        if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(headers.getContentType())) {
            resolvableType = ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, Part.class);
        } else {
            resolvableType = ResolvableType.forClass(String.class);
        }
        return MESSAGE_READERS.stream().filter(reader -> reader.canRead(resolvableType, exchange.getRequest().getHeaders().getContentType())).findFirst()
            .orElseThrow(() -> new IllegalStateException("no suitable HttpMessageReader.")).readMono(resolvableType, exchange.getRequest(), Collections.emptyMap()).flatMap(resolvedBody -> {
                if (resolvedBody instanceof MultiValueMap) {
                    final Part partInfo = (Part) ((MultiValueMap) resolvedBody).getFirst("info");
                    if (partInfo instanceof FormFieldPart) {
                        gatewayContext.setRequestBody(((FormFieldPart) partInfo).value());
                    }
                } else {
                    gatewayContext.setRequestBody((String) resolvedBody);
                }
                return chain.filter(exchange);
            });
    }
}

参考:https://github.com/xurui8691413/sping-cloud-gateway-read-multipart-filter/blob/master/spring-cloud-gateway-sample

望能帮助到大家

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 33
    评论
application/x-www-form-urlencoded 是一种常见的编码格式,用于在 HTTP 请求中传输表单数据。在这种格式下,表单数据被编码为键值对的形式,并使用特定的字符进行分隔。以下是一个示例: ``` POST /submit-form HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded username=johndoe&password=secretpassword&email=johndoe@example.com ``` 在这个例子中,表单数据包括用户名、密码和电子邮件地址,它们被编码为键值对的形式,并使用 `&` 符号进行分隔。 multipart/form-data 是一种用于在 HTTP 请求中传输带有二进制数据的表单数据的编码格式。它允许在单个请求中传输文件和其他类型的数据。以下是一个示例: ``` POST /upload-file HTTP/1.1 Host: example.com Content-Type: multipart/form-data; boundary=---------------------------1234567890 -----------------------------1234567890 Content-Disposition: form-data; name="file"; filename="example.jpg" Content-Type: image/jpeg [文件内容] -----------------------------1234567890 Content-Disposition: form-data; name="description" 这是一个示例图片。 -----------------------------1234567890-- ``` 在这个例子中,表单数据包括一个文件和一个描述字段。每个字段都由一个分隔符(boundary)进行分隔,这个分隔符必须在请求头中指定。每个字段都有一个 Content-Disposition 头部,用于指定字段的名称和文件名(如果是文件字段)。文件字段还有一个 Content-Type 头部,用于指定文件的类型。文件内容在字段的分隔符之间被包含。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Listen丶Me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值