The CRAP Stack, Part 3 – Front-End Routes with a Play server

As I continue to develop my React app that is hosted on a Play backend

, I’ve come across the need to support “front-end routes”; that is, URLs that look like this:

Where there is no explicit entry for GET /foo/bar
in Play’s routes
and nor is there a physical asset located in /public/foo/bar
for the Assets controller to return to the client, as we set up in the last instalment


# Last of all, fall through to the React app
  GET /"/public",file="index.html")
  GET /*file"/public",file)

As it stands, the last line of routes
match, the Assets controller will fail
to find the resource, and your configured “client error handler” will be called to deal with the 404. This is not
what we want for a “front-end route”!

What we want
, is for requests that don’t correspond to a physical asset to be considered a request for a virtual asset – and hence given to the React app. And after a bit of fiddling around, I’ve come up with a FrontEndServingController
that gives me the most efficient possible way of dealing with this. The Gist is available
for your copy-paste-and-improve pleasure, but the key points are:

The fall-through cases at the bottom of routes


GET /       controllers.FrontEndServingController.index
  GET /*file  controllers.FrontEndServingController.frontEndPath(file)

Those methods in FrontEndServingController

just being:

val index = serve(indexFile)

  def frontEndPath(path: String) = serve(path)

  private def serve(path: String) = {
    if (physicalAssets.contains(path)) {
      logger.debug(s"Serving physical resource: '$path'"), path, true)
    } else {
      logger.debug(s"Serving virtual resource: '$path'")
      // It's some kind of "virtual resource" -
      // a front-end "route" most likely, indexFile, true)

And the “clever” bit being a recursive scan of the /public
directory when we start up, assembling a definitive (and immutable!) Set[String]

of what’s actually a physical asset path:

lazy val physicalAssets:Set[String] = {
    val startingDirectory = new File(physicalPublicDirectory)

  private def deepList(f: File): Set[String] = {
    val these = f.listFiles.toSet
    val inHere = these.filter(_.isFile).map { f =>
      f.getPath.replace(physicalPublicDirectory, "")
    val belowHere = these.filter(_.isDirectory).flatMap(deepList)
    inHere ++ belowHere
稿源:The Millhouse Group Blog (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 综合技术 » The CRAP Stack, Part 3 – Front-End Routes with a Play server

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录