Spring MVC Spring webflux -抛出异常时不执行ServerWebExchangeDecorator代码

ne5o7dgx  于 10个月前  发布在  Spring
关注(0)|答案(2)|浏览(92)

我有一个Webflux应用程序,其中有一个ServerWebExchangeDecorator来装饰请求和响应。我有一些重写来做一些日志记录,然后调用超级方法。这就是我的代码:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebExchangeDecorator;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

@Component
public class LoggingWebFilter implements WebFilter {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    return chain.filter(decorate(exchange));
  }

  private ServerWebExchange decorate(ServerWebExchange exchange) {

    final ServerHttpRequest decoratedRequest = new LoggingServerHttpRequestDecorator(exchange.getRequest());
    final ServerHttpResponse decoratedResponse = new LoggingServerHttpResponseDecorator(exchange.getResponse());

    return new ServerWebExchangeDecorator(exchange) {

      @Override
      public ServerHttpRequest getRequest() {
        return decoratedRequest;
      }

      @Override
      public ServerHttpResponse getResponse() {
        return decoratedResponse;
      }

    };
  }

}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import reactor.core.publisher.Flux;

public class LoggingServerHttpRequestDecorator extends ServerHttpRequestDecorator {

  private static final Logger logger = LoggerFactory.getLogger(LoggingServerHttpRequestDecorator.class);

  public LoggingServerHttpRequestDecorator(ServerHttpRequest delegate) {
    super(delegate);
  }

  @Override
  public Flux<DataBuffer> getBody() {
    logger.info("getBody method");
    return super.getBody();
  }

}
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import reactor.core.publisher.Mono;

public class LoggingServerHttpResponseDecorator extends ServerHttpResponseDecorator {

  private static final Logger logger = LoggerFactory.getLogger(LoggingServerHttpResponseDecorator.class);

  public LoggingServerHttpResponseDecorator(ServerHttpResponse delegate) {
    super(delegate);
  }

  @Override
  public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
    logger.info("writeWith method");//THIS LINE IS NOT EXECUTED WHEN AN EXCEPTION IS THROWN
    return super.writeWith(body);
  }

  @Override
  public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
    logger.info("writeAndFlushWith method");
    return super.writeAndFlushWith(body);
  }

}

当我用POST请求创建一个happy path时,这很好,但是当抛出一个异常时,Response Decorator被省略了,我的自定义代码也没有被执行。
这是用于复制问题的控制器代码:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/decorator-demo")
public class DecoratorDemoController {

  /** The Constant logger. */
  private static final Logger logger = LoggerFactory.getLogger(DecoratorDemoController.class);

  @PostMapping(produces = MediaType.APPLICATION_STREAM_JSON_VALUE, consumes = MediaType.APPLICATION_STREAM_JSON_VALUE)
  public Mono<ResponseEntity<String>> postData(@RequestBody String id) {
    logger.info("attempting to post the data");
    if(id.length() == 1){
      Mono<String> created = Mono.just(id);
      return created.flatMap(vo -> Mono.just(ResponseEntity.status(HttpStatus.CREATED).body(vo)));
    }
    throw new IllegalArgumentException("String length must be 1");
  }

}


当我发布一个字符时,我有我期望的日志:

LoggingServerHttpRequestDecorator  : getBody method
DecoratorDemoController            : attempting to post the data
LoggingServerHttpResponseDecorator : writeWith method


但是当我发布多个字符时,这是我的日志:

LoggingServerHttpRequestDecorator  : getBody method
DecoratorDemoController            : attempting to post the data
AbstractErrorWebExceptionHandler : [0b933716]  500 Server Error for HTTP POST "/decorator-demo"


我是做错了什么,还是错过了什么?

2exbekwf

2exbekwf1#

试试这个

import java.io.ByteArrayOutputStream;
import java.io.IOException;
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.cloud.gateway.filter.NettyWriteResponseFilter;
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.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;

@Component
public class ResponseInterceptorFilter implements Ordered, GlobalFilter {

  private static final Logger log = LoggerFactory.getLogger(ResponseInterceptorFilter.class);

  public ResponseInterceptorFilter(SqsPublisher sqsPublisher) {
    this.sqsPublisher = sqsPublisher;
  }

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

    ServerHttpResponse response = exchange.getResponse();
    DataBufferFactory dataBufferFactory = response.bufferFactory();
    ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {
      @Override
      public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        if (!(body instanceof Flux)) {
          return super.writeWith(body);
        }

        Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body;
        return super.writeWith(flux.buffer().map(dataBuffers -> {
          ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
          dataBuffers.forEach(buffer -> {
            // three byte copies here
            byte[] array = new byte[buffer.readableByteCount()];
            buffer.read(array);
            try {
              outputStream.write(array);
            } catch (IOException e) {
              // TODO: need catch?
            }
            DataBufferUtils.release(buffer);
          });

          byte[] write = outputStream.toByteArray();
          String responseBody = new String(write);
          log.debug("Response ----> {}", responseBody);
          response.getHeaders().setContentLength(write.length);
          return dataBufferFactory.wrap(write);
        }));
      }
    };

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

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

字符串

htrmnn0y

htrmnn0y2#

对于那些正在与请求或响应装饰器不执行if的问题作斗争的人;

  • 任何Filter都会抛出错误。
  • 链中的任何类(router> handler)都会抛出一个错误,这个错误在任何.errorXXX()Map方法中都没有处理。

让我们看看下面的例子:

@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class CustomLoggingFilter implements WebFilter {

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

    ServerWebExchangeDecorator decorator = new ServerWebExchangeDecorator(exchange) {
        @Override
        public ServerHttpRequest getRequest() {
            return new CustomRequestDecorator(exchange.getRequest());
        }

        @Override
        public ServerHttpResponse getResponse() {
            return new CustomResponseDecorator(exchange.getResponse());
        }
    };

    return chain.filter(decorator)
            .doOnError(throwable -> {
                // do your logging here, this is not the final logging!
            })
            // or
            .onErrorResume(throwable -> {
                // do logging here
                log.error("");
                return Mono.error(throwable);
            });
    }
}

字符串

注意事项

1.如果链接中的任何Filter或任何其他类向为请求启动的Mono链发出错误信号,则不会执行装饰器(ServerHttpRequestDecoratorServerHttpResponseDecorator)。
1.如果任何Filter或类抛出错误,它将到达此Filter的errorHandler,一旦此Filter(日志记录)将错误提交到链,则它将被ErrorWebExceptionHandler捕获
1.此方法 * 仅适用于Happy Path*
1.请求装饰器的public Flux<DataBuffer> getBody()方法只有在传入的请求有body时才会被调用!这意味着,对于GET请求或任何其他没有body的请求,甚至不会调用RequestDecorator。所以你需要依赖CONTENT_LENGTH头文件并手动调用日志记录!(yikes)
1.您需要处理 * GlobalErrorHandler中的Request & Response日志记录,以防出现故障场景
1.在链中缓冲请求和响应的整个方法 * 必须高度考虑到,通过缓冲其内容,Reactive的主要目的是失败的!

使用的堆栈:

  • spring-boot-starter-webflux:3.x.x
  • Router、Handler和ErrorWebExceptionHandler方法,与@Controller@RestControllerAdvice相反

请纠正我,如果其他人已经找到了一个推荐/优化的解决方案。

相关问题