上篇提到,按当前对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. 面试题:java中String为什么要设置成final

    1.不可改变---执行效率高 2.因为String这个对象基本是被所有的类对象都会使用的到了,如果可以被复写,就会很乱套,比如map的key ,如果是一个string为key的话,String如果可以 ...

  2. python中for循环里去修改列表注意的事项

    你的微信好友当中有 5 个推销的,他们存在一个列表 # black_list=['卖茶叶', '卖面膜', '卖保险', '卖花生', '卖手机'] # 当中, 请把这 5 个人分别从 black_l ...

  3. MySQL基础-Linux从入门到精通第十天(非原创)

    文章大纲 一.关于数据库二.MySQL的安装与初始化三.MySQL的基本操作(难点)四.扩展五.学习资料下载六.参考文章   一.关于数据库 mysql的基础知识,可以参考文章:https://www ...

  4. Oracle的功能性sql

    --创建表空间 CREATE TABLESPACE FSNEW DATAFILE 'E:\oracle\oracledata\oradata\FSNEW' SIZE 30G EXTENT MANAGE ...

  5. CPU使用率与负载的爱恨情仇

    今天有一个电话面试,面试官问我:CentOS怎么查看CPU负载?我说:看top的第一行有load average.面试官又问:为什么从这就判定是负载高呢?依据是什么呢?然后... 然后我就尴尬了,挂了 ...

  6. python库包大全(转)

    python 库资源大全 转自: Python 资源大全中文版 环境管理 管理 Python 版本和环境的工具 p:非常简单的交互式 python 版本管理工具.官网 pyenv:简单的 Python ...

  7. CEF 拦截打开超链接事件

    使用 CEF 加载指定页面后,如果你希望控制页面在打开超链接时根据自己预定义的一些行为来操作,比如在自己的 UI 框架中新建一个 Tab 页又或者阻止打开新的页面等.我们就需要通过 CEF 提供的两个 ...

  8. 201871010109-胡欢欢《面向对象程序设计(java)》第二周学习总结

    开头: 项目 内容 这个作业属于哪个课程 <任课教师博客主页链接>     https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 <作业链接地 ...

  9. mobx 学习笔记

    Mobx 笔记 Mobx 三板斧,observable.observer.action. observable: 通过 observable(state) 定义组件的状态,包装后的状态是一个可观察数据 ...

  10. Hibernate框架学习3

    一对多|多对一 一对多 多对一 级联操作 结论: 简化操作.一定要用,save-update,不建议使用delete. 关系维护 在保存时.两方都会维护外键关系.关系维护两次,冗余了. 多余的维护关系 ...