经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Scala » 查看文章
Akka-CQRS(12)- akka-http for http-web-service: Routing-服务项目接口
来源:cnblogs  作者:雪川大虫  时间:2019/6/20 10:08:12  对本文有异议

   上篇提到,按当前对web-service功能需要,我们需要完成数据转换marshalling,服务接口routing这两部分的调研和示范。上篇已经完成了对序列化marshalling的讨论,这篇就介绍一下routing了。akka-http提供了一套功能强大,使用又很方便的Routing DSL。Route是个类型:

  1. type Route = RequestContext Future[RouteResult]

实际上就是个把HttpRequest转换成HttpResponse的函数。举个例子: 

  1. val route: Flow[HttpRequest, HttpResponse, NotUsed]=
  2. get {
  3. pathSingleSlash {
  4. complete(HttpEntity(ContentTypes.`text/html(UTF-8)`,"<html><body>Hello world!</body></html>"))
  5. } ~
  6. path("ping") {
  7. complete("PONG!")
  8. } ~
  9. path("crash") {
  10. sys.error("BOOM!")
  11. }
  12. }

这个route是个handler Flow, 但Route可以用RouteResult.route2HandlerFlow转换成Flow:

  1. /**
  2. * Turns a `Route` into a server flow.
  3. *
  4. * This conversion is also implicitly available through [[RouteResult#route2HandlerFlow]].
  5. */
  6. def handlerFlow(route: Route)(implicit
  7. routingSettings: RoutingSettings,
  8. parserSettings: ParserSettings,
  9. materializer: Materializer,
  10. routingLog: RoutingLog,
  11. executionContext: ExecutionContextExecutor = null,
  12. rejectionHandler: RejectionHandler = RejectionHandler.default,
  13. exceptionHandler: ExceptionHandler = null): Flow[HttpRequest, HttpResponse, NotUsed] =
  14. Flow[HttpRequest].mapAsync(1)(asyncHandler(route))
  15. ...
  16. implicit def route2HandlerFlow(route: Route)(
  17. implicit
  18. routingSettings: RoutingSettings,
  19. parserSettings: ParserSettings,
  20. materializer: Materializer,
  21. routingLog: RoutingLog,
  22. executionContext: ExecutionContext = null,
  23. rejectionHandler: RejectionHandler = RejectionHandler.default,
  24. exceptionHandler: ExceptionHandler = null
  25. ): Flow[HttpRequest, HttpResponse, NotUsed] =
  26. Route.handlerFlow(route)

route是由Directive类组合而成的一个决策树decision-tree。get、path、pathSingleSlash等都是Directive, 如:

  1. def path[L](pm: PathMatcher[L]): Directive[L] = pathPrefix(pm ~ PathEnd)

然后complete返回Route类:

  1. def complete(m: ToResponseMarshallable): StandardRoute =
  2. StandardRoute(_.complete(m))
  3. ...
  4. abstract class StandardRoute extends Route {
  5. def toDirective[L: Tuple]: Directive[L] = StandardRoute.toDirective(this)
  6. }

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的表达形式如下: 

  1. dirname(arguments) { extractions =>
  2. ... // 内层inner route
  3. }

下面是Directive的一些用例: 

下面的三个route效果相等:

  1. val route: Route = { ctx =>
  2. if (ctx.request.method == HttpMethods.GET)
  3. ctx.complete("Received GET")
  4. else
  5. ctx.complete("Received something else")
  6. }
  7. val route =
  8. get {
  9. complete("Received GET")
  10. } ~
  11. complete("Received something else")
  12. val route =
  13. get { ctx =>
  14. ctx.complete("Received GET")
  15. } ~
  16. complete("Received something else")

