Route 是Akka-http routing DSL的核心部分,使用户能对照便利的从http-server的角度筛选http-request、进行server运算、构建答复的http-response。所谓筛选http-request主要目的是容许request进入下一内层Route,或者拒绝reject request。好比这个~标记:它连接了上下两个独立的Route。如果上面的Route拒绝了一个request,那么下面的Route就会接着测验考试这个request。一般来说:当一个筛选成果的Directive如get遇到一个不切合筛选条件的request时,,它会拒绝reject这个request进入下一层Route。这时用~标记链接的下一组Route会接着测验考试,直到链条最后一组Route。整个过程中的这些rejection事件会被记录下来最后由某个隐式或明式的RejectionHandler实例把这组rejection转化成HttpResponse返回用户。rejection也可以直接挪用requestContext.reject(...)孕育产生。Akka-http是通过在运行Route时用Route.seal(route)的方法来确保所有rejection在最终城市得处措置惩罚惩罚:
override def seal(system: ActorSystem, materializer: Materializer): Route = { implicit val s = system implicit val m = materializer RouteAdapter(scaladsl.server.Route.seal(delegate)) }
下面是Route.seal()函数界说:
/** * "Seals" a route by wrapping it with default exception handling and rejection conversion. * * A sealed route has these properties: * - The result of the route will always be a complete response, i.e. the result of the future is a * ``Success(RouteResult.Complete(response))``, never a failed future and never a rejected route. These * will be already be handled using the implicitly given [[RejectionHandler]] and [[ExceptionHandler]] (or * the default handlers if none are given or can be found implicitly). * - Consequently, no route alternatives will be tried that were combined with this route * using the ``~`` on routes or the [[Directive.|]] operator on directives. */ def seal(route: Route)(implicit routingSettings: RoutingSettings, parserSettings: ParserSettings = null, rejectionHandler: RejectionHandler = RejectionHandler.default, exceptionHandler: ExceptionHandler = null): Route = { import directives.ExecutionDirectives._ // optimized as this is the root handler for all akka-http applications (handleExceptions(ExceptionHandler.seal(exceptionHandler)) & handleRejections(rejectionHandler.seal)) .tapply(_ ⇒ route) // execute above directives eagerly, avoiding useless laziness of Directive.addByNameNullaryApply }
RejectionHandler.default是Akka-http供给的默认handler。我们也可以把自界说的隐式RejectionHandler实例放在可视域内就会自动被挪用了。下面是一个自界说RejectionHandler例子:
RejectionHandler.newBuilder() .handle { case MissingCookieRejection(cookieName) => complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!")) } .handle { case AuthorizationFailedRejection => complete((Forbidden, "You‘re out of your depth!")) } .handle { case ValidationRejection(msg, _) => complete((InternalServerError, "That wasn‘t valid! " + msg)) } .handleAll[MethodRejection] { methodRejections => val names = methodRejections.map(_.supported.name) complete((MethodNotAllowed, s"Can‘t do that! Supported: ${names mkString " or "}!")) } .handleNotFound { complete((NotFound, "Not here!")) } .result()
所有Rejection类型都在Rejection.scala里界说。result()函数返回Rejection类型:
def result(): RejectionHandler = new BuiltRejectionHandler(cases.result(), notFound, isDefault)
我们也可以用mapRejetionResponse对现成handler中孕育产生的HttpResponse进行转换:
RejectionHandler.default .mapRejectionResponse { case res @ HttpResponse(_, _, ent: HttpEntity.Strict, _) => // since all Akka default rejection responses are Strict this will handle all rejections val message = ent.data.utf8String.replaceAll("\"", """\"""") // we copy the response in order to keep all headers and status code, wrapping the message as hand rolled JSON // you could the entity using your favourite marshalling library (e.g. spray json or anything else) res.copy(entity = HttpEntity(ContentTypes.`application/json`, s"""{"rejection": "$message"}""")) case x => x // pass through all other types of responses }
下面是一个对照全面的RejectionHandle应用示范: