Akka(31): Http:High-Level-Api,Route rejection handling

来源:互联网 发布:韩国电视直播软件 编辑:程序博客网 时间:2024/06/05 20:04

  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)`,"

  hello http server

  ")}

  }

  }

  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())

  }

阅读全文
0 0
原创粉丝点击