只对某些内容类型应用modifyResponseBody

问题描述 投票:0回答:2

我正在使用

GatewayFilterSpec.modifyResponseBody
(标记为“BETA”功能)重写 JSON 有效负载。只要响应有效负载实际上是内容类型
application/json
,这种方法就可以正常工作。就我而言,不幸的是,这并不总是得到保证,我希望它仅在响应具有
modifyResponseBody
标头时应用
Content-Type: application/json
,否则跳过过滤器。 Spring Cloud Gateway 可以做到这一点吗?如何做到这一点?谢谢。

现在我明白了:

org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'text/html' not supported
    at org.springframework.web.reactive.function.BodyInserters.lambda$null$11(BodyInserters.java:329)
    at java.util.Optional.orElseGet(Optional.java:267)
    at org.springframework.web.reactive.function.BodyInserters.lambda$bodyInserterFor$12(BodyInserters.java:325)
spring-cloud spring-cloud-gateway
2个回答
2
投票

这是一个“解决方案”,它有各种问题

package my_package;

import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.codec.ServerCodecConfigurer;
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.Mono;

import static org.springframework.http.MediaType.APPLICATION_JSON;

@Component
@Primary
public class JsonOnlyModifyResponseBodyGatewayFilterFactory extends ModifyResponseBodyGatewayFilterFactory {
    public JsonOnlyModifyResponseBodyGatewayFilterFactory(ServerCodecConfigurer codecConfigurer) {
        super(codecConfigurer);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return new MyModifyResponseGatewayFilter(config);
    }

    public class MyModifyResponseGatewayFilter extends ModifyResponseGatewayFilter {
        MyModifyResponseGatewayFilter(Config config) {
            super(config);
        }

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpResponse serverHttpResponse = getServerHttpResponseFromSuper(exchange);
            ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    if (APPLICATION_JSON.isCompatibleWith(getDelegate().getHeaders().getContentType())) {
                        return serverHttpResponse.writeWith(body);
                    }
                    return super.writeWith(body);
                }
            };
            return chain.filter(exchange.mutate().response(responseDecorator).build());
        }

        private ServerHttpResponse getServerHttpResponseFromSuper(ServerWebExchange exchange) {
            ServerHttpResponse[] serverHttpResponse = new ServerHttpResponse[1];
            //noinspection UnassignedFluxMonoInstance
            super.filter(exchange, chain -> {
                serverHttpResponse[0] = chain.getResponse(); // capture the response when the super sets it
                return null;
            });
            return serverHttpResponse[0];
        }
    }
}

所选择的方法代替了仅更改现有

ModifyResponseBodyGatewayFilterFactory
的副本。这允许 Spring Boot Gateway 的版本升级带来
ModifyResponseBodyGatewayFilterFactory
的微小变化。但是由于
JsonOnlyModifyResponseBodyGatewayFilterFactory
非常依赖于
ModifyResponseBodyGatewayFilterFactory
的实现,这很容易被破坏。这个解决方案的另一个缺陷是我必须放置一个
@Primary
注释以避免
required a single bean, but 2 were found
异常,但它会覆盖默认值,这可能会影响
modifyResponseBody
的其他用途。调用
super.filter
而不使用其结果是丑陋的。等等。所以,虽然这个“有效”,但它并没有让我感到高兴。


0
投票

受这个answer的启发,我们可以通过组合

ModifyResponseBodyGatewayFilterFactory
来创建一个稍微不同的GatewayFilter。组合可能比扩展它更简洁、更短。

package my_package;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

@Component
public class ConditionallyModifyResponseBodyGatewayFilterFactory
    extends AbstractGatewayFilterFactory<ModifyResponseBodyGatewayFilterFactory.Config> {

  private final ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory;

  @Autowired
  public ConditionallyModifyResponseBodyGatewayFilterFactory(
      final ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory) {
    super(ModifyResponseBodyGatewayFilterFactory.Config.class);
    this.modifyResponseBodyGatewayFilterFactory = modifyResponseBodyGatewayFilterFactory;
  }

  @Override
  public GatewayFilter apply(final ModifyResponseBodyGatewayFilterFactory.Config config) {
    return (exchange, chain) -> {
      MediaType contentType = exchange.getResponse().getHeaders().getContentType();
      boolean shouldModify = contentType != null && contentType.isCompatibleWith(MediaType.APPLICATION_JSON);
      if (shouldModify) {
        return this.modifyResponseBodyGatewayFilterFactory.apply(config).filter(exchange, chain);
      } else {
        // Do nothing
        return chain.filter(exchange);
      }
    };
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.