Jouer au Framework 2.2.x :L'emplacement des actifs statiques ne fonctionne pas en production
-
21-12-2019 - |
Question
Difficulté à accéder à l’emplacement des actifs compilés en production.
Ma stratégie a été de servir mes actifs dans "app/assets/ui" en développement et "public" en production, comme indiqué ci-dessous dans mon fichier conf/routes.
#{if(play.Play.mode.isDev())}
GET /assets/*file controllers.common.Assets.at(path="/app/assets/ui", file)
#{/}
#{else}
GET /assets/*file controllers.common.Assets.at(path="/public", file)
#{/}
Depuis que j'ai défini les mappages d'actifs en dehors de « public », j'ai ajouté la ligne suivante dans mon Build.scala
playAssetsDirectories <+= baseDirectory / "app/assets/ui"
A titre d'exemple, mes scripts sont chargés de manière conditionnelle en fonction de l'environnement, comme indiqué ci-dessous
@if(play.Play.isDev()) {<script src="@routes.Assets.at("/app/assets/ui", "javascripts/application.js")"type="text/javascript"></script>} else {<script src="@.routes.Assets.at("/public", "javascripts/application.min.js")" type="text/javascript"></script>}
J'utilise Grunt pour mon flux de travail frontal et lorsque l'application est créée, elle copie les fichiers de distribution dans le dossier public de l'application.Je démarre l'application en production en utilisant "sbt clean compile stage"
puis exécutez l'application packagée.
Mon problème semble que les routes font toujours référence au dossier "app/assets/ui" au lieu du dossier "public" de distribution.
Des conseils sur la façon dont je peux déboguer cela ?Mon expérience professionnelle est celle de développeur front-end, je suis donc très nouveau sur Play !et Scala.
La solution
Comme mentionné par @estmatic, votre conditionnel dans routes
ne sera pas évalué.
Comme il est généralement extrêmement utile de consolider les différences entre les applications Mode
s dans les fichiers, je vous suggère d'étendre GlobalSettings
(si ce n'est pas déjà fait) et remplacez le onLoadConfig
méthode:
class Settings extends GlobalSettings {
override def onLoadConfig(config: Configuration, path: File, classloader: ClassLoader, mode: Mode.Mode): Configuration = {
val specificConfig:Config = // ... Use the mode param to load appropriate file
super.onLoadConfig(specificConfig, path, classloader, mode)
}
...
}
Vous pourriez alors avoir des fichiers nommés de manière appropriée (dev.conf
et production.conf
me viennent à l'esprit) qui contiennent des valeurs appropriées, l'une d'entre elles étant la base path
pour que le contrôleur d'actifs puisse l'utiliser.
EDIT s'avère que le faire de cette façon permet d'utiliser routes
gênant, voici une autre approche :
Cette approche n'utilise pas de fichier de configuration par environnement, ce qui signifie que si quelque chose change dans la configuration du frontend (par ex.il n'est plus servi de /public
) vous devrez modifier ce code et le redéployer.Cependant, il s'intègre assez bien dans Play 2.x :
package controllers
object EnvironmentSpecificAssets extends AssetsBuilder {
val modeToAssetsPathMap = Map(
Mode.Dev -> "/app/assets/ui",
Mode.Prod -> "/public")
lazy val modePath = modeToAssetsPathMap(Play.current.mode)
/** New single-argument `at`, determines its path from the current app mode */
def at(file:String): Action[AnyContent] = at(modePath, file)
}
Le code est assez explicite, le seul "truc" est probablement le lazy val
ce qui signifie que nous n'avons qu'à évaluer le mode de fonctionnement actuel et à effectuer la recherche sur la carte une seule fois.
Maintenant votre routes
le fichier ressemble à ceci :
GET /assets/*file controllers.EnvironmentSpecificAssets.at(file)
Autres conseils
Playframework 2.x ne prend pas en charge les instructions conditionnelles dans le fichier routes.Les versions 1.x l'avaient mais il a été supprimé.
Ce que vous avez dans votre fichier de routes, ce sont simplement deux routes avec le même modèle d'URI, /assets/file*
.Les autres lignes sont simplement ignorées en tant que commentaires puisqu'elles commencent par le caractère dièse, #
.Je pense que puisque le modèle est le même pour les deux, le premier itinéraire attrape tout et le second ne fait rien.
Ce n'est pas exactement ce que vous essayez de faire, mais je pense que vous pouvez simplement rendre les modèles d'itinéraire un peu différents et cela devrait fonctionner.
GET /assets/dev/*file controllers.common.Assets.at(path="/app/assets/ui", file)
GET /assets/*file controllers.common.Assets.at(path="/public", file)