Gets: "+gets+" Posts: "+posts+"
Last Get: "+lastget.getOrElse("Never").toString+" Last Post: "+lastpost.getOrElse("Never").toString+"
" get.OK(res) // handle a post request case post:Post => // the expected content type of the request // similar to @Consumes if (post.request.getContentType startsWith MediaType.APPLICATION_FORM_URLENCODED) { // the content type of the response. // similar to @Produces annotation post.response.setContentType(MediaType.TEXT_HTML) // "work" posts += 1 lastpost = Some(new Date) // respond val res = "Gets: "+gets+" Posts: "+posts+"
Last Get: "+lastget.getOrElse("Never").toString+" Last Post: "+lastpost.getOrElse("Never").toString+"
" post.OK(res) } else { post.UnsupportedMediaType("Content-Type request header missing or incorrect (was '" + post.request.getContentType + "' should be '" + MediaType.APPLICATION_FORM_URLENCODED + "')") } } case other: RequestMethod => other.NotAllowed("Invalid method for this endpoint") } } **Timeouts** Messages will expire according to the default timeout (specified in akka.conf). Individual messages can also be updated using the *timeout* method. One thing that may seem unexpected is that when an expired request returns to the caller, it will have a status code of OK (200). Mist will add an HTTP header to such responses to help clients, if applicable. By default, the header will be named "Async-Timeout" with a value of "expired" - both of which are configurable. Another Example - multiplexing handlers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As noted above, hook functions are non-exclusive. This means multiple actors can handle the same request if desired. In this next example, the hook functions are identical (yes, the same one could have been reused) and new instances of both A and B actors will be created to handle the Post. A third mediator is inserted to coordinate the results of these actions and respond to the caller. .. code-block:: scala package sample.mist import akka.actor._ import akka.actor.Actor._ import akka.http._ import javax.servlet.http.HttpServletResponse class InterestingService extends Actor with Endpoint { final val ServiceRoot = "/interesting/" final val Multi = ServiceRoot + "multi/" // use the configurable dispatcher self.dispatcher = Endpoint.Dispatcher // // The "multi" endpoint shows forking off multiple actions per request // It is triggered by POSTing to http://localhost:9998/interesting/multi/{foo} // Try with/without a header named "Test-Token" // Try with/without a form parameter named "Data" def hookMultiActionA(uri: String): Boolean = uri startsWith Multi def provideMultiActionA(uri: String): ActorRef = actorOf(new ActionAActor(complete)) def hookMultiActionB(uri: String): Boolean = uri startsWith Multi def provideMultiActionB(uri: String): ActorRef = actorOf(new ActionBActor(complete)) // // this is where you want attach your endpoint hooks // override def preStart() = { // // we expect there to be one root and that it's already been started up // obviously there are plenty of other ways to obtaining this actor // the point is that we need to attach something (for starters anyway) // to the root // val root = Actor.registry.actorsFor(classOf[RootEndpoint]).head root ! Endpoint.Attach(hookMultiActionA, provideMultiActionA) root ! Endpoint.Attach(hookMultiActionB, provideMultiActionB) } // // since this actor isn't doing anything else (i.e. not handling other messages) // just assign the receive func like so... // otherwise you could do something like: // def myrecv = {...} // def receive = myrecv orElse handleHttpRequest // def receive = handleHttpRequest // // this guy completes requests after other actions have occurred // lazy val complete = actorOf[ActionCompleteActor] } class ActionAActor(complete:ActorRef) extends Actor { import javax.ws.rs.core.MediaType def receive = { // handle a post request case post: Post => // the expected content type of the request // similar to @Consumes if (post.request.getContentType startsWith MediaType.APPLICATION_FORM_URLENCODED) { // the content type of the response. // similar to @Produces annotation post.response.setContentType(MediaType.TEXT_HTML) // get the resource name val name = post.request.getRequestURI.substring("/interesting/multi/".length) if (name.length % 2 == 0) post.response.getWriter.write("Action A verified request.
") else post.response.getWriter.write("Action A could not verify request.
") // notify the next actor to coordinate the response complete ! post } else post.UnsupportedMediaType("Content-Type request header missing or incorrect (was '" + post.request.getContentType + "' should be '" + MediaType.APPLICATION_FORM_URLENCODED + "')") } } } class ActionBActor(complete:ActorRef) extends Actor { import javax.ws.rs.core.MediaType def receive = { // handle a post request case post: Post => // the expected content type of the request // similar to @Consumes if (post.request.getContentType startsWith MediaType.APPLICATION_FORM_URLENCODED) { // pull some headers and form params def default(any: Any): String = "" val token = post.getHeaderOrElse("Test-Token", default) val data = post.getParameterOrElse("Data", default) val (resp, status) = (token, data) match { case ("", _) => ("No token provided", HttpServletResponse.SC_FORBIDDEN) case (_, "") => ("No data", HttpServletResponse.SC_ACCEPTED) case _ => ("Data accepted", HttpServletResponse.SC_OK) } // update the response body post.response.getWriter.write(resp) // notify the next actor to coordinate the response complete ! (post, status) } else post.UnsupportedMediaType("Content-Type request header missing or incorrect (was '" + post.request.getContentType + "' should be '" + MediaType.APPLICATION_FORM_URLENCODED + "')") } case other: RequestMethod => other.NotAllowed("Invalid method for this endpoint") } } class ActionCompleteActor extends Actor { import collection.mutable.HashMap val requests = HashMap.empty[Int, Int] def receive = { case req: RequestMethod => if (requests contains req.hashCode) complete(req) else requests += (req.hashCode -> 0) case t: Tuple2[RequestMethod, Int] => if (requests contains t._1.hashCode) complete(t._1) else requests += (t._1.hashCode -> t._2) } def complete(req: RequestMethod) = requests.remove(req.hashCode) match { case Some(HttpServletResponse.SC_FORBIDDEN) => req.Forbidden("") case Some(HttpServletResponse.SC_ACCEPTED) => req.Accepted("") case Some(_) => req.OK("") case _ => {} } } Examples ^^^^^^^^ Using the Akka Mist module with OAuth ************************************* `