Embeding Play using AkkaHttpServer and making conf/routes to be also available


(Francis Brosnan Blázquez) #1

Hello.
We attempting to integrate Play with AkkaHttpServer into our MQTT Akka implementation [1] as indicated here:

https://www.playframework.com/documentation/2.6.x/ScalaEmbeddingPlayAkkaHttp

In particular, we have used different combinations proposed by this page and all of them work, but we have detected we are not able to make play to also serve routing defined by our conf/routes.

We have found that we can extend the following code (provided at ScalaEmbeddingPlayAkkaHttp) to also serve static assets:

val _server = AkkaHttpServer.fromRouterWithComponents (serverConfig) { components =>
  import Results._
  import components.{ defaultActionBuilder => Action }
  {
    case GET(p"/hello/$to") => Action {
      Ok(views.html.index(to))
      /* Ok(s"Hello $to") */
    }
  }
}

…by using examples from https://github.com/lightbend/lightbend-markdown/blob/master/server/src/main/scala/com/lightbend/markdown/server/DocumentationServer.scala
(for example, using code like this):

  case GET(p"/$name/resources/$path*") if nameExists(name) => Action {
    sendFileInline(name, path).getOrElse(NotFound("Resource not found [" + path + "]"))
  }

However, support provided by conf/routes ( https://www.playframework.com/documentation/2.6.x/ScalaRouting ) is much more than that. It provides a lot of features (along with loading static assets).

In this context, we would like to be able embed Play Server with support for conf/routes defined.

Please, could you give us some advise how to do this?

Thanks for your time and patience.

[1] https://github.com/asples/reactive-myqtt


(Greg Methvin) #2

It should be possible to enable just the routes compiler plugin on your project, but it’s not documented anywhere I know of.

You’ll need to depend on the Play sbt plugin, so add to plugins.sbt:

addSbtPlugin("com.typesafe.play" %% "sbt-plugin" % "2.6.13")

Then add the RoutesCompiler plugin to your project, e.g.

lazy val root = (project in file(".")).enablePlugins(RoutesCompiler)

That won’t add any dependencies or other functionality from Play. It just adds the routes compiler task as a source generator for that project.

Then add sources for it to compile:

sources in (Compile, routes) ++= {
  val dirs = (unmanagedResourceDirectories in Compile).value
  (dirs * "routes").get ++ (dirs * "*.routes").get
}

That will set it up to compile any files named “routes” or ending in “.routes” in your unmanaged resource directories. So, for example, it would compile src/main/resources/routes or src/main/resources/com.example.foo.routes; in the former case the generated router class would be router.Routes and in the latter it would be com.example.foo.Routes.

Then you can construct your server like this:

object Example extends App {
  val components = new AkkaHttpServerComponents with BuiltInComponents {
    override lazy val serverConfig: ServerConfig = ServerConfig()
    override lazy val httpFilters = Nil
    override lazy val router = new Routes(httpErrorHandler, new MyController())
  }
  val server = components.server
}

You can mix traits into the components just like any other Play app using compile-time dependency injection.

Note that the static assets functionality is separate from Play’s routes compiler. The core play library comes with the Assets controller, which you can get by mixing in AssetsComponents. That serves resources from the /public directory on your classpath. You could use the assets controller from a SIRD router as well:

  case GET(p"/assets/$path*") => assets.at("/public", path)

I hope that helps. I think our documentation could definitely improved around how to use Play’s plugins individually without completely Play-ifying your project.


(Francis Brosnan Blázquez) #3

Hello @greg
Thanks for taking your time and all notes.
I’m going to check all this and let you know.
Best Regards


(Francis Brosnan Blázquez) #4

Hello @greg

Just keep you updated about this. I wasn’t able to make it work following these notes. After working with the problem and giving a try with different alternatives, I found I was fighting with different details that all led to the same conclusion: I wasn’t using Play in an usual/standard way.

For all folks that are not play-experts, this is what I did to solve the problem:

  1. Separate the logic (mostly POST) building a plugin using Akka-Http that is attached to the service (Akka) for which we wanted to give a REST API.

  2. Create a pure, standard play project that provides all the web functions (csrf protection, asset loading, webjars --nice-- and, when a POST to the REST API is received on the play project, using PlayWS, is routed/passed to the REST service (making it to work as a backend service for the play project).

Thanks for your notes and help.
Best Regards.


(Greg Methvin) #5

Not sure why you weren’t able to get it to work. Glad you found a setup that makes sense for you though.

For those interested in the answer to the original question, I created a small test project to show that it works: https://github.com/gmethvin/play-routes-standalone-example