上篇提到,按当前对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 {
"Received PUT request for order " + id
}
}
} def innerRoute(id: Int): Route =
get {
complete {
"Received GET request for order " + id
}
} ~
put {
complete {
"Received PUT request for order " + id
}
}
val route: Route = path("order" / IntNumber) { id => innerRoute(id) } val route =
path("order" / IntNumber) { id =>
(get | put) { ctx =>
ctx.complete(s"Received ${ctx.request.method.name} request for order $id")
}
} val route =
path("order" / IntNumber) { id =>
(get | put) {
extractMethod { m =>
complete(s"Received ${m.name} request for order $id")
}
}
} val getOrPut = get | put
val route =
path("order" / IntNumber) { id =>
getOrPut {
extractMethod { m =>
complete(s"Received ${m.name} request for order $id")
}
}
} val route =
(path("order" / IntNumber) & getOrPut & extractMethod) { (id, m) =>
complete(s"Received ${m.name} request for order $id")
} val orderGetOrPutWithMethod =
path("order" / IntNumber) & (get | put) & extractMethod
val route =
orderGetOrPutWithMethod { (id, m) =>
complete(s"Received ${m.name} request for order $id")
}

我们可以从上面这些示范例子得出结论:Directive的组合能力是routing DSL的核心。来看看Directive的组合能力是如何实现的。Directive类定义如下:

//#basic
abstract class Directive[L](implicit val ev: Tuple[L]) { /**
* Calls the inner route with a tuple of extracted values of type `L`.
*
* `tapply` is short for "tuple-apply". Usually, you will use the regular `apply` method instead,
* which is added by an implicit conversion (see `Directive.addDirectiveApply`).
*/
def tapply(f: L ⇒ Route): Route
...
}
/**
* Constructs a directive from a function literal.
*/
def apply[T: Tuple](f: (T ⇒ Route) ⇒ Route): Directive[T] =
new Directive[T] { def tapply(inner: T ⇒ Route) = f(inner) } /**
* A Directive that always passes the request on to its inner route (i.e. does nothing).
*/
val Empty: Directive0 = Directive(_(()))
...
implicit class SingleValueModifiers[T](underlying: Directive1[T]) extends AnyRef {
def map[R](f: T ⇒ R)(implicit tupler: Tupler[R]): Directive[tupler.Out] =
underlying.tmap { case Tuple1(value) ⇒ f(value) } def flatMap[R: Tuple](f: T ⇒ Directive[R]): Directive[R] =
underlying.tflatMap { case Tuple1(value) ⇒ f(value) } def require(predicate: T ⇒ Boolean, rejections: Rejection*): Directive0 =
underlying.filter(predicate, rejections: _*).tflatMap(_ ⇒ Empty) def filter(predicate: T ⇒ Boolean, rejections: Rejection*): Directive1[T] =
underlying.tfilter({ case Tuple1(value) ⇒ predicate(value) }, rejections: _*)
}
}

注意Directive.apply参数f: (T =>Route)=>Route), 代表 dirname (args){extractions => ...} 这样的构建函数款式。还有implicit ev: Tuple[L]是给compiler的证例,它要求Tuple[L]存在于可视域。Akka-http提供了所有22个TupleXX[L]的隐形实例。再注意implicit class singleValueModifiers[T]:它提供了多层Directive的自动展平,能够实现下面的自动转换结果:

Directive1[T] = Directive[Tuple1[T]]
Directive1[Tuple2[M,N]] = Directive[Tuple1[Tuple2[M,N]]] = Directive[Tuple2[M,N]]
Directive1[Tuple3[M,N,G]] = ... = Directive[Tuple3[M,N,G]]
Directive1[Tuple4[M1,M2,M3,M4]] = ... = Directive[Tuple4[M1,M2,M3,M4]]
...
Directive1[Unit] = Directive0

Directive1,Directive0:

  type Directive0 = Directive[Unit]
type Directive1[T] = Directive[Tuple1[T]]

下面是这几种Directive的使用模式:

  dirname { route }                  //Directive0
