Interceptor Chain(拦截器链)

Interceptor Chain在应用开发中是一个很常见的设计模式,Fusion提供了HttpInterceptor接口来支持拦截器链模式。

Scala是一门面向对象和函数式编程相融合的一门语言,在函数式编程中可通过函数的嵌套和各种高级处理来实现拦截器链的功能。

应用拦截器链

  trait HttpInterceptor {
    def filter(handler: HttpHandler): HttpHandler
  }

  def applyHttpInterceptorChain(httpHandler: HttpHandler, filters: Iterable[HttpInterceptor]): HttpHandler = {
    val duce: HttpHandler = filters.foldLeft(httpHandler) {
      (h, filter) =>
//      interceptor.filter(handler)
//      val result = interceptor.filter(h)
//      req => result(req).getOrElse(h(req))
        req =>
          filter.filter(h).apply(req)
    }
    duce
  }

applyHttpInterceptorChain函数中,对拦截器链filters应用foldLeft函数,即可非常优雅的应用每一个拦截器。

拦截器示例

拦截器示例 1:trace

class TraceHttpInterceptor(system: classic.ActorSystem) extends HttpInterceptor {
  import system.dispatcher

  override def interceptor(route: Route): Route = { ctx =>
    val req = ctx.request
    val traceHeader = RawHeader("trace-id", ObjectId.get().toHexString)
    val headers = traceHeader +: req.headers
    val request = req.copy(headers = headers)
    route(ctx.withRequest(request)).map {
      case RouteResult.Complete(response) => RouteResult.Complete(toTrace(response, traceHeader))
      case a @ RouteResult.Rejected(_)    => a
    }
  }

  private def toTrace(response: HttpResponse, traceHeader: RawHeader): HttpResponse = {
    val headers = traceHeader +: response.headers
    response.copy(headers = headers)
  }
}

TraceHttpInterceptor实现了给接收到的HTTP请求添加 trace id 的功能,这在微服务中监控调用链非常有用。

拦截器示例 2:不做任务处理

class NothingHttpInterceptor extends HttpInterceptor {
  override def interceptor(route: Route): Route = { ctx =>
    route(ctx)
  }
}

直接返回handler,并不对HTTP请求过程做任务处理,就像 NothingHttpIntercepter 那样。你可以加入判断逻辑,

拦截器示例 3:终止调用链

某个拦截器想终止调用链的执行,在转换函数中抛出一个异常即可:

  class TerminationHttpInterceptor extends HttpInterceptor {
    override def filter(handler: HttpHandler): HttpHandler = { req =>
      //handler(req).flatMap(resp => Future.failed(HttpResponseException(resp)))
//      handler(req).map(resp => throw HttpResponseException(resp))
      throw HttpResponseException(HttpResponse(StatusCodes.InternalServerError))
    }
  }