Spring Cloud Gateway-过滤器中不满意的路径处理(异常处理与响应操作)和响应主体



用SCG Filters和Response实现不愉快路径管理的最佳(也就是最有效的方法(是什么?异常处理还是响应重写?

场景

我们正在实现一个API网关,该网关具有一些复杂的GatewayFilter(在不同类型的Throttling和其他一些行为之间(,它们有一个共同的不幸路径,在该路径中它们终止交换链处理,并应立即返回一个4xx响应,该响应具有特定的公共主体格式(基于Accept头的json/xml,我们有遗留兼容性要求(。

初始解决方案

我最初的解决方案是使用过滤器,然后尝试用其中一个选项编写响应:

response.setStatusCode(..);
//set headers
return response.writeAndFlushWith(...);

response.setStatusCode(..);
//set headers
return response.writeWith(...);

甚至

response.setStatusCode(..);
exchange.mutate().response(...);
return response.setComplete();

在每个特定的过滤器中。但是,主体响应的处理似乎是一个相当冗长的过程,加上过滤器提供的错误格式不应该是特定过滤器的问题。我们希望将关注点分离,因此过滤器应该定义标题、状态和错误消息,但其他人应该将其转换为正确的响应格式。

因此,经过长时间(血腥的:(讨论,我们提出了两种可能的选择。让我们假设我们有4个滤波器,我们的满意路径是(在一些近似下,不考虑全局滤波器顺序的重叠(:

(initial global filters) -> 1 -> 2 -> 3 -> 4-> 5 -> (other global filters) -> Routing -> (other global filters) -> 5 -> 4-> 3 -> 2 -> 1 -> 0 -> (initial global filters) -> Response

选项1-错误处理

实现一个类java MyErrorHandler extends DefaultErrorWebExceptionHandlerjava MyErrorAttributes extends DefaultErrorAttributes,这就起到了神奇的作用。

这意味着在特定的过滤器中抛出一个扩展ResponseStatusException或用@ResponseStatus注释的异常,并将其冒泡到ExceptionHandler如果我没有错的话,这也有中断过滤器中所有交换处理的优点。例如:给定3个过滤器,过滤器3抛出一个异常... 1 -> 2 -> 3 -> ExH -> (other global filters) -> Response

选项2-回复写作

在最上面的位置添加一个过滤器(过滤器0(,可能是一个扩展ModifyResponseGatewayFilter,它通过查看bean的特定交换属性(例如:gateway.error(来管理在响应中写入的错误并写入响应,并让链中的过滤器设置该特定bean并用它生成响应。

这将具有以下流程:... -> 0 -> 1 -> 2 -> 3 -> 2 -> 1 -> 0 -> (other global filters) -> Response

问题

选项

  1. 选项1似乎是一个短路选项,它确实阻碍了处理并建立在现有功能(ExceptionHandler(的基础上,但我被告知,异常对效率来说是一个可怕的负担,因为它们被迫建立所有的堆栈竞争
  2. 选项2似乎更有效,但随后响应在所有";张贴";过滤器,我们必须确保没有其他过滤器试图通过检查特定的交换属性来更新响应(例如:gateway.error(

SCG提供的RedisrateLimit使用选项2,但不进行正文重写,并使用response.setComplete(),但这不允许以后进行任何正文重写。

这是首选方法吗?

最后,为了减少不必要的堆栈争用创建,我们采取了以下措施:

  1. 我们返回了Mono.error(new GatewayRoutingException(..((
  2. 我们使GatewayRoutingException扩展了ResponseStatusException
  3. 我们重写了fillInStackTrace,因此不调用本机fillInStockTrace(int(

    公共同步可抛出的fillInStackTrace(({返回此;}

  4. 我们将要传递的HttpHeader与HttpStatus 一起存储

  5. 我们重写ErrorHandler来管理该异常,并将GatewayError bean作为一个主体返回,其中包含我们的错误数据结构、http状态和GatewayRoutingException的头

您可以处理错误,其中一个类实现ErrorWebExceptionHandler

@Component
@Primary
@Slf4j
public class ErrorResponseFilter implements ErrorWebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
...
return null;
} 
}

并将其添加到另一类中

@Configuration
public class ExceptionConfig {
@Primary
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public ErrorWebExceptionHandler errorWebExceptionHandler(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
ErrorResponseFilter jsonExceptionHandler = new ErrorResponseFilter();
jsonExceptionHandler.setViewResolvers(viewResolversProvider.getIfAvailable(Collections::emptyList));
jsonExceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
jsonExceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
return jsonExceptionHandler;
}
}

最新更新