1
votes

Google App Engine, index.html mis en cache après le déploiement dans l'application express / react

J'ai une application nodejs10 qui est déployée sur Google App Engine. Si un utilisateur a déjà utilisé l'application dans Chrome, l'application ne fonctionne pas lorsque l'application est redéployée. L'application est créée avec create-react-app et construite avec webpack afin que css et js soient renommés lors de la construction, mais l'index.html qui fait référence à ces fichiers ne l'est pas. Ainsi, lorsque l'utilisateur accède au site après le déploiement, il obtient l'index.html mis en cache qui fait référence à des fichiers js / css qui n'existent plus. Cela provoque à son tour une Uncaught syntax error: unexpected token '<'

Le seul moyen que j'ai trouvé pour que l'application fonctionne à nouveau est d'ouvrir l'inspecteur, de désactiver le cache et de recharger la page. Inutile de dire que ce n'est pas quelque chose que vous pouvez demander à l'utilisateur final de faire.

J'ai cherché une solution pour cela et trouvé des problèmes similaires mais pas exactement les mêmes, ou s'ils sont identiques, il n'y a pas de solution disponible.

Le code express sert index.html avec res.sendFile lorsque la requête n'est pas adressée à un fichier statique existant pour que le routeur React fonctionne correctement:

Accept-Ranges: bytes
Cache-Control: public, max-age=0
Content-Encoding: gzip
Content-Length: 1200
Content-Type: text/html; charset=UTF-8
Date: Thu, 03 Sep 2020 20:43:51 GMT
ETag: W/"967-49773873e8"
Last-Modified: Tue, 01 Jan 1980 00:00:01 GMT
Server: Google Frontend
Vary: Accept-Encoding
X-Cloud-Trace-Context: 7f98678c2ba7dc90ad204b67bec766df;o=1
X-Powered-By: Express

Le fait est que j'ai récemment déplacé l'application d'un compte Google Cloud personnel vers mon compte professionnel et je n'ai jamais eu ce problème auparavant. Donc, soit j'ai changé quelque chose pour provoquer cela, soit App Engine a changé.

Les en-têtes de réponse (code de réponse 304) ressemblent à ceci:

app.get('*', (req,res) =>{
    try {
        res.sendFile(path.join(public+'/index.html'))
    } catch(e) {
        res.status(404).send()
    }
});

Ainsi, comme vous pouvez le voir, la date de "dernière modification" sur le fichier est le 1er janvier 1980. Et j'ai vérifié que le fichier a effectivement cette date de dernière modification sur le moteur d'application. Mais dans le dossier de construction local qui est déployé, le fichier a une date actuelle. Je ne sais pas si c'est ce qui cause le problème.

Alors je suis un peu perdu. Je suppose que je pourrais pirater l'en-tête "last modified", mais cela ne me semble pas être une bonne solution. Et je crains également que le même comportement ne se produise sur d'autres fichiers qui causent des problèmes similaires. Quelqu'un d'autre a rencontré ce problème et a trouvé une solution?


3 commentaires

En effet, s'il s'agit d'un problème causé par le cache, vous ne pourrez pas le résoudre, car il n'est pas possible de vider le cache d'un navigateur côté client. En recherchant une telle erreur, j'ai pu découvrir qu'elle pourrait être liée au système de routage et à la configuration app.yaml de votre application. Compte tenu de cela, pourriez-vous jeter un coup d'œil à ce cas similaireici et vérifier si les modifications apportées à ces deux configurations vous aident à résoudre votre cas?


@gso_gabriel Bien que le problème lié donne le même message d'erreur, ce n'est pas le même problème. Dans mon cas, tout fonctionne bien si je vide le cache du navigateur. Je comprends qu'il n'est peut-être pas possible de vider le cache du client, mais au moins je pourrais empêcher le fichier d'être mis en cache pour les versions futures. Dans mon code déployé, il n'y a aucun problème à servir les fichiers css / js, c'est juste que l'index.html mis en cache essaie d'accéder aux mauvais fichiers car ils changent de nom lors de la reconstruction.


Salut @ChristofferHallqvist J'ai publié une réponse pour fournir une solution possible pour la suppression de la mise en cache qui pourrait vous aider. Au cas où cela vous aiderait, pensez à voter pour / l'accepter. :)


4 Réponses :


0
votes