dirname[L] { L => route } //Directive1[L]
dirname[T] { (T1,T2...) => route} //Directive[T]

任何类型值到Tuple的自动转换是通过Tupler类实现的:

/**
* Provides a way to convert a value into an Tuple.
* If the value is already a Tuple then it is returned unchanged, otherwise it's wrapped in a Tuple1 instance.
*/
trait Tupler[T] {
type Out
def OutIsTuple: Tuple[Out]
def apply(value: T): Out
} object Tupler extends LowerPriorityTupler {
implicit def forTuple[T: Tuple]: Tupler[T] { type Out = T } =
new Tupler[T] {
type Out = T
def OutIsTuple = implicitly[Tuple[Out]]
def apply(value: T) = value
}
} private[server] abstract class LowerPriorityTupler {
implicit def forAnyRef[T]: Tupler[T] { type Out = Tuple1[T] } =
new Tupler[T] {
type Out = Tuple1[T]
def OutIsTuple = implicitly[Tuple[Out]]
def apply(value: T) = Tuple1(value)
}
}

好了,还是回到具体的Uri解析上来吧。在POS例子里需要上传的指令款式如下:

http://192.168.11.189:2588/pos/logon?shopid=1101&opr=1010
http://192.168.11.189:2588/pos/logoff?shopid=1101
http://192.168.11.189:2588/pos/logsales?shopid=1101&acct=001&dpt=01&code=978111&qty=3&price=1200
http://192.168.11.189:2588/pos/shopid=1101&subtotal?level=0
http://192.168.11.189:2588/pos/shopid=1101&discount?disctype=2&grouped=true&code=481&percent=20

基本上全部是Uri Path解析的工作。下面是具体的Route示范:

  val route =
