Spray(3)spray-routing

Spray(3)spray-routing


The spray-routing module provides a high-level, very flexible routing DSL for RESTful web services. Normally I will use it either on top of a spray-can HttpServer or inside of a servlet container together with spray-servlet.



Dependencies
spray-http, spray-httpx, spray-util, spray-caching, shapeless, Scalate, akka-actor



"io.spray"            %   "spray-routing"             % "1.1-M7",
"com.typesafe"        %   "config"                    % "1.0.0"


Configuration
Just like Akka spray-routing relies on the type safe config library for configuration. The same configuration file with akka, application.conf(reference.conf).



Getting Started
SimpleRoutingApp
package com.sillycat.easyspray.external

import spray.routing.SimpleRoutingApp

import spray.http.MediaTypes._

import scala.concurrent.duration._

object EasyRoutingApp extends App with SimpleRoutingApp {



  startServer(interface = "localhost", port = 8080) {

    get {

      path("") {

        redirect("/hello")

      } ~

      path("hello") {

        respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here

          complete {

            <html>

              <h1>Say hello to <em>spray</em> on <em>spray-can</em>!</h1>

              <p>(<a href="/stop?method=post">stop server</a>)</p>

            </html>

          }

        }

      }

    } ~

    (post | parameter('method ! "post")) {

      path("stop") {

        complete {

          system.scheduler.scheduleOnce(1 second span) {

            system.shutdown()

          }

          "Shutting down in 1 second…"

        }

      }

    }

  }

 

}



Key Concepts
Big Picture
We have spray-can HttpServer and the spray-servlet connector servlet both an actor-level to response to incoming HTTP requests by simply replying with an HttpResponse.



We can do complete REST API by pattern-matching against the incoming HttpRequest. But it is not wise to do in that way.



As an alternative spray-routing provides a flexible DSL for expressing service behavior.



def receive = runRoute {

     path("ping"){

          get{

               complete("PONG")

          }

     }

}



Routes
type Route = RequestContext => Unit



Generally when a route receives a request (or rather a RequestContext for it) it can do one of the three things:

1. Complete the request by calling requestContext.complete(…)

2. Reject the request by calling requestContext.reject(…)

3. Ignore the request



Constructing Routes
ctx => ctx.complete("Response")     ----> complete("Response")



Composing Routes
Route transformation, Route filtering, Route chaining



The Routing Tree
varl route =

     a{

          b{

               c{…} ~

               d{…} ~

               ...

          } ~

          ...

     }



Directives
val route =
  path("order" / IntNumber) { id =>
    (get | put) { ctx =>
      ctx.complete("Received " + ctx.request.method + " request for order " + id)
    } }
Or

val getOrPut = get | put
val route =
  (path("order" / IntNumber) & getOrPut) { id => ctx =>
    ctx.complete("Received " + ctx.request.method + " request for order " + id) }


Rejections
The ~ operator was introduced, which connects two routes in a way that allows a second route to get a go at a request if the first route "rejected" it.



Exception Handling


import spray.routing.SimpleRoutingApp

import spray.http.MediaTypes._

import scala.concurrent.duration._

import spray.util.LoggingContext

import spray.routing.ExceptionHandler

import spray.util.LoggingContext

import spray.http.StatusCodes._

import spray.routing._



object EasyRoutingApp extends App with SimpleRoutingApp {



  implicit def myExceptionHandler(implicit log: LoggingContext) =

  ExceptionHandler.fromPF {

    case e: ArithmeticException => ctx =>

      log.warning("Request {} could not be handled normally", ctx.request)

      ctx.complete(InternalServerError, "Bad numbers, bad result!!!")

  }

…snip…


Timeout Handling
import spray.http._
import spray.routing._

class MyService extends Actor with HttpServiceActor {
  def receive = handleTimeouts orElse runRoute(myRoute)

  def myRoute: Route = `<my-route-definition>`

  def handleTimeouts: Receive = {
    case Timeout(x: HttpRequest) =>
      sender ! HttpResponse(StatusCodes.InternalServerError, "Too late")
  } }




References:
http://spray.io/documentation/spray-routing/

你可能感兴趣的:(out)