Akka(31): Http:High-Level-Api,Route rejection handling
2017-10-25 10:35
399 查看
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在最终都会得到处理:
下面是Route.seal()函数定义:
RejectionHandler.default是Akka-http提供的默认handler。我们也可以把自定义的隐式RejectionHandler实例放在可视域内就会自动被调用了。下面是一个自定义RejectionHandler例子:
所有Rejection类型都在Rejection.scala里定义。result()函数返回Rejection类型:
我们也可以用mapRejetionResponse对现成handler中产生的HttpResponse进行转换:
下面是一个比较全面的RejectionHandle应用示范:
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()) }
相关文章推荐
- Akka(31): Http:High-Level-Api,Route rejection handling
- Akka(31): Http:High-Level-Api,Route rejection handling
- Akka(32): Http:High-Level-Api,Route exception handling
- Akka(32): Http:High-Level-Api,Route exception handling
- Akka(30): Http:High-Level-Api,Routing DSL
- Akka(30): Http:High-Level-Api,Routing DSL
- Akka(30): Http:High-Level-Api,Routing DSL
- Akka(29): Http:Server-Side-Api,Low-Level-Api
- Akka(29): Http:Server-Side-Api,Low-Level-Api
- Akka(29): Http:Server-Side-Api,Low-Level-Api
- Akka(36): Http:Client-side-Api,Client-Connections
- Akka(36): Http:Client-side-Api,Client-Connections
- kafka Consumer high-level api 之白名单
- YTKRequest - 基于 AFNetworking 封装的一套 High Level 的 API
- 使用kafka consumer high-level API开发的一些问题
- Towards a High-Level Trusted Computing API for Android Software Stack
- 【异常】关于使用ES的RestHighLevel JavaAPI执行search操作时,参数的问题
- Handling HTTP 404 Error in ASP.NET Web API
- Kafka High Level API vs. Low Level API
- Unity Networking API文档翻译(二):The High Level API