Ari
Ari

Reputation: 4191

Akka HTTP Routing and Marshaling

I'm just starting out with Akka HTTP and I'm having a bit of trouble with the routing DSL and marshaling. The tilde in the 'route' setup results in the error:

value ~ is not a member of akka.http.scaladsl.server.RequestContext ⇒ scala.concurrent.Future[akka.http.scaladsl.server.RouteResult] possible cause: maybe a semicolon is missing before 'value ~'?

Also, the JSON marshaling in the 'get' clause causing the error:

◾Cannot find JsonWriter or JsonFormat type class for scala.collection.immutable.Map[String,scala.collection.mutable.Map[String,Tweet]]

◾not enough arguments for method toJson: (implicit writer: spray.json.JsonWriter[scala.collection.immutable.Map[String,scala.collection> .mutable.Map[String,Tweet]]])spray.json.JsValue. Unspecified value parameter writer.

I've followed the documentation examples fairly closely, so I'd appreciate help in understanding these errors and how to resolve them. Thanks.

API

import akka.actor.ActorSystem
import scala.concurrent.Future
import akka.stream.ActorMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives.path
import akka.http.scaladsl.server.Directives.pathPrefix
import akka.http.scaladsl.server.Directives.post
import akka.http.scaladsl.server.Directives.get
import akka.http.scaladsl.server.Directives.complete
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives.{entity, as}
import akka.http.scaladsl.model.StatusCodes.{Created, OK}
import spray.json._
import DefaultJsonProtocol._
import akka.stream.Materializer
import scala.concurrent.ExecutionContext

trait RestApi {
  import TweetProtocol._
  import TweetDb._

  implicit val system: ActorSystem
  implicit val materializer: Materializer
  implicit val execCtx: ExecutionContext

  val route =
    pathPrefix("tweets") {
      (post & entity(as[Tweet])) { tweet =>
        complete {
          Created -> Map("id" -> TweetDb.save(tweet)).toJson
        }
      } ~ 
      (get) {
        complete {
          OK -> Map("tweets" -> TweetDb.find()).toJson
        }
      } 
    }   
}

object TweetApi extends App with RestApi {
  implicit val system = ActorSystem("webapi")
  implicit val materializer = ActorMaterializer()
  implicit val execCtx = system.dispatcher

  val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)

  println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
  Console.readLine()

  bindingFuture.flatMap(_.unbind()).onComplete { _ => system.shutdown() }
}

Protocol

import spray.json.DefaultJsonProtocol

case class Tweet(author: String, body: String)

object TweetProtocol extends DefaultJsonProtocol {
  implicit val TweetFormat = jsonFormat2(Tweet.apply)
}

Pseudo-database

import scala.collection.mutable.Map

object TweetDb {
  private var tweets = Map[String, Tweet]()

  def save(tweet: Tweet) = {
    val id: String = java.util.UUID.randomUUID().toString()
    tweets += (id -> tweet)
    id
  }

  def find() = tweets

  def findById(id: String) = tweets.get(id)
}

Upvotes: 2

Views: 2089

Answers (1)

Qingwei
Qingwei

Reputation: 510

For your 1st error, try the suggestion from the comment, ie. import all from Directives

For second part

◾Cannot find JsonWriter or JsonFormat type class for scala.collection.immutable.Map[String,scala.collection.mutable.Map[String,Tweet]]

◾not enough arguments for method toJson: (implicit writer: spray.json.JsonWriter[scala.collection.immutable.Map[String,scala.collection> .mutable.Map[String,Tweet]]])spray.json.JsValue. Unspecified value parameter writer.

You need to define JsonFormat for Map[String, mutable.Map[String, Tweet]]

By creating an object in your TweetProtocol, extending RootJsonFormat eg.

type DBEntry = Map[String, mutable.Map[String, Tweet]]
object TweetProtocol extends DefaultJsonProtocol {
  implicit object DBEntryJsonFormat extends RootJsonFormat[DBEntry] {
    override def read(json: JSValue) { 
      // your implementation
    }
    override def write(dbEntry: DBEntry) {
      // implementation
    }
  }
}

Upvotes: 4

Related Questions