Comme mentionné dans les commentaires, vous ne devrez pas vider le cache du navigateur côté client. Donc, ce ne serait pas une solution viable pour vous. Cependant, étant donné que tout fonctionne bien, mais uniquement le problème de la mise en cache, j'ai recherché et trouvé une alternative possible.

Il semble que ce référentiel ici , appelé nocache , soit un middleware pour désactiver la mise en cache et qui peut être utilisé dans Javascript. Il vous suffit de l'installer en exécutant npm install --save nocache , puis d'ajouter les lignes ci-dessous à votre code.

const nocache = require('nocache')
app.use(nocache())

C'est une solution que j'ai trouvée dans ce cas ici , où c'est une solution pour arrêter la mise en cache côté client.


4 commentaires

Eh bien, bien sûr, c'est une manière de la "résoudre", mais une manière plutôt brutale, et une manière qui ne devrait pas être nécessaire en premier lieu. Si le cache fonctionne correctement en premier lieu, un tel hack ne devrait pas être nécessaire. Je cherche une explication sur la raison pour laquelle ce problème est survenu en premier lieu, pourquoi la dernière date de modification est 1980, et si c'est prévu.


Salut @ChristofferHallqvist il semble que vous n'êtes pas le seul, car cet autre cas ici a la même situation que vous et comme mentionné par une autre personne, c'est en effet un problème de cache. Il comprend également une solution possible différente. Si vous pensez que ce cas est un problème créé par App Engine, vous pouvez le signaler ici .


Merci, il semble certainement que le problème soit causé par App Engine.Je suis donc le problème signalé sur l'affaire et j'espère que cela conduira à une solution appropriée, plutôt que de casser le cache lorsque vous ne devriez pas avoir à le faire.


Je rencontre également le même problème et je ne trouve aucune solution. J'essaye les choses décrites ici. @ChristofferHallqvist, l'avez-vous résolu?



0
votes

Je soupçonne que vous appelez également app.use(express.static()) avec le répertoire contenant index.html . Déplacez vos fichiers de modèle dans un répertoire distinct de votre contenu statique.


0 commentaires

1
votes

J'ai eu exactement le même problème avec les horodatages étranges.

Cela s'avère être un comportement par conception, car tous les horodatages sont rendus nuls lors du déploiement par le moteur d'application. voir: https://issuetracker.google.com/issues/168399701

Je l'ai résolu en express comme ceci:

app.get("/*", async (req, res) => {
  return res.sendFile("index.html", { root, lastModified: false, etag: false });
});

Ou pour les fichiers individuels:

app.use("/", express.static(root, { etag: false, lastModified: false }));


0 commentaires

0
votes

gens.

Après avoir lutté, j'ai trouvé la bonne solution.

Lors de la diffusion de fichiers statiques depuis express, les middlewares normaux ne s'appliquent pas. Vous devez utiliser la fonction personnalisée setHeaders de express.static.

  • D'un côté, nous servons index.htmnl lors de l'accès à la route «/». Cela utilise des en-têtes express normaux
  • De l'autre côté, index.html est demandé via la diffusion statique. Cela n'utilise PAS les autres en-têtes.

Nous devons donc couvrir les deux cas.

Voici le code:

app.use(express.static('build', { etag: false, lastModified: false, setHeaders: (res, path) => {
  // No cache for index html otherwhise there's gonna be problems loading the scripts
  if (path.indexOf('index.html') !== -1) {
    res.set('Cache-Control', 'no-store')
  }
} }));


app.set('etag', false)

app.disable('x-powered-by');

app.use((req, res, next) => {
  // hide x-powered-by for security reasons
  res.set( 'X-Powered-By', 'some server' );
  // This should apply to other routes
  res.set('Cache-Control', 'no-store')
  next()
})

app.get('/', (req,res) =>{
  // I think this is redundant
  res.set('Cache-Control', 'no-store')
  res.sendFile(path.join(__dirname+'/build/index.html'), { etag: false, lastModified: false });
});

app.get('*', (req,res) =>{
  // I think this is redundant
  res.set('Cache-Control', 'no-store')
  res.sendFile(path.join(__dirname+'/build/index.html'), { etag: false, lastModified: false });
});

const port = process.env.PORT || 8080
app.listen(port, () => console.log('App listening on ' + port));


0 commentaires