Spring 云网关 ServerHttpResponseDecorator writeWith 在接收响应时不调用

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

我有一个 Spring Cloud Gateway 应用程序,我想实现一个过滤器,它在将响应主体传递给客户端之前读取响应主体。以下是自定义过滤器的代码:

@Component
public class CreateResourceGatewayFilterFactory extends AbstractGatewayFilterFactory<CreateResourceGatewayFilterFactory.Config> implements Ordered {

    private static final Logger log = LoggerFactory.getLogger(CreateResourceGatewayFilterFactory.class);
    
    public static class Config {

        private ResourceType resourceType;
        private String requiredRoles;

        public ResourceType getResourceType() {
            return resourceType;
        }
        public void setResourceType(ResourceType resourceType) {
            this.resourceType = resourceType;
        }

        public String getRequiredRoles() {
            return requiredRoles;
        }
        public void setRequiredRoles(String requiredRoles) {
            this.requiredRoles = requiredRoles;
        }
    }

    public CreateResourceGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        List<String> order = new ArrayList<>(2);
        order.add("resourceType");
        order.add("requiredRoles");
        return order;
    }

    @Override
    public GatewayFilter apply(Config config) {

        return (exchange, chain) -> {
            log.debug("Received request at CreateResource filter.");
            return chain.filter(exchange.mutate().request(getDecoratedRequest(exchange.getRequest())).response(getDecoratedResponse(exchange.getResponse(), config.getResourceType())).build());
        };
    }

    private ServerHttpResponseDecorator getDecoratedResponse(ServerHttpResponse response, ResourceType resourceType) {
        return new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {
                log.debug("Inside response decorator");
                if (body instanceof Flux) {
                    log.debug("Received response from microservice.");
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.buffer()
                        .map(dataBuffers -> {
                            DefaultDataBuffer joinedBuffers = new DefaultDataBufferFactory().join(dataBuffers);
                            byte[] content = new byte[joinedBuffers.readableByteCount()];
                            joinedBuffers.read(content);
                            String responseBody = new String(content, StandardCharsets.UTF_8);
                            log.debug("Buffered response parsed to String {}", responseBody);
                            return responseBody;
                        })
                        .flatMap(responseBody -> doSomething(resourceType, responseBody))
                        .map(responseBody -> response.bufferFactory().wrap(responseBody.getBytes())))
                    .onErrorResume(err -> {
                        log.error("Error extracting resoruce from message {}",err.getMessage());
                        return Mono.empty();
                    });
                }
                return super.writeWith(body);
            }
        };
    }

    private Flux<String> doSomething(ResourceType resourceType, String payload) {
        // DO SOMETHING WITH PAYLOAD
        return Flux.just(payload);
    }

    private ServerHttpRequest getDecoratedRequest(ServerHttpRequest request) {
        return new ServerHttpRequestDecorator(request) {
            @Override
            public Flux<DataBuffer> getBody() {
                log.debug("requestId: {}, method: {} , url: {}", request.getId(), request.getMethodValue(), request.getURI());
                return request.getBody();
            }
        };
    }

    @Override
    public int getOrder() {
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }

我用这个post作为参考.

getDecoratedResponse
是创建
ServerHttpResponseDecorator
对象的方法。
CreateResourceGatewayFilterFactory
是过滤器类,
Config
是指定将传递给过滤器的参数的配置类。

application.yml
中的DSL是:

spring:
  cloud:
    gateway:
      default-filters:
        - TokenRelay
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
          predicates:
            - name: Path
              args:
                pattern: "'/services/'+serviceId.toLowerCase()+'/**'"
          filters:
            - name: RewritePath
              args:
                regexp: "'/services/' + serviceId.toLowerCase() + '/(?<remaining>.*)'"
                replacement: "'/${remaining}'"
      routes:
        - id: sample_route
          uri: http://localhost:8097
          predicates:
            - Method=POST
            - Path=/api/resources/**
          filters:
            - CreateResource=DEVICE,SUPER_ADMIN

请求在过滤器和请求装饰器中被捕获,因为我可以看到日志,但是没有来自响应装饰器的日志。

响应的 HTTP 状态代码为

201

我需要帮助来理解为什么没有触发响应装饰器。

注意:这个过滤器的目的是读取响应体。此进程可以在不同的线程中运行,因为它不会在将响应主体发送回客户端之前对其进行修改。如果有更好的方法而不是使用

ServerHttpResponseDecorator
,请告诉我。也许后过滤器逻辑 maybe.

java spring-boot filter api-gateway spring-cloud-gateway
© www.soinside.com 2019 - 2024. All rights reserved.