上篇提到,按当前对web-service功能需要,我们需要完成数据转换marshalling,服务接口routing这两部分的调研和示范。上篇已经完成了对序列化marshalling的讨论,这篇就介绍一下routing了。akka-http提供了一套功能强大,使用又很方便的Routing DSL。Route是个类型:
type Route = RequestContext ? Future[RouteResult]
实际上就是个把HttpRequest转换成HttpResponse的函数。举个例子:
val route: Flow[HttpRequest, HttpResponse, NotUsed]=
get {
pathSingleSlash {
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`,"<html><body>Hello world!</body></html>"))
} ~
path("ping") {
complete("PONG!")
} ~
path("crash") {
sys.error("BOOM!")
}
}
这个route是个handler Flow, 但Route可以用RouteResult.route2HandlerFlow转换成Flow:
/**
* Turns a `Route` into a server flow.
*
* This conversion is also implicitly available through [[RouteResult#route2HandlerFlow]].
*/
def handlerFlow(route: Route)(implicit
routingSettings: RoutingSettings,
parserSettings: ParserSettings,
materializer: Materializer,
routingLog: RoutingLog,
executionContext: ExecutionContextExecutor = null,
rejectionHandler: RejectionHandler = RejectionHandler.default,
exceptionHandler: ExceptionHandler = null): Flow[HttpRequest, HttpResponse, NotUsed] =
Flow[HttpRequest].mapAsync(1)(asyncHandler(route))
...
implicit def route2HandlerFlow(route: Route)(
implicit
routingSettings: RoutingSettings,
parserSettings: ParserSettings,
materializer: Materializer,
routingLog: RoutingLog,
executionContext: ExecutionContext = null,
rejectionHandler: RejectionHandler = RejectionHandler.default,
exceptionHandler: ExceptionHandler = null
): Flow[HttpRequest, HttpResponse, NotUsed] =
Route.handlerFlow(route)
route是由Directive类组合而成的一个决策树decision-tree。get、path、pathSingleSlash等都是Directive, 如:
def path[L](pm: PathMatcher[L]): Directive[L] = pathPrefix(pm ~ PathEnd)
然后complete返回Route类:
def complete(m: ? ToResponseMarshallable): StandardRoute = StandardRoute(_.complete(m)) ... abstract class StandardRoute extends Route { def toDirective[L: Tuple]: Directive[L] = StandardRoute.toDirective(this) }
Directive的主要功能就是对HttpRequest的Uri进行解析,找出具体的服务接口点,已经对entity里的数据进行调取。
Route是一种可组合组件。我们可以用简单的Route组合成更多层次的Route。下面是组合Route的几种方式:
1、Route转化:对输入的request,输出的response进行转化处理后把实际运算托付给下一层内部(inner)Route
2、筛选Route:只容许符合某种条件的Route通过并拒绝其它不符合条件的Route
3、链接Route:假如一个Route被拒绝,尝试下一个Route。这个是通过 ~ 操作符号实现的
在Akka-http的routing DSL里这些Route组合操作是通过Directive实现的。Akka-http提供了大量现成的Directive,我们也可以自定义一些特殊功能的Directive,详情可以查询官方文件或者api文件。
Directive的表达形式如下:
dirname(arguments) { extractions => ... // 内层inner route
}
下面是Directive的一些用例:
下面的三个route效果相等:
val route: Route = { ctx =>
if (ctx.request.method == HttpMethods.GET) ctx.complete("Received GET") else ctx.complete("Received something else") } val route =
get { complete("Received GET") } ~ complete("Received something else") val route =
get { ctx => ctx.complete("Received GET") } ~ complete("Received something else")
下面列出一些Directive的组合例子:
val route: Route = path("order" / IntNumber) { id =>
get { complete { "Received GET request for order " + id } } ~ put { complete { "R