上篇提到,按当前对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. git分支的衍合

    一般我们使用衍合的目的,是想要得到一个能在远程分支上干净应用的补丁 — 比如某些项目你不是维护者,但想帮点忙的话,最好用衍合:先在自己的一个分支里进行开发,当准备向主项目提交补丁的时候,根据最新的 o ...

  2. Vue – 基础学习(4):事件修饰符

    Vue – 基础学习(3):事件修饰符

  3. Spark GraphX图计算简单案例【代码实现,源码分析】

    一.简介 参考:https://www.cnblogs.com/yszd/p/10186556.html 二.代码实现 package big.data.analyse.graphx import o ...

  4. AAC的RTP封装中的AU头分析

      解码器收到一个RTP的AAC流,发现RTP流里的音频里带有4个字节AU头,然后才是AAC的ADTS头.     这种情况之前已经出现过多次,每次我们都告知对方,不要往AAC前面加AU头,解码器不支 ...

  5. NOIp常用的算法

    24种NOIp常用的算法 https://blog.csdn.net/weixin_40676873/article/details/81166139 NOIP 算法总结(来自 啊哈磊的专栏) htt ...

  6. 团队项目-Beta冲刺及发布说明

    请大家在每次作业开头添加格式描述: 这个作业属于哪个课程 <课程的链接> 这个作业要求在哪里 <作业要求的链接> 团队名称 <写上团队名称>(附上团队博客链接) 这 ...

  7. django 权限设置

    关于权限: 首先明确权限是为了限制其他访问的url 通过在models使之关联的表再通过关联表的权限来permissions的url 来检测url的访问 一. 需要三个类五张表 from django ...

  8. Python3 if 变量variable SQL where 语句拼接

    最近在写python3的项目,在实际中运用到了根据 if 判断变量variable ,然后去拼接where子句.但是在百度.BING搜索中未找到合适的答案,这是自己想出来的典型php写法,这里做一下记 ...

  9. JPA 基础

    JPA 基础 数据库驱动 ==> JDBC 规范 ==> ORM 框架 ==> JPA 规范 ==> spring-data-jpa ORM 思想 JPA 的使用步骤 jpa ...

  10. 【字符串】 Z-algorithm

    Z-algorithm Algorithm Task 给定一个文本串 \(S\) 和一个模式串 \(T\),求 \(T\) 对于 \(S\) 的每个后缀子串的公共前缀子串. Limitations 要 ...