下面列出一些Directive的组合例子:

  1. val route: Route =
  2. path("order" / IntNumber) { id =>
  3. get {
  4. complete {
  5. "Received GET request for order " + id
  6. }
  7. } ~
  8. put {
  9. complete {
  10. "Received PUT request for order " + id
  11. }
  12. }
  13. }
  14. def innerRoute(id: Int): Route =
  15. get {
  16. complete {
  17. "Received GET request for order " + id
  18. }
  19. } ~
  20. put {
  21. complete {
  22. "Received PUT request for order " + id
  23. }
  24. }
  25. val route: Route = path("order" / IntNumber) { id => innerRoute(id) }
  26. val route =
  27. path("order" / IntNumber) { id =>
  28. (get | put) { ctx =>
  29. ctx.complete(s"Received ${ctx.request.method.name} request for order $id")
  30. }
  31. }
  32. val route =
  33. path("order" / IntNumber) { id =>
  34. (get | put) {
  35. extractMethod { m =>
  36. complete(s"Received ${m.name} request for order $id")
  37. }
  38. }
  39. }
  40. val getOrPut = get | put
  41. val route =
  42. path("order" / IntNumber) { id =>
  43. getOrPut {
  44. extractMethod { m =>
  45. complete(s"Received ${m.name} request for order $id")
  46. }
  47. }
  48. }
  49. val route =
  50. (path("order" / IntNumber) & getOrPut & extractMethod) { (id, m) =>
  51. complete(s"Received ${m.name} request for order $id")
  52. }
  53. val orderGetOrPutWithMethod =
  54. path("order" / IntNumber) & (get | put) & extractMethod
  55. val route =
  56. orderGetOrPutWithMethod { (id, m) =>
  57. complete(s"Received ${m.name} request for order $id")
  58. }

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

  1. //#basic
  2. abstract class Directive[L](implicit val ev: Tuple[L]) {
  3. /**
  4. * Calls the inner route with a tuple of extracted values of type `L`.
  5. *
  6. * `tapply` is short for "tuple-apply". Usually, you will use the regular `apply` method instead,
  7. * which is added by an implicit conversion (see `Directive.addDirectiveApply`).
  8. */
  9. def tapply(f: L Route): Route
  10. ...
  11. }
  12. /**
  13. * Constructs a directive from a function literal.
  14. */
  15. def apply[T: Tuple](f: (T Route) Route): Directive[T] =
  16. new Directive[T] { def tapply(inner: T Route) = f(inner) }
  17. /**
  18. * A Directive that always passes the request on to its inner route (i.e. does nothing).
  19. */
  20. val Empty: Directive0 = Directive(_(()))
  21. ...
  22. implicit class SingleValueModifiers[T](underlying: Directive1[T]) extends AnyRef {
  23. def map[R](f: T R)(implicit tupler: Tupler[R]): Directive[tupler.Out] =
  24. underlying.tmap { case Tuple1(value) f(value) }
  25. def flatMap[R: Tuple](f: T Directive[R]): Directive[R] =
  26. underlying.tflatMap { case Tuple1(value) f(value) }
  27. def require(predicate: T Boolean, rejections: Rejection*): Directive0 =
  28. underlying.filter(predicate, rejections: _*).tflatMap(_ Empty)
  29. def filter(predicate: T Boolean, rejections: Rejection*): Directive1[T] =
  30. underlying.tfilter({ case Tuple1(value) predicate(value) }, rejections: _*)
  31. }
  32. }

注意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的自动展平,能够实现下面的自动转换结果:

  1. Directive1[T] = Directive[Tuple1[T]]
  2. Directive1[Tuple2[M,N]] = Directive[Tuple1[Tuple2[M,N]]] = Directive[Tuple2[M,N]]
  3. Directive1[Tuple3[M,N,G]] = ... = Directive[Tuple3[M,N,G]]
  4. Directive1[Tuple4[M1,M2,M3,M4]] = ... = Directive[Tuple4[M1,M2,M3,M4]]
  5. ...
  6. Directive1[Unit] = Directive0

Directive1,Directive0:

  1. type Directive0 = Directive[Unit]
  2. type Directive1[T] = Directive[Tuple1[T]]

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

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

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

  1. /**
  2. * Provides a way to convert a value into an Tuple.
  3. * If the value is already a Tuple then it is returned unchanged, otherwise it's wrapped in a Tuple1 instance.
  4. */
  5. trait Tupler[T] {
  6. type Out
  7. def OutIsTuple: Tuple[Out]
  8. def apply(value: T): Out
  9. }
  10. object Tupler extends LowerPriorityTupler {
  11. implicit def forTuple[T: Tuple]: Tupler[T] { type Out = T } =
  12. new Tupler[T] {
  13. type Out = T
  14. def OutIsTuple = implicitly[Tuple[Out]]
  15. def apply(value: T) = value
  16. }
  17. }
  18. private[server] abstract class LowerPriorityTupler {
  19. implicit def forAnyRef[T]: Tupler[T] { type Out = Tuple1[T] } =
  20. new Tupler[T] {
  21. type Out = Tuple1[T]
  22. def OutIsTuple = implicitly[Tuple[Out]]
  23. def apply(value: T) = Tuple1(value)
  24. }
  25. }

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

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

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

  1. val route =
  2. (pathPrefix("pos-on-cloud") & get) {
  3. ((pathSuffix("logon") & parameters('shopid.as[Int], 'opr)){ (id, op) =>
  4. complete(s"logon: shopid=$id and opr=$op")
  5. }
  6. ~ (pathSuffix("logoff") & parameter('shopid.as[Int])){ id =>
  7. complete(s"logoff: shopid=$id")
  8. }
  9. ~ (pathSuffix("logsales") & parameters(
  10. 'shopid.as[Int],
  11. 'acct,
  12. 'dpt,
  13. 'code,
  14. 'qty.as[Int],
  15. 'price.as[Int]
  16. )){ (id,acct,dpt,code,qty,price) =>
  17. complete(s"logsales: shopid=$id,$acct,$dpt,$code,$qty,$price")
  18. }
  19. ~ (pathSuffix("subtotal") & parameters('shopid.as[Int],'level)){ (id,l) =>
  20. complete(s"subtotal: shopid=$id, level=$l")
  21. }
  22. )
  23. }

用browser来测试:

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

没错,解析正确!

原文链接:http://www.cnblogs.com/tiger-xc/p/11056657.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号