HTTP Filter

使用 Java Chassis 对请求进行拦截推荐的方式是处理链。使用处理链不关注通信协议, 无论使用 HIGHWAY 还是使用 REST, 请求均会经过处理链进行处理。 当用户使用 REST 的时候,支持两种通道:REST over ServletREST over Vertx,这两种通道都支持通过 HttpClientFilterHttpServerFilter 两个接口对请求进行拦截。

比如某些场景中,业务使用 http 而不是 https,为了防止被伪造或篡改请求,需要提供consumer、producer之间对http码流的签名功能。 签名功能可以使用 HttpClientFilterHttpServerFilter 实现, 详细参考示例代码。 本章节通过 签名功能的实现介绍如何使用 HttpClientFilterHttpServerFilter

HttpClientFilter 和 HttpServerFilter 介绍

HttpClientFilter 和 HttpServerFilter 使用 Java 标准的 SPI 机制加载。 允许加载多个, 各实例之间的执行顺序由getOrder的返回值决定。 如果getOrder返回值相同,则相应的实例顺序随机决定。无论是request,还是response,读取body码流,都使用getBodyBytes(),返回值可能为 null(比如get调用的场景),如果不为null,对应的码流长度,通过getBodyBytesLength()获取。

注意事项: HttpClientFilter 的 beforeSendRequest 在接口调用的当前线程执行, afterReceiveResponse 在业务线程池中执行。 HttpServerFilter 的 afterReceiveRequest 在业务线程池中执行,beforeSendResponse 和 beforeSendResponseAsync 可能在业务线程池执行, 也可能在网络线程池执行, 务必保证不能够出现阻塞操作。

Java Chassis底层是异步框架,线程切换频繁。当业务扩展Filter时,若涉及通过ThreadLocal获取线程上下文时,可能会出现获取为空的情况。针对这种场景,建议使用InHeritableThreadLocal来代替ThreadLocal存储数据,或者使用扩展Handler的方式来代替Filter。

HttpClientFilter

系统内置2个HttpClientFilter,扩展功能时注意order值不要冲突:

  • org.apache.servicecomb.provider.springmvc.reference.RestTemplateCopyHeaderFilter, order值为Integer.MIN_VALUE

  • org.apache.servicecomb.transport.rest.client.http.DefaultHttpClientFilter, order值为Integer.MAX_VALUE

  • 原型

public interface HttpClientFilter {
  int getOrder();

  void beforeSendRequest(Invocation invocation, HttpServletRequestEx requestEx);

  // if finished, then return a none null response
  // if return a null response, then sdk will call next filter.afterReceive
  Response afterReceiveResponse(Invocation invocation, HttpServletResponseEx responseEx);
}
  • beforeSendRequest

用于在已经生成码流之后,发送请求之前,根据url、header、query、码流计算签名,并设置到header中去(requestEx.setHeader)。 从入参invocation中可以获取本次调用的各种元数据以及对象形式的参数(码流是根据这些参数生成的)。

  • afterReceiveResponse

用于在从网络收到应答后,根据header、码流计算签名,并与header中的签名对比。如果签名不对,直接构造一个Response 作为返回值,只要不是返回NULL,则框架会中断对其他HttpClientFilter的调用。

HttpServerFilter

  • 原型
public interface HttpServerFilter {
  int getOrder();

  default boolean needCacheRequest(OperationMeta operationMeta) {
    return false;
  }

  // if finished, then return a none null response
  // if return a null response, then sdk will call next filter.afterReceiveRequest
  Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx);

  // invocation maybe null
  void beforeSendResponse(Invocation invocation, HttpServletResponseEx responseEx);
}
  • needCacheRequest

与HttpClientFilter不同的是,增加了决定是否缓存请求的功能。这是因为ServiceComb不仅仅能使用standalone的方式运行,也 能运行于web容器(比如tomcat),在servlet的实现上,请求码流只能读取一次,并且不一定支持reset(比如tomcat),REST 框架需要执行反序列化,需 要读取body码流,签名逻辑也需要读取body码流,如果使用默认的处理,必然有一方功能无法实现。

所以运行于web容器场景时,所有HttpServerFilter,只要有一个返回需要缓存请求,则body码流会被复制保存起来,以支持重复读取。

入参是本次请求对应的元数据,业务可以针对该请求决定是否需要缓存请求。

  • afterReceiveRequest

在收到请求后,根据url、header、query、码流计算签名,并与header中的签名对比,如果签名不对,直接构造一个Response作为返回值, 只要不是返回NULL,则框架会中断对其他HttpClientFilter的调用。

  • beforeSendResponse 和 beforeSendResponseAsync

在发送应答之前,根据header、码流计算签名,并设置到header中去。因为可能invocation还没来得及构造,调用流程 已经出错,所以入参invocation可能是null。