zoukankan      html  css  js  c++  java
  • Akka(31): Http:High-Level-Api,Route rejection handling

       Route 是Akka-http routing DSL的核心部分,使用户能比较方便的从http-server的角度筛选http-request、进行server运算、构建回复的http-response。所谓筛选http-request主要目的是容许request进入下一内层Route,或者拒绝reject request。比如这个~符号:它连接了上下两个独立的Route。如果上面的Route拒绝了一个request,那么下面的Route就会接着尝试这个request。一般来说:当一个筛选功能的Directive如get遇到一个不符合筛选条件的request时,它会拒绝reject这个request进入下一层Route。这时用~符号链接的下一组Route会接着尝试,直到链条最后一组Route。整个过程中的这些rejection事件会被记录下来最后由某个隐式或明式的RejectionHandler实例把这组rejection转化成HttpResponse返回用户。rejection也可以直接调用requestContext.reject(...)产生。Akka-http是通过在运行Route时用Route.seal(route)的方式来确保所有rejection在最终都会得到处理:

      override def seal(system: ActorSystem, materializer: Materializer): Route = {
        implicit val s = system
        implicit val m = materializer
    
        RouteAdapter(scaladsl.server.Route.seal(delegate))
      }

    下面是Route.seal()函数定义:

      /**
       * "Seals" a route by wrapping it with default exception handling and rejection conversion.
       *
       * A sealed route has these properties:
       *  - The result of the route will always be a complete response, i.e. the result of the future is a
       *    ``Success(RouteResult.Complete(response))``, never a failed future and never a rejected route. These
       *    will be already be handled using the implicitly given [[RejectionHandler]] and [[ExceptionHandler]] (or
       *    the default handlers if none are given or can be found implicitly).
       *  - Consequently, no route alternatives will be tried that were combined with this route
       *    using the ``~`` on routes or the [[Directive.|]] operator on directives.
       */
      def seal(route: Route)(implicit
        routingSettings: RoutingSettings,
                             parserSettings:   ParserSettings   = null,
                             rejectionHandler: RejectionHandler = RejectionHandler.default,
                             exceptionHandler: ExceptionHandler = null): Route = {
        import directives.ExecutionDirectives._
        // optimized as this is the root handler for all akka-http applications
        (handleExceptions(ExceptionHandler.seal(exceptionHandler)) & handleRejections(rejectionHandler.seal))
          .tapply(_ ⇒ route) // execute above directives eagerly, avoiding useless laziness of Directive.addByNameNullaryApply
      }

    RejectionHandler.default是Akka-http提供的默认handler。我们也可以把自定义的隐式RejectionHandler实例放在可视域内就会自动被调用了。下面是一个自定义RejectionHandler例子:

          RejectionHandler.newBuilder()
            .handle { case MissingCookieRejection(cookieName) =>
              complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!"))
            }
            .handle { case AuthorizationFailedRejection =>
              complete((Forbidden, "You're out of your depth!"))
            }
            .handle { case ValidationRejection(msg, _) =>
              complete((InternalServerError, "That wasn't valid! " + msg))
            }
            .handleAll[MethodRejection] { methodRejections =>
              val names = methodRejections.map(_.supported.name)
              complete((MethodNotAllowed, s"Can't do that! Supported: ${names mkString " or "}!"))
            }
            .handleNotFound { complete((NotFound, "Not here!")) }
            .result()

    所有Rejection类型都在Rejection.scala里定义。result()函数返回Rejection类型:

       def result(): RejectionHandler =
          new BuiltRejectionHandler(cases.result(), notFound, isDefault)

    我们也可以用mapRejetionResponse对现成handler中产生的HttpResponse进行转换: 

          RejectionHandler.default
            .mapRejectionResponse {
              case res @ HttpResponse(_, _, ent: HttpEntity.Strict, _) =>
                // since all Akka default rejection responses are Strict this will handle all rejections
                val message = ent.data.utf8String.replaceAll(""", """"""")
                
                // we copy the response in order to keep all headers and status code, wrapping the message as hand rolled JSON
                // you could the entity using your favourite marshalling library (e.g. spray json or anything else) 
                res.copy(entity = HttpEntity(ContentTypes.`application/json`, s"""{"rejection": "$message"}"""))
                
              case x => x // pass through all other types of responses
            }

    下面是一个比较全面的RejectionHandle应用示范:

    akka.actor._
    import akka.http.scaladsl.Http
    import akka.http.scaladsl.model._
    import akka.http.scaladsl.server._
    import akka.http.scaladsl.server.Directives._
    import akka.stream._
    import akka.stream.scaladsl._
    import akka._
    import StatusCodes._
    import scala.concurrent._
    
    object RejectionHandlers {
      implicit val rejectionHandler =
        (RejectionHandler.newBuilder()
          .handle { case MissingCookieRejection(cookieName) =>
            complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!"))
          }
          .handle { case AuthorizationFailedRejection =>
            complete((Forbidden, "You're out of your depth!"))
          }
          .handle { case ValidationRejection(msg, _) =>
            complete((InternalServerError, "That wasn't valid! " + msg))
          }
          .handleAll[MethodRejection] { methodRejections =>
          val names = methodRejections.map(_.supported.name)
          complete((MethodNotAllowed, s"Can't do that! Supported: ${names mkString " or "}!"))
          }
          .handleNotFound {
            extractUnmatchedPath { p =>
              complete((NotFound, s"The path you requested [${p}] does not exist."))
            }
          }
          .result())
          .mapRejectionResponse {
            case res @ HttpResponse(_, _, ent: HttpEntity.Strict, _) =>
              // since all Akka default rejection responses are Strict this will handle all rejections
              val message = ent.data.utf8String.replaceAll(""", """"""")
    
              // we copy the response in order to keep all headers and status code, wrapping the message as hand rolled JSON
              // you could the entity using your favourite marshalling library (e.g. spray json or anything else)
              res.copy(entity = HttpEntity(ContentTypes.`application/json`, s"""{"rejection mapped response": "$message"}"""))
    
            case x => x // pass through all other types of responses
          }
    }
    
    object RejectionHandlerDemo extends App {
      import RejectionHandlers._
    
      implicit val httpSys = ActorSystem("httpSys")
      implicit val httpMat = ActorMaterializer()
      implicit val httpEc = httpSys.dispatcher
    
      val (port, host) = (8011,"localhost")
    
      val route: Flow[HttpRequest, HttpResponse, NotUsed] = pathPrefix("hello" ~ PathEnd) {
        get {
          complete {Future("OK")}
          //HttpEntity(ContentTypes.`text/html(UTF-8)`,"<h1> hello http server </h1>")}
        }
      }
    
      val bindingFuture: Future[Http.ServerBinding] = Http().bindAndHandle(route,host,port)
    
      println(s"Server running at $host $port. Press any key to exit ...")
    
      scala.io.StdIn.readLine()
    
      bindingFuture.flatMap(_.unbind())
        .onComplete(_ => httpSys.terminate())
    
    }

     

     

     

     

  • 相关阅读:
    如何将应用安装到/system/app下
    WPF Perf: RenderCapability.Tier & DesiredFrameRate
    DataGridComboBoxColumn为什么就不能在Binding的时候引用其他Named Element了呢?
    A366T使用技巧
    在XAML里面引用枚举值的注意点
    高斯消元bzoj1013球形空间产生器
    欧拉函数bzoj2818简单推导
    链剖进阶ing填坑NOIP2013货车运输
    .net 2.0 BackgroundWorker 文章三篇
    19号晚21号上午
  • 原文地址:https://www.cnblogs.com/tiger-xc/p/7728221.html
Copyright © 2011-2022 走看看