(pathPrefix("pos-on-cloud") & get) {
((pathSuffix("logon") & parameters('shopid.as[Int], 'opr)){ (id, op) =>
complete(s"logon: shopid=$id and opr=$op")
}
~ (pathSuffix("logoff") & parameter('shopid.as[Int])){ id =>
complete(s"logoff: shopid=$id")
}
~ (pathSuffix("logsales") & parameters(
'shopid.as[Int],
'acct,
'dpt,
'code,
'qty.as[Int],
'price.as[Int]
)){ (id,acct,dpt,code,qty,price) =>
complete(s"logsales: shopid=$id,$acct,$dpt,$code,$qty,$price")
}
~ (pathSuffix("subtotal") & parameters('shopid.as[Int],'level)){ (id,l) =>
complete(s"subtotal: shopid=$id, level=$l")
}
)
}

用browser来测试:

http://192.168.11.189:8011/pos-on-cloud/logsales?shopid=1101&acct=001&dpt=01&code=978111&qty=3&price=1200

logsales: shopid=,,,,,

没错,解析正确!

Akka-CQRS(12)- akka-http for http-web-service: Routing-服务项目接口的更多相关文章

  1. Node.js 从零开发 web server博客项目[接口]

    web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...

  2. Web Service 的服务端的引用

    1.先说说服务端的引用 先写一个Web Service 的文件  上图 创建一个web 项目或者网站  然后添加新项 创建一个web服务 得到 下面的页面 然后运行起来 然后复制下地址 接下来创建另一 ...

  3. 构建安全的Xml Web Service系列之初探使用Soap头

    原文:构建安全的Xml Web Service系列之初探使用Soap头 Xml Web Service 从诞生那天就说自己都么都么好,还津津乐道的说internet也会因此而进入一个新纪元,可5年多来 ...

  4. Web Service学习笔记(webservice、soap、wsdl、jws详细分析)

    Web Service概述 Web Service的定义 W3C组织对其的定义如下,它是一个软件系统,为了支持跨网络的机器间相互操作交互而设计.Web Service服务通常被定义为一组模块化的API ...

  5. Web Service学习笔记

    Web Service概述 Web Service的定义 W3C组织对其的定义如下,它是一个软件系统,为了支持跨网络的机器间相互操作交互而设计.Web Service服务通常被定义为一组模块化的API ...

  6. Web Service学习笔记(webservice、soap、wsdl、jws详细分析) (转)

    Web Service概述 Web Service的定义 W3C组织对其的定义如下,它是一个软件系统,为了支持跨网络的机器间相互操作交互而设计.Web Service服务通常被定义为一组模块化的API ...

  7. 构建安全的Xml Web Service系列之如何察看SoapMessage

    原文:构建安全的Xml Web Service系列之如何察看SoapMessage 上一篇文章地址:构建安全的Xml Web Service系列一之初探使用Soap头 (5-22 12:53)     ...

  8. MyEclipse构建Web Service(Xfire框架)

    以下是本人原创,如若转载和使用请注明转载地址.本博客信息切勿用于商业,可以个人使用,若喜欢我的博客,请关注我,谢谢!博客地址 任务要求: 使用Xfire实现一个简单的CalculatorWebServ ...

  9. 建立自己的Web service(SOAP篇)

    1.简介 这篇文章主要介绍采用SOAP来建立以及访问Web service接口. Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML(标准通用 ...

  10. Web Service 简介

    最近使用ODI的工具箱中的ODIInvokeWebService.因此简单了解下WebService的理论知识. 一.Web Service 简介 Web Service就是可编程的URL,使用标准的 ...

随机推荐

  1. D2Admin基本使用

    目录 d2-admin基本使用 1 安装 1.1 全局安装 d2-admin 1.2 创建项目 1.3 启动项目 1.4 注意事项 2 修改框架 title 和 logo 2.1 修改 title 2 ...

  2. 选美?作秀?MES系统的选择更应该从实际出发

    MES选型不是做秀,不是选美. 如今不少企业在信息化推广应用过程中面面求好.追求完美,用意没错,然而在MES开发过程中,软件商不可能将今后各种可能出现的问题考虑周全,不可能将系统做到十全十美.随着系统 ...

  3. Bugku 多次

    网址:http://123.206.87.240:9004/1ndex.php?id=1 前言:bugku中一涉及多次注入的题 1.异或注入(判断字符是否被过滤) 0X00   很明显 注入点在id上 ...

  4. testlink使用方法

    1.测试项目管理         创建新项目: 类型设置成:活动的,公开的,才可以进行创建.创建完进入主页. 2.用户管理                注意:一般账号就是角色名,共6个角色,记住账号 ...

  5. Fundebug前端JavaScript插件更新至1.8.0,兼容低版本的Android浏览器

    摘要: 兼容低版本Android浏览器,请大家及时更新. Fundebug前端BUG监控服务 Fundebug是专业的程序BUG监控平台,我们JavaScript插件可以提供全方位的BUG监控,可以帮 ...

  6. 机器学习之KMeans聚类

    零.学习生成测试数据 from sklearn.datasets import make_blobs from matplotlib import pyplot # create test data ...

  7. opencart 3 配置阿里邮箱smtp实测可用

    最近ytkah在做一个客户的opencart项目时,配置阿里邮箱smtp一直收不到邮件,修改了很多配置文件也不起作用,今天再继续调试终于成功了,下面把所有步骤都记录下来,希望能帮到碰到同样问题的朋友们 ...

  8. application platform as a service (aPaaS)

    Application platform as a service (aPaaS) is a cloud service that provides environments for the deve ...

  9. Centos7安装MySQL(多图)

    文章目录 一.在线安装1.替换网易yum源2.清理缓存3.下载rpm文件4.安装MySQL数据库二.本地安装1.上传MySQL安装包2.安装依赖的程序包3.卸载mariadb程序包4.安装MySQL程 ...

  10. date命令的FORMAT中输入空格的几种方法

    1.date +%Y-%m-%d\ (一个空格)%H:%M:%S 此命令中用了转义字符 \ ,将空格转义出来 2.date +%Y-%m-%d'  '%H:%M:%S 此命令中的单引号内可以是一个或多 ...