Next.js: La barre oblique de fin dans le lien pour la page légitime fonctionne pour la navigation côté client mais conduit à un bundle introuvable et 404 lors d'une actualisation matérielle (ssr)

Créé le 20 sept. 2018  ·  119Commentaires  ·  Source: vercel/next.js

La barre oblique de fin dans le lien pour la page légitime fonctionne pour la navigation côté client mais conduit à un bundle introuvable et 404 lors d'une actualisation matérielle (ssr)

Rapport d'erreur

Décrivez le bogue

faites-moi savoir si le titre a besoin de plus de précisions.

tous les problèmes pertinents ont été

Je réécris mon blog existant en next.js et j'utilisais auparavant des barres obliques. Les derniers serve peuvent m'aider une fois que j'ai créé mon blog propulsé par next.js. Mais pour corriger dev env, je dois soit me débarrasser des barres obliques finales et utiliser 301 Moved Permanently dans prod; ou vivre avec une prise en charge de la barre oblique interrompue dans dev.

Reproduire

Voici un cas reproductible minimal (le lien vers le référentiel repro se trouve sous l'extrait):

// pages/index.js
import Link from "next/link";

export default () => (
  <Link href="/about/">
    <a>About</a>
  </Link>
);

// pages/index.js
export default () => "about";

Repo reproductible minimal https://github.com/iamstarkov/next.js-trailing-slash-bug-demo

  1. cloner le dépôt git clone https://github.com/iamstarkov/next.js-trailing-slash-bug-demo
  2. changer de répertoire cd next.js-trailing-slash-bug-demo
  3. installer les deps yarn
  4. exécuter dev : yarn dev
  5. ouvrir http://localhost :3000/
  6. ouvrir l'onglet réseau de devtools
  7. observer http://localhost:3000/_next/static/development/pages/about.js être 200ed
  8. observer http://localhost:3000/_next/on-demand-entries-ping?page=/about/ être 200ed
  9. observer http://localhost:3000/about/ être 404ed
  10. observer des tentatives persistantes pour résoudre http://localhost:3000/about/
  11. observer dans le terminal Client pings, but there's no entry for page: /about/
  12. actualiser la page
  13. observer la page 404.
  14. supprimez la barre oblique de fin dans l'URL ou cliquez sur http://localhost :3000/about
  15. observer la page en cours de 200ed
  16. pour garantir la persistance des erreurs, répétez les étapes 5 à 15 une fois.

Comportement prévisible

  1. /about/ ne devrait pas être résolu en tant que 404 not found
  2. /about/ doit être résolu comme 200 ok
  3. Le serveur ne doit pas imprimer Client pings, but there's no entry for page: /about/
  4. /about et /about/ devraient fonctionner de la même manière

Captures d'écran

N / A

Informations système

  • Système d'exploitation : macOS High Sierra 10.13.6 (17G65)
  • Navigateur (ne devrait pas avoir d'importance, mais peut être reproduit dans Chrome 69.0.3497.100 et Safari Version 12.0 (13606.2.11) (était le même pour Safari 11)
  • Version de Next.js : 7.0.0 (pourrait repro sur 5.x et 6.x)

Contexte supplémentaire

Ajoutez ici tout autre contexte concernant le problème.

Si vous modifiez ce code dans https://github.com/zeit/next.js/blob/459c1c13d054b37442126889077b7056269eeb35/server/on-demand-entry-handler.js#L242 -L249

ou node_modules/next/dist/server/on-demand-entry-handler.js localement

          const { query } = parse(req.url, true)
          const page = normalizePage(query.page)
+         console.log('query.page', query.page);
+         console.log('page', page);
+         console.log('Object.keys(entries)', Object.keys(entries));
          const entryInfo = entries[page]

          // If there's no entry.
          // Then it seems like an weird issue.
          if (!entryInfo) {
            const message = `Client pings, but there's no entry for page: ${page}`

et redémarrez next dev et ouvrez http://localhost :3000/ et cliquez sur le lien puis :

  • pour /about
    query.page /about page /about Object.keys(entries) [ '/', '/about' ]
  • pour /about/ :
    query.page /about/ page /about/ Object.keys(entries) [ '/', '/about' ] Client pings, but there's no entry for page: /about/

Je pense que le problème (au moins en partie) réside dans l'incapacité du middleware de onDemandEntryHandler à trouver la page dans les entrées si la page a une barre oblique de fin.

J'espère que mes 2 heures d'enquête et de préparation pourront aider à résoudre ce problème.

story 8 feature request

Commentaire le plus utile

Nous sommes sur le point de débarquer une fonctionnalité corrigeant cela, dans un jour ou deux !

Tous les 119 commentaires

les problèmes les plus pertinents et les plus notables sont #1189 et #3876

J'ai hâte que cela soit enfin résolu ! @timneutkens Quel est le statut des problèmes de barre oblique de fin pour Next 7 ?

@NathanielHill je pourrais le reproduire sur next@7

J'utilise nextjs 7 et la barre oblique finale produit un 404 pour moi à la fois sur dev et prod :

  • au chargement initial de la page
  • à l'actualisation de la page

Et affecte :

  • un lien externe
  • un lien interne
  • URL collée dans le navigateur

La simple suppression de la barre oblique de fin résout le problème.

Les barres obliques de fin sont souvent ajoutées par les navigateurs, les serveurs et/ou d'autres services où des liens peuvent être collés. Ainsi, même si je peux contrôler les liens internes, il est difficile de contrôler à quels liens les utilisateurs externes peuvent arriver.

Je vois également ce problème dans la version 7. Je ne sais pas si cela est pertinent, mais j'attribue un alias à un projet Next.js à un sous-dossier d'un autre déploiement Now. Notre URL de base est donc primer.style et nous créons primer-components.now.sh alias pour notre application primer.style/components . En production, la page d'index de primer.style/components fonctionne bien, mais primer.style/components/ produit un 404.

J'ai dû chercher un peu pour trouver ce problème. J'utilise des déploiements statiques sur Netlify, donc ce n'est pas un problème sur prod, mais sur le développement (Next 7), la compilation se bloque simplement s'il y avait une barre oblique à la fin, et il était difficile de comprendre pourquoi. Je ne pense pas que cela (ne gère pas la barre oblique dans l'environnement de développement) soit un bon DX.

J'ai aussi ce problème et c'est vraiment ennuyeux, j'espère qu'il sera bientôt résolu.

Si vous voulez une barre oblique de fin, vous pouvez simplement le faire. <Link href='/about' as='/about/'><a>about</a></Link> mais si vous utilisez les vendredis/prochains itinéraires, ce n'est pas possible. J'ai donc un fork où vous pouvez ajouter trailingSlash comme accessoire. J'espère que cela t'aides

Si vous voulez une barre oblique de fin, vous pouvez simplement le faire. <Link href='/about' as='/about/'><a>about</a></Link> mais si vous utilisez les vendredis/prochains itinéraires, ce n'est pas possible. J'ai donc un fork où vous pouvez ajouter trailingSlash comme accessoire. J'espère que cela t'aides

@aluminick Je suis désolé, je viens d'essayer et cela ne fonctionne pas pour moi. J'arrive toujours à la page traling-slashed (dernière version), qui n'est pas trouvée après l'actualisation (comportement actuel).

aussi ni #6664 ni #6752 n'aident avec ceux-ci, car experimental.exportTrailingSlash n'aide pas car c'est pour next export seulement, je crois

il y a eu une pull request prometteuse #6421 de @Janpot qui n'a malheureusement pas

@iamstarkov Quel est le statut de ce problème ? Des solutions à côté du crochet server.js ?

@dryleaf status : il est toujours ouvert

Un problème similaire ... rediriger lorsque plusieurs barres obliques sont ajoutées. Exemple : https://github.com/zeit/next.js////////////issues/5214

Les URL GitHub ne sont pas pertinentes

@iamstarkov Je ne sais pas ce que vous voulez dire. Mais après avoir relu mon message d'origine, il semble que j'aurais pu être plus clair.

L'url GitHub est censée être une simple démonstration de la façon dont les URL devraient (de préférence) fonctionner lorsqu'une application est créée avec Next.js. En d'autres termes, si un utilisateur ajoute une barre oblique supplémentaire, l'URL devrait toujours fonctionner.

Une mise à jour pour nextjs 9 ?

Je suis nouveau sur Next, mais quelle est la solution de contournement que vous utilisez pour ce problème ?

@iamstarkov Quel est le statut de ce problème ?

Je suis choqué que ce problème ne soit pas résolu depuis environ un an !
L'équipe Next.js a-t-elle besoin d'autres raisons pour commencer à résoudre ce problème ?

Les URL devraient fonctionner indépendamment de la barre oblique de fin. Vérifiez n'importe quel site sur le Web.

Si cela dépasse le cadre de Next.js, donnez-nous la possibilité de le configurer dans Now.
Je suis vraiment confus que l'équipe de Zeit ignore des problèmes aussi critiques pendant des années.

@exentrich Ceci est facilement configurable dans Zeit Now en redirigeant simplement 301 toutes les barres obliques de fin vers le même itinéraire sans barres obliques :

now.json :

"routes": [
    {
      "src": "/(.*)/",
      "status": 301,
      "headers": { "Location": "/$1" }
    },
    ...
]

Cependant, je ne comprends pas non plus pourquoi cela n'est pas géré par Next.js lui-même et pourquoi l'équipe a ignoré ce problème.

Ceci, ainsi qu'un public/ (en préparation) sont les principaux problèmes auxquels je vois les convertis de l'ARC se heurter.

@rauchg

@NathanielHill merci !
J'ai essayé cette solution, mais les paramètres de requête sont supprimés. Par exemple, /some/?query=1 redirigera vers /some sans requête. Savez-vous comment y remédier ?

Ouais, ça a l'air d'être un problème @exentrich

Je n'aurais pas deviné ce comportement car on m'a dit qu'il y avait un ^ et un $ implicites enroulés autour de l'expression régulière (ce qui signifie que votre exemple ne correspondrait pas). Peut-être qu'il y a un moyen d'accéder à la chaîne de requête par elle-même pour la rajouter :man_shruging: Bonne chance

Essayer de le faire fonctionner à l'aide d'un serveur express personnalisé et d'avinoamr/connect-slashes mais semble rencontrer le même problème

C'est certainement un problème énorme, en particulier parce que les routes / génèrent des pages d'erreur et cela nuit au référencement (qui est l'un des principaux attraits de Next).

Les redirections 301 et les serveurs express personnalisés semblent tous être des hacks plutôt que des correctifs. Dans mon cas, j'ai une application entièrement fonctionnelle construite sur Next sans serveur Express personnalisé - tout le reste fonctionne parfaitement, mais maintenant je dois créer un nouveau serveur Express uniquement à cause du problème de barre oblique finale. L'effort requis semble être disproportionné étant donné qu'il s'agit d'un hack. J'adorerais si cela pouvait être augmenté en priorité! Pour cette raison, j'entends des grognements dans mon équipe au sujet d'avoir utilisé Next plutôt que quelque chose comme vanille React/Angular et cela affaiblit certainement le cas de Next.

PS : j'adore travailler avec Next ❤️

C'est certainement un problème énorme, surtout parce que les routes / génèrent des pages d'erreur et cela nuit au référencement

Cela ne nuit pas à votre référencement. Google traite la barre oblique finale comme une page différente. L'avoir 404 n'a pas plus d'impact sur le référencement que toute autre page non existante de votre site. En outre, tant que vous n'y mettez jamais de lien avec une barre oblique à la fin, Google n'essaiera pas de l'explorer en premier lieu. Ce problème, tout en étant un problème valable, est beaucoup moins critique que vous ne le prétendez tous.

@nik-john @NathanielHill @dkrish @exentrich

Vous ne devriez pas avoir à utiliser un serveur Express pour effectuer une redirection 301. Cela dépend de vos besoins, mais j'ai pu répondre aux miens avec un server.js .

Une redirection 301 est également la meilleure solution pour le référencement, car vous n'obtiendrez pas de pénalités de contenu en double pour la route slash et non-slash.

J'adore ❤️ Next.js, mais je vote pour que cela soit géré sans ce travail de contournement.

// server.js

const { createServer } = require('http');
const { parse } = require("url");
const next = require("next");

const dev = process.env.NODE_ENV !== 'production'
const port = parseInt(process.env.PORT, 10) || 3000;
const app = next({ dev, quiet: false });
const handle = app.getRequestHandler();

(async () => {
    await app.prepare();
    const server = createServer();

    server.on('request', async (req, res) => {

        const parsedUrl = parse(req.url, true);
        const { pathname, query } = parsedUrl;

        if (pathname.length > 1 && pathname.slice(-1) === "/") {
            console.log('server.js - redirect on "/"...', pathname, query);
            const queryString = await Object.keys(query).map(key => key + '=' + query[key]).join('&');
            res.writeHead(301, { Location: pathname.slice(0, -1) + (queryString ? '?'+ queryString : '') });
            res.end();
        }

        handle(req, res, parsedUrl);

    });

    await server.listen(port);
    console.log(`🚀 Ready on http://localhost:${port}`);

})();

@Janpot

Cela ne nuit pas à votre référencement. Google traite la barre oblique finale comme une page différente. L'avoir 404 n'a pas plus d'impact sur le référencement que toute autre page non existante de votre site.

Je comprends que cela ne nuit pas particulièrement au référencement de manière innée. Mais cela exerce une pression supplémentaire sur les développeurs pour obtenir les définitions d'URL correctes à chaque fois, ce qui est sujet à des erreurs humaines. Un développeur qui est nouveau sur Next ne saura pas nécessairement que l'URL suivante (parfaitement normale) mènera à une page 404. <Link href='/people/'>

Idéalement, un framework mature ne devrait pas être soumis à de telles erreurs humaines.

En outre, tant que vous n'y mettez jamais de lien avec une barre oblique à la fin, Google n'essaiera pas de l'explorer en premier lieu.

Encore une fois, il existe le problème des personnes qui se connectent accidentellement à _ www.monsite.com/personnes_ (les deux semblent être exactement les mêmes pour les utilisateurs - même la plupart des développeurs).

Ces deux scénarios _peuvent_ affecter le référencement.

Maintenant, sans tenir compte de l'impact sur le référencement, il y a aussi la signification sémantique de l'URL - vers quoi _fait_ _ www.monsite.com/people / _ ? Idéalement, parce qu'il pointe vers un répertoire, Next devrait renvoyer tout ce qui se trouve dans pages > people > index.js (par opposition à pages > people.js pour _www.mysite.com/people_) mais à la place il ne renvoie rien, ce qui est très défaut de haut niveau dans le fonctionnement du routage.

Les principales bibliothèques de routage ont déjà des dispositions pour cela - comme isExact dans le cas de React Router

Bien que je comprenne d'où vous venez, je pense toujours que c'est un problème flagrant qui doit être corrigé

Ceci est également totalement inévitable dans le cas de next export

il existe le problème des personnes qui se connectent accidentellement...

Il existe le problème des personnes qui se connectent accidentellement à une URL inexistante, pourquoi /some/path/ serait-il moins inexistant que /some/path/dhgfiuwo ?

il y a aussi la signification sémantique de l'URL

C'est très subjectif, pour autant que je sache, il n'y a aucune spécification qui dicte quelle est la différence sémantique. Selon la spécification d' URL , avec et sans barre oblique finale sont considérées comme des URL différentes. Je peux penser à au moins 7 comportements valides différents :

  • avec et sans contenu complètement différent
  • avec fait 404, sans résolution
  • avec résout, sans fait 404
  • avec des redirections vers sans
  • sans redirection vers avec
  • avec et sans ont le même contenu avec pointage canonique vers avec
  • avec et sans ont le même contenu avec pointage canonique vers sans

Associez cela à la possibilité d'avoir soit /pages/some-page.js et /pages/some-page/index.js (ou les deux).

Next.js devrait-il prendre en charge tous ces cas d'utilisation ? Doit-il choisir un comportement par défaut ?

Je ne suis pas contre cela, mais après avoir essayé de l'implémenter auparavant, je pense simplement qu'il y a plus de nuances qu'il n'y paraît au départ.

Il existe le problème des personnes qui se connectent accidentellement à une URL inexistante, pourquoi/some/path/ serait-il moins inexistant que /some/path/dhgfiuwo ?

Pour le cas /some/path/dhgfiuwo - les gens s'attendent à ce que l'itinéraire dhgfiuwo soit manquant. (Par exemple, l'utilisateur dhgfiuwo est introuvable dans le système et la méthode users/dhgfiuwo est erronée. L'absence d'un utilisateur dans le système est une occurrence attendue.)
Pour le cas /some/path/ - les gens s'attendent à ce que ce chemin soit le même que /some/path , car c'est le comportement par défaut sur d'autres sites.
Par conséquent, un échec dans would/some/path/ est moins inexistant que /some/path/dhgfiuwo .

Je vois que d'autres ont posté leurs solutions, alors je voulais partager mon approche : https://github.com/DevSpeak/next-trailingslash

Certaines améliorations et la prise en charge des pages routées dynamiquement en ce qui concerne ?= devraient être effectuées IMO, mais cela ne sert qu'à montrer l'idée.

Pour une solution rapide, vous pouvez remplacer la page _error par défaut (comme dans l'exemple de @DevSpeak ).

@DevSpeak , je recommanderais quelques modifications pour votre dépôt :

  • Évitez les redirections 301 – elles sont mises en cache en permanence par les navigateurs et peuvent vous causer beaucoup de douleur. Dans la plupart des cas, tout ce dont vous avez besoin est un 302.
  • Votre ternaire errorCode peut être mis à
  • Ceci est uniquement côté serveur, vous pouvez donc l'envelopper avec if (typeof window === 'undefined') { ... } pour le secouer à partir du bundle client

Voici ce que j'utilise dans un projet Typescript (basé sur la page d'erreur intégrée) :

/pages/_error.tsx (ou supprimez les types TypeScript et nommez-le /pages/_error.jsx ):

import React from 'react';
import Head from 'next/head';
import { NextPageContext } from 'next';

const statusCodes: { [code: number]: string } = {
  400: 'Bad Request',
  404: 'This page could not be found',
  405: 'Method Not Allowed',
  500: 'Internal Server Error'
};

export type ErrorProps = {
  statusCode: number;
  title?: string;
};

/**
 * `Error` component used for handling errors.
 */
export default class Error<P = {}> extends React.Component<P & ErrorProps> {
  static displayName = 'ErrorPage';

  static getInitialProps({
    req,
    res,
    err
  }: NextPageContext): Promise<ErrorProps> | ErrorProps {
    const statusCode =
      res && res.statusCode ? res.statusCode : err ? err.statusCode! : 404;
    if (typeof window === 'undefined') {
      /**
       * Workaround for: https://github.com/zeit/next.js/issues/8913#issuecomment-537632531
       * Test vectors:
       * `/test/test/` -> `/test/test`
       * `/test/////test////` -> `/test/test`
       * `/test//test//?a=1&b=2` -> `/test?a=1&b=2`
       * `/test///#test` -> `/test#test`
       */
      const correctPath = (invalidPath: string) =>
        invalidPath
          .replace(/\/+$/, '')
          .replace(/\/+#/, '#')
          .replace(/\/+\?/, '?')
          .replace(/\/+/g, '/');
      if (req && res && req.url && correctPath(req.url) !== req.url) {
        res.writeHead(302, {
          Location: correctPath(req.url)
        });
        res.end();
      }
      const reqInfo = req
        ? `; Url: ${req.url}; IP: ${req.headers['x-forwarded-for'] ||
            (req.connection && req.connection.remoteAddress)};`
        : '';
      console.log(`Error rendered: ${statusCode}${reqInfo}`);
    }
    return { statusCode };
  }

  render() {
    const { statusCode } = this.props;
    const title =
      this.props.title ||
      statusCodes[statusCode] ||
      'An unexpected error has occurred';

    return (
      <div style={styles.error}>
        <Head>
          <title>
            {statusCode}: {title}
          </title>
        </Head>
        <div>
          <style dangerouslySetInnerHTML={{ __html: 'body { margin: 0 }' }} />
          {statusCode ? <h1 style={styles.h1}>{statusCode}</h1> : null}
          <div style={styles.desc}>
            <h2 style={styles.h2}>{title}.</h2>
          </div>
        </div>
      </div>
    );
  }
}

const styles: { [k: string]: React.CSSProperties } = {
  error: {
    color: '#000',
    background: '#fff',
    fontFamily:
      '-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif',
    height: '100vh',
    textAlign: 'center',
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center'
  },

  desc: {
    display: 'inline-block',
    textAlign: 'left',
    lineHeight: '49px',
    height: '49px',
    verticalAlign: 'middle'
  },

  h1: {
    display: 'inline-block',
    borderRight: '1px solid rgba(0, 0, 0,.3)',
    margin: 0,
    marginRight: '20px',
    padding: '10px 23px 10px 0',
    fontSize: '24px',
    fontWeight: 500,
    verticalAlign: 'top'
  },

  h2: {
    fontSize: '14px',
    fontWeight: 'normal',
    lineHeight: 'inherit',
    margin: 0,
    padding: 0
  }
};

Notez que cela enregistre également une erreur lorsque la page est visitée, vous pouvez donc vérifier vos journaux pour corriger les liens/autres problèmes.

@DevSpeak @bitjson Merci pour vos suggestions. C'est certainement une façon de procéder et cela résout certainement très bien le problème. Mais étant donné que le _error.jsx est à l'origine destiné à gérer les _erreurs_ et non la logique de routage interne, à mon avis, avoir tout ce code là est bidon et assez déclaratif. Attendre que chaque utilisateur fasse cela dans chaque base de code ne devrait pas être une exigence - cela devrait sortir de la boîte. = Je suis d'avis que cette condition doit être intégrée à la logique de routage, avec une option de désactivation comme React Router.

@NathanielHill

Ceci est également totalement inévitable dans le cas d'une prochaine exportation

Attendez - j'ai compris en lisant la documentation qu'il existe un code spécifique pour gérer la condition de barre oblique finale :

Les pages seront exportées sous forme de fichiers html, c'est-à-dire que /about deviendra /about.html.

Il est possible de configurer Next.js pour exporter les pages en tant que fichiers index.html et nécessiter des barres obliques de fin, c'est-à-dire que /about devient /about/index.html et est routable via /about/. C'était le comportement par défaut avant Next.js 9. Vous pouvez utiliser le next.config.js suivant pour revenir à ce comportement :

// next.config.js
module.exports = {
  exportTrailingSlash: true,
}

Même si ce n'est pas vraiment une option pour l'exportation HTML statique via next export , je ne suis pas d'accord avec la logique selon laquelle, simplement parce que Next prend en charge cette fonctionnalité (incroyable), d'autres modes doivent en souffrir (je ne connaître les statistiques d'utilisation, mais je suppose que plus de gens utilisent le mode avec serveur normal par opposition au mode sans serveur), en particulier lorsque cela est connu pour être un cas d'utilisation si courant

Pour info : il y a une RFC qui pourrait vous intéresser https://github.com/zeit/next.js/issues/9081

// next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: "/:path*/",
        destination: "/:path",
        statusCode: 301
      }
    ];
  }
};

@Janpot Love it - cela nous amènera à mi-chemin, c'est-à-dire avoir une sorte de support pour les redirections sans avoir à créer un serveur personnalisé. Cela va toujours être impératif car pour chaque route ajoutée par l'utilisateur, il devrait configurer une redirection dans le next.config.js - ou peut-être pourrions-nous simplement utiliser une expression régulière pour @bitjson mentionné :

          .replace(/\/+$/, '')
          .replace(/\/+#/, '#')
          .replace(/\/+\?/, '?')
          .replace(/\/+/g, '/')

Dans les deux cas, si l'équipe principale donne la priorité à cette RFC, je vous recommande fortement d'aller plus loin et d'en faire une _config_ intégrée que l'on peut _désactiver_ comme telle

// next.config.js
module.exports = {
  ignoreStrictRoutes: false, // default value: true
};

Dans l'ensemble, je pense que c'est un grand pas en avant - de bonnes choses @Timer !! ??

@nik-john Le chemin que j'ai spécifié dans "/:path*/" devrait tout attraper ( :path attrape un seul segment, * fait attraper 0 à n instances.)

@Janpot Ah mon mauvais Je suppose que nous devrions également prendre en compte tous les paramètres de requête de fin dans cette regex

De plus, je maintiens toujours la deuxième partie:

Dans les deux cas, si l'équipe principale donne la priorité à cette RFC, je vous recommande fortement d'aller plus loin et d'en faire une configuration intégrée que l'on peut désactiver comme telle.

// next.config.js
module.exports = {
  ignoreStrictRoutes: false, // default value: true
};

Si vous utilisez un serveur personnalisé et que vous souhaitez ignorer les routes strictes, vous pouvez également utiliser un gestionnaire de route personnalisé au lieu de faire une redirection.

app.render(req, res, urlWithoutTrailingSlash, query);

De cette façon, nous pouvons prendre en charge à la fois /path et /path/ et résoudre sur la même page.

Les fournisseurs de fédération Oauth nécessitent souvent des barres obliques de fin, ce comportement rend donc un flux simple très compliqué. Quel est le défi technique dans la mise en œuvre de ce comportement ? Ou est-ce une décision de conception de la prochaine?

Je ne l'ai pas vu mentionné jusqu'à présent dans ce fil, mais je ne rencontre pas ce problème après le déploiement avec Now, je ne le rencontre que localement lors des tests avec now dev .

const removeTrailingSlashes = (req, res, expressNext) => {
  if (req.path.substr(-1) === '/' && req.path.length > 1) {
    const query = req.url.slice(req.path.length);
    res.redirect(301, req.path.slice(0, -1) + query);
  } else {
    expressNext();
  }
};

obtenu cela de stackoverflow et a parfaitement fonctionné. cette solution fonctionne avec express.

@GaneshKathar, je ne vois pas comment cela fonctionnera si vous tenez compte de Next.js sans utiliser express

Je pense que nous ne pouvons pas nous entendre là-dessus et cela devrait être configurable.

En fait, je veux toujours la barre oblique de fin, les URL relatives sont plus faciles à raisonner lorsque toutes les pages se terminent par une barre oblique de fin.

Par exemple, cela n'a aucun sens que /about/index.tsx soit /about au lieu de /about/ , mais c'est compréhensible maintenant que next attend sans barre oblique. Si toutes les pages devaient se terminer par une barre oblique, cela permettrait aux pages de contenir des sous-pages à l'avenir, ce qui, je pense, est une manière plus extensible pour les pages.

Faire des liens relatifs dans le fichier /about/index.tsx est maintenant fastidieux. Si vous créez un lien ./mysubpage/ il pointe plutôt vers la racine du site. Cela rend les sous-pages non renommables. Je ne peux pas créer un répertoire /about/ plein de pages que je peux simplement renommer, car je devrais aussi modifier les liens relatifs.

De plus, le site wget -r produit des résultats raisonnables avec toujours des barres obliques à la fin, produisant des fichiers index.html.

Cependant, la modification de ce paramètre est un changement radical car tous les sites s'attendent à des barres obliques non finales, il doit donc être configurable.

J'utilise la version 9 et ce problème n'est toujours pas résolu

J'ai pu le faire fonctionner en utilisant quelque chose comme ce qui suit sur mon next.config.js :

exportPathMap: async function() {
  const paths = {
    '/': { page: '/' },
    '/authors/index.html': { page: '/authors' },
  };

  return paths;
},

L'accès à /authors donne 302 pointant location à /authors/ . Je teste avec http-serve , je ne sais pas si ce comportement est spécifique au serveur.

quand j'ai rencontré ce problème, j'ai trouvé cette solution

dans ma page _error.js

Error.getInitialProps = ({ res, err, asPath }) => {
    const statusCode = res ? res.statusCode : err ? err.statusCode : 404;

    const checkForTrailingSlashes = () => {
        if (asPath.match(/\/$/)) { // check if the path ends with trailing slash
            const withoutTrailingSlash = asPath.substr(0, asPath.length - 1);
            if (res) {
                res.writeHead(302, {
                    Location: withoutTrailingSlash
                })
                res.end()
            } else {
                Router.push(withoutTrailingSlash)
            }
        }
    }

    if (statusCode && statusCode === 404) {
        checkForTrailingSlashes();
    } else {
        // 
    }
    return { statusCode };
}

est-ce un bon moyen de surmonter le problème ?

Que dis-tu de ça?

pages/_app.jsx

```importer React depuis 'react';
importer l'application depuis « suivant/application » ;

exporter la classe par défaut MyApp étend App {
rendu() {
const { Composant, pageProps, routeur : { asPath } } = this.props ;

// Next.js currently does not allow trailing slash in a route.
// This is a client side redirect in case trailing slash occurs.
if (asPath.length > 1 && asPath.endsWith('/')) {
  const urlWithoutEndingSlash = asPath.replace(/\/*$/gim, '');

  if (typeof window !== 'undefined') {
    window.location.replace(urlWithoutEndingSlash);
  }
  return null;
}

return <Component {...pageProps} />;

}
}
```

@cnblackxp merci pour la suggestion. Cela m'a aidé. Voici comment je l'ai implémenté afin de conserver le comportement par défaut pour les 404 non suiveurs (c'est-à-dire que je réexporte simplement l'implémentation par défaut de Error ) :

import Error from "next/error";
import Router from "next/router";

export default Error;

Error.getInitialProps = ({ res, err, asPath }) => {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404;

  if (statusCode && statusCode === 404) {
    if (asPath.match(/\/$/)) {
      const withoutTrailingSlash = asPath.substr(0, asPath.length - 1);
      if (res) {
        res.writeHead(302, {
          Location: withoutTrailingSlash
        });
        res.end();
      } else {
        Router.push(withoutTrailingSlash);
      }
    }
  }

  return { statusCode };
};

ouais ça fera @cansin tant que rien d'autre n'est décidé :) bravo !

Petite amélioration de la solution de contournement de @AlexSapoznikov :

  render() {
    const { Component, pageProps, router: { asPath } } = this.props;

    // Next.js currently does not allow trailing slash in a route.
    // This is a client side redirect in case trailing slash occurs.
    if (pageProps.statusCode === 404 && asPath.length > 1 && asPath.endsWith('/')) {

La seule différence ici est de vérifier que le code d'état est 404. J'ai rencontré des problèmes en utilisant Link pour les routes dynamiques où ils étaient toujours rendus sur le serveur en raison de la redirection. Si vous voulez que le routage côté client fonctionne, vous ne pouvez pas ajouter de barre oblique à la propriété Link href , mais vous devez vous assurer de ne pas rediriger dans ce cas.

Le problème avec l'implémentation d'une solution de contournement dans le composant Error est qu'elle générera une erreur de notification dans le développement, ce qui me dérange. Quelques améliorations par rapport à ma précédente redirection côté client :

Ce qui s'est amélioré, c'est qu'il utilise désormais next/router côté client et que le remplacement de l'URL se fait sans rechargement.

pages/_app.jsx

import App from 'next/app';
import Router from 'next/router';

export default class MyApp extends App {
  render() {
    const { Component, pageProps, router: { asPath, route } } = this.props;

    // Next.js currently does not allow trailing slash in a route.
    // This is a client side redirect in case trailing slash occurs.
    if (pageProps.statusCode === 404 && asPath.length > 1 && asPath.endsWith('/')) {
      const routeWithoutEndingSlash = route.replace(/\/*$/gim, '');
      const asPathWithoutEndingSlash = asPath.replace(/\/*$/gim, '');

      if (typeof window !== 'undefined') {
        Router.replace(routeWithoutEndingSlash, asPathWithoutEndingSlash);
      }
      return null;
    }

    return <Component {...pageProps} />;
  }
}

merci aussi à @mbrowne pour le correctif 404 :)

A pris la solution de

MyError.getInitialProps = async ({ res, err, asPath }) => {
  // Capture 404 of pages with traling slash and redirect them
  const statusCode = res 
    ? res.statusCode
    : (err ? err.statusCode : 404);

  if (statusCode && statusCode === 404) {
    const [path, query = ''] = asPath.split('?');                                                                                                                                                                                             
    if (path.match(/\/$/)) {
      const withoutTrailingSlash = path.substr(0, path.length - 1); 
      if (res) {
        res.writeHead(302, {
          Location: `${withoutTrailingSlash}${query ? `?${query}` : ''}`,
        }); 
        res.end();
      } else {
        Router.push(`${withoutTrailingSlash}${query ? `?${query}` : ''}`);
      }   
    }   
  }

@pinpointcoder pouvez-vous fournir des exemples d'URL avec une barre oblique /blog/?123 ?

Merci à tous pour certaines de vos solutions de contournement ci-dessus. Ils ont travaillé!

Cependant, avons-nous un moyen officiel de résoudre ce problème de la part de l'équipe de Next ? Ce problème est là depuis des années.

Les pages de répertoire ne sont pas servies avec une barre oblique de fin lors de la prochaine exportation

@pinpointcoder pouvez-vous fournir des exemples d'URL avec une barre oblique /blog/?123 ?

@coodoo Pas lui, mais oui, malheureusement cela arrive

Alors que nous sommes sur le point de migrer des tonnes d'articles de blog pour lesquels l'URL canonique comprend actuellement une barre oblique de fin, c'est une douleur géante dans mon cul en ce moment.

J'ai décidé d'implémenter un serveur personnalisé pour gérer cela et il s'avère que c'est facile à faire, et vous pouvez toujours utiliser le système de routage basé sur les fichiers de next.js. De cette façon, vous pouvez réécrire l'URL que next.js voit et la vraie URL a toujours une barre oblique à la fin :

const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const conf = require('./next.config.js')

const PORT = process.env.PORT || 5000

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev, conf })
const handle = app.getRequestHandler()

app.prepare().then(() => {
    createServer((req, res) => {
        // If there is a slash at the end of the URL, remove it before sending it to the handle() function.
        // This is a workaround for https://github.com/zeit/next.js/issues/5214
        const url =
            req.url !== '/' && req.url.endsWith('/')
                ? req.url.slice(0, -1)
                : req.url
        // Be sure to pass `true` as the second argument to `url.parse`.
        // This tells it to parse the query portion of the URL.
        const parsedUrl = parse(url, true)

        handle(req, res, parsedUrl)
    }).listen(PORT, err => {
        if (err) throw err
        console.log(`> Ready on http://localhost:${PORT}`)
    })
})

Voir https://nextjs.org/docs/advanced-features/custom-server

@mbrowne Nous avons en fait un tas de raisons d'utiliser un serveur personnalisé, mais la principale chose qui m'a empêché d'en implémenter un jusqu'à présent est le fait que vous perdez l'optimisation statique automatique. Savez-vous s'il est possible de spécifier manuellement des routes statiques ?

Nous n'avons pas besoin d'optimisation statique automatique pour notre application pour le moment, je ne l'ai donc pas examinée.

J'utilise également un serveur personnalisé, mais lorsque vous passez une URL modifiée (sans barre oblique) à handle , SSR voit une URL différente du côté client.
Je préférerais que le routeur next corresponde à l'URL avec la barre oblique principale sans ces méchants hacks.

2020 et ce bug persiste. Incroyable

C'est un mauvais bug qui doit vraiment être corrigé. /products fonctionne, mais pas /products/ . Avec ce lien

<Link href="/products">
  <a>Products</a>
</Link>

Je reçois

index.js:1 Warning: Prop `href` did not match. Server: "/products" Client: "/products/"

Cependant, si je pointe le lien vers /products/ , que je visite le lien et que j'actualise la page pendant le développement, j'obtiens un 404. C'est une expérience de développement assez pénible.

Ce problème a été signalé pour la première fois il y a un an et demi ; pouvons-nous s'il vous plaît obtenir un correctif officiel? Il est toujours présent dans 9.3.4.

J'ai fait une redirection vers une URL à barre oblique non finale au lieu d'afficher le contenu, pour des raisons de référencement.

app.prepare().then(() => {
  createServer((req, res) => {
    if (req.url !== '/' && req.url.endsWith('/')) {
      res.writeHead(301, { Location: req.url.slice(0, -1) })
      res.end()
    }
    handle(req, res, parse(req.url, true))
  }).listen(PORT, err => {
    if (err) throw err
    console.log(`> Ready on http://localhost:${PORT}`)
  })
})

Pour le référencement, rel="canonical" peut aider, mais il faut quand même résoudre ce problème 404.

C'est un mauvais bug qui doit vraiment être corrigé. /products fonctionne, mais pas /products/ . Avec ce lien

<Link href="/products">
  <a>Products</a>
</Link>

Je reçois

index.js:1 Warning: Prop `href` did not match. Server: "/products" Client: "/products/"

Cependant, si je pointe le lien vers /products/ , que je visite le lien et que j'actualise la page pendant le développement, j'obtiens un 404. C'est une expérience de développement assez pénible.

Ce problème a été signalé pour la première fois il y a un an et demi ; pouvons-nous s'il vous plaît obtenir un correctif officiel? Il est toujours présent dans 9.3.4.

Je reçois également actuellement ce problème.

Voici comment je l'ai corrigé, https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3

Voici comment je l'ai corrigé, https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3

Merci, bien que cela nécessite un serveur personnalisé lors du développement local, et cela ne devrait pas être requis.

@timneutkens Y a-t-il une chance qu'un correctif pour ce problème puisse être

Plus important encore, la solution de redirection ne fonctionne pas pour ceux qui gèrent des sites dans cette zone déjà configurée pour ajouter une barre oblique plutôt que d'en supprimer une en production. Je ne pense pas que le cadre devrait dicter ce choix de manière arbitraire.

La solution de @AlexSapoznikov a bien fonctionné pour nous avec Netlify (qui ajoute une barre oblique par défaut). Voici une version avancée qui ajoute la prise en charge des paramètres de requête :

import App from "next/app";

export default class MyApp extends App {
  render() {
    const { Component, pageProps, router, router: { asPath } } = this.props;

    // Next.js currently does not allow trailing slash in a route, but Netlify appends trailing slashes. This is a
    // client side redirect in case trailing slash occurs. See https://github.com/zeit/next.js/issues/5214 for details
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/")) {
        const asPathWithoutTrailingSlash = path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (typeof window !== "undefined") {
          router.replace(asPathWithoutTrailingSlash, undefined, { shallow: true });
          return null;
        }
      }
    }

    return <Component {...pageProps} />;
  }
}

Je m'excuse parce que je suis un novice de Next JS, bien que j'aie de l'expérience en développement de logiciels sur d'autres SDK et plates-formes.

Je pense que ce "bug" m'a le plus surpris. Pour moi, cela violait le "principe du moindre étonnement". Je m'attendais simplement à ce que mon /about/ et /about fonctionnent de la même manière, puisque j'ai placé un index.tsx dans mon dossier /pages/about/.

J'ai commencé à créer des sites Web à la fin des années 90 avec HTML FTP sur mon serveur, puis je suis passé à PHP et Apache, et finalement aux serveurs Java. Maintenant, je me spécialise dans les applications mobiles. Cela me fait juste bizarre que ce comportement ne soit pas le comportement par défaut et que je doive écrire une page de serveur personnalisée pour le corriger sur mon serveur de développement.

Je prévois de faire une exportation statique, donc elle n'apparaîtra pas en production même si je n'écris pas le serveur personnalisé. Cela rend le développement et le débogage légèrement plus ennuyeux.

Pouvons-nous obtenir un indicateur "prochain dev" qui corrige cela afin que nous, les développeurs paresseux, n'ayons pas besoin d'écrire une logique de routage supplémentaire juste pour le temps de développement/débogage ?

Merci!

ps : Oui, je sais que /about et /about/ sont des URL complètement différentes. Je suis devenu vraiment confus quand j'ai mis un fichier index.tsx dans mon dossier /pages/about/ , et j'ai découvert qu'il ne fonctionnait qu'avec le chemin /about mais pas avec /about/ . Je serais moins surpris si c'était l'inverse.

pps : C'était encore plus déroutant lorsque j'avais un composant <Link></Link> qui pointe vers /about/ et qu'il fonctionne comme prévu. Ensuite, lorsque j'appuie sur Actualiser sur mon navigateur, il s'affiche immédiatement en 404, même si l'URL n'a pas changé. C'était très surprenant. :-RÉ

Mais attends, ça devient pire! Nous avons ajouté une fonction personnalisée checkForTrailingSlash l'intérieur de _error.js qui supprimerait la barre oblique de fin et redirigerait. Cela a bien fonctionné pendant un certain temps jusqu'à ce que nous ayons (enfin) ajouté une page 404 personnalisée et constaté qu'avec une page 404 personnalisée, Next.js contourne complètement Error . Cela signifie qu'aucune de vos logiques personnalisées dans Error.getInitialProps ne fonctionnera plus - y compris une vérification des barres obliques de fin.

Je suppose que je vais essayer la solution _app.js mentionnée par d'autres, car un serveur personnalisé n'est tout simplement pas encore une possibilité.

La solution de @AlexSapoznikov a bien fonctionné pour nous avec Netlify (qui ajoute une barre oblique par défaut). Voici une version avancée qui ajoute la prise en charge des paramètres de requête :

import App from "next/app";

export default class MyApp extends App {
  render() {
    const { Component, pageProps, router, router: { asPath } } = this.props;

    // Next.js currently does not allow trailing slash in a route, but Netlify appends trailing slashes. This is a
    // client side redirect in case trailing slash occurs. See https://github.com/zeit/next.js/issues/5214 for details
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/")) {
        const asPathWithoutTrailingSlash = path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (typeof window !== "undefined") {
          router.replace(asPathWithoutTrailingSlash, undefined, { shallow: true });
          return null;
        }
      }
    }

    return <Component {...pageProps} />;
  }
}

Il y a une erreur critique dans votre exemple de code : les requêtes à la route d'index avec un paramètre de requête généreront une erreur, car vous finirez par essayer de transmettre uniquement la chaîne de requête à Next.js en tant que asPath .

Cela résout le problème :

  if (asPath && asPath.length > 1) {
    const [path, query = ''] = asPath.split('?');
    if (path.endsWith('/') && path.length > 1) {
      const asPathWithoutTrailingSlash =
        path.replace(/\/*$/gim, '') + (query ? `?${query}` : '');
      if (typeof window !== 'undefined') {
        router.replace(asPathWithoutTrailingSlash, undefined, {
          shallow: true,
        });
        return null;
      }
    }
  }

Pour que cela fonctionne avec SSR, j'ai dû ajouter ce qui suit à la solution @pjaws & @AlexSapoznikov :

  static async getInitialProps({ Component, ctx, router }) {
    /* Fixes the trailing-slash-404 bug for server-side rendering. */
    const { asPath } = router;
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/") && path.length > 1) {
        const asPathWithoutTrailingSlash =
          path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (ctx.res) {
          ctx.res.writeHead(301, {
            Location: asPathWithoutTrailingSlash,
          });
          ctx.res.end();
        }
      }
    }
    return {
      pageProps: Component.getInitialProps
        ? await Component.getInitialProps(ctx)
        : {},
    };
  }

C'est probablement une bonne idée de généraliser cette fonctionnalité en une fonction qui fonctionne à la fois pendant la SSR et pendant la CSR et de l'appeler aux deux endroits ( getInitialProps et render ).

par

cela va corriger mais le titre est faux. Hmm
image

@AlexSapoznikov @pjaws

Votre solution nous met en boucle infinie :

  if (asPath && asPath.length > 1) {
    const [path, query = ''] = asPath.split('?');
    if (path.endsWith('/') && path.length > 1) {
      const asPathWithoutTrailingSlash =
        path.replace(/\/*$/gim, '') + (query ? `?${query}` : '');
      if (typeof window !== 'undefined') {
        router.replace(asPathWithoutTrailingSlash, undefined, {
          shallow: true,
        });
        return null;
      }
    }
  }

Le contexte

Pour des raisons indépendantes de notre volonté, nous devons utiliser l'option exportTrailingSlash dans next.config.js .

Nous voulons avoir un lien vers une autre page mais nous voulons que le lien soit /somepage?param=whatever .

Il semble que le lien suivant le convertisse en /somepage/?param=whatever et nous obtenons la page introuvable.

En utilisant la solution ci-dessus, résolvez le problème des paramètres, mais lorsque vous accédez à une page déployée comme /somepage/ il entre dans une boucle infinie.

Je pense que @ronyeh avait fait un très bon point ici, donc je veux vraiment une solution officielle pour ce problème :(

Pour que cela fonctionne avec SSR, j'ai dû ajouter ce qui suit à la solution @pjaws & @AlexSapoznikov :

  static async getInitialProps({ Component, ctx, router }) {
    /* Fixes the trailing-slash-404 bug for server-side rendering. */
    const { asPath } = router;
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/") && path.length > 1) {
        const asPathWithoutTrailingSlash =
          path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (ctx.res) {
          ctx.res.writeHead(301, {
            Location: asPathWithoutTrailingSlash,
          });
          ctx.res.end();
        }
      }
    }
    return {
      pageProps: Component.getInitialProps
        ? await Component.getInitialProps(ctx)
        : {},
    };
  }

C'est probablement une bonne idée de généraliser cette fonctionnalité en une fonction qui fonctionne à la fois pendant la SSR et pendant la CSR et de l'appeler aux deux endroits ( getInitialProps et render ).

Cela a fonctionné pour les pages avec getServerSideProps et maintenant les URL avec des barres obliques de fin renvoient la même page sans 404.
Mais il y a un problème, j'ai quelques pages qui utilisent des routes dynamiques et getStaticPaths, je ne peux pas utiliser getServerSideProps dessus et donc lorsque ces routes dynamiques sont parcourues avec une barre oblique, elles renvoient d'abord un 404, puis elles redirigent vers la page .

Je travaille avec un dossier /api/test

  • pages/api/test.tsx
  • pages/api/test/[id].tsx

ça marche pour

  • GET /api/test
  • GET /api/test/123
  • GET /api/test/123/

et je viens de découvrir que cela ne fonctionne pas

  • GET /api/test/

je ne sais pas si c'est un problème lié
P/D exportTrailingSlash = true ne le résout pas

C'est un problème très ancien, y a-t-il une raison pour laquelle il n'est pas traité depuis si longtemps ?

Je ne sais plus ce qui ne fonctionne plus.

Ma compréhension est que les exigences sont les suivantes:

| | exportTrailingSlash : false | exportTrailingSlash : true |
|--------------------------|------------------------ -----|---------------------------|
| l'url se termine par / | Ne devrait pas fonctionner | Devrait fonctionner |
| l'url ne se termine pas par / | Devrait fonctionner | Ne devrait pas fonctionner |

Cela fonctionne comme prévu où :

  • Localement, nous utilisons exportTrailingSlash: false
  • Pour les déploiements (versions de production), nous utilisons exportTrailingSlash: true et un nginx convertit url/ en url/index.html

D'après ce que je peux voir dans @andrescabana86 Cela fonctionne là où cela ne devrait pas: GET /api/test/123/ alors que GET /api/test/ ne fonctionne pas et cela ne devrait pas.

@Izhaki j'ai essayé les deux, en déployant sur prod... et pour moi ça ne marche pas

  • GET /api/test/

et j'utilise exportTrailingSlash: true

Je peux essayer de créer un dépôt public si vous voulez, j'ai peut-être oublié quelque chose au milieu.

Merci pour vos réponses

@ andrescabana86 Je ne sais pas dans quelle mesure un

Nous testons nos builds de production (avec exportTrailingSlash: true ) localement en utilisant ce script dans package.json :

"serve:out": "docker run --rm -v $(pwd)/out:/static -p 5000:80 flashspys/nginx-static"

S'il vous plaît laissez-moi savoir si aller dans votre navigateur à http://localhost:5000/api/test/ fonctionne.

(Notez que $(pwd) est sur Mac/Linux - voir ceci pour Windows)

@Izhaki, le problème était

Je viens de tester avec 9.4.1 et exportTrailingSlash: true .

Aller à http://localhost:6500/admin/ renvoie 404 lors du développement local.

Mais le même chemin fonctionne lorsque vous exportez.

Notez que exportTrailingSlash indique que c'est uniquement pour les _exports_.

Ce que nous faisons, c'est utiliser :

exportTrailingSlash: process.env.NODE_ENV === 'production'

Cela signifie que les choses fonctionnent comme prévu lorsque nous nous développons localement. Et fonctionnent correctement une fois déployés (via l'exportation).

N'est-ce pas la solution correcte et viable pour cela?

Si une URL ne fonctionne pas en développement mais fonctionne en production, ne pensez-vous pas que cela va à l'encontre du principe de moindre surprise ? Je pense que cela doit toujours être considéré comme un bug.

^ Cela dit, je suis presque sûr qu'auparavant, en production, il y avait un comportement conflictuel entre une actualisation de page et un événement router.push. Je ne sais pas si c'est toujours le cas.

@andrescabana86 @Izhaki exportTrailingSlash n'est pas lié à cela. Cette option concerne l'exportation statique des applications Next.js. Lorsqu'il est vrai, example/index.html est généré, alors que lorsqu'il est faux, example.html est généré. Je crois comprendre que exportTrailingSlash n'a rien à voir avec le mode de développement.

Je pense qu'une source de confusion est que lorsque vous avez exportTrailingSlash next.js ajoute une barre oblique de fin aux liens. Cela se produit également dans le développement, je ne suis pas sûr que cela devrait le faire? Mais de toute façon, il ne s'agit pas seulement de example/index.html contre example.html - vous avez également besoin de modifier les liens.

Si une URL ne fonctionne pas en développement mais fonctionne en production, ne pensez-vous pas que cela va à l'encontre du principe de moindre surprise ? Je pense que cela doit toujours être considéré comme un bug.

Je me trompe peut-être, mais l'option exportTrailingSlash était destinée aux serveurs nginx qui ne sont pas configurés pour servir /something.html lorsque l'url est /something .

Ce n'est pas le cas avec le prochain serveur utilisé pour le développement local. Donc, ce qui fonctionne et ce qui ne fonctionne pas dépend de ce qui sert votre application.

Vous pouvez faire valoir que lorsque exportTrailingSlash est vrai, le serveur suivant devrait prendre en charge les routes se terminant par une barre oblique (bien que cela rende le export dans exportTrailingSlash quelque peu hors de propos).

FWIW cela est déjà en cours de travail #13333

Je ne suis pas un codeur très expérimenté, j'utilise Next.js principalement pour les atterrissages multipages. Apparemment, j'ai utilisé la solution de contournement suivante presque tout le temps, à l'insu de son effet. En voici une version simplifiée :

// In your server.js
server.get('/:id', (req, res) => {
  const actualPage = `/${req.params.id}`
  app.render(req, res, actualPage)
})

Dans mon cas, le code est un peu plus compliqué, car je l'utilise pour prendre en charge des préfixes d'URL statiques supplémentaires, etc. Mais cette version simplifiée semble fonctionner correctement pour le problème discuté, quel que soit le exportTrailingSlash réglage et son effet sur les Link s. Par exemple, les URL /about et /about/ fonctionnent très bien.

Dans sa forme actuelle, il imite essentiellement le routage natif de Next.js. L'inconvénient : cela nécessite un server.js , et vous devrez le prendre en charge manuellement pour les URL "plus profondes" (avec des "sous-dossiers") supplémentaires, par exemple /company/about/ . Mais cela semble être une solution relativement simple pour ceux qui utilisent déjà des server.js dans leur projet.

Pour que cela fonctionne avec SSR, j'ai dû ajouter ce qui suit à la solution @pjaws & @AlexSapoznikov :

  static async getInitialProps({ Component, ctx, router }) {
    /* Fixes the trailing-slash-404 bug for server-side rendering. */
    const { asPath } = router;
    if (asPath && asPath.length > 1) {
      const [path, query = ""] = asPath.split("?");
      if (path.endsWith("/") && path.length > 1) {
        const asPathWithoutTrailingSlash =
          path.replace(/\/*$/gim, "") + (query ? `?${query}` : "");
        if (ctx.res) {
          ctx.res.writeHead(301, {
            Location: asPathWithoutTrailingSlash,
          });
          ctx.res.end();
        }
      }
    }
    return {
      pageProps: Component.getInitialProps
        ? await Component.getInitialProps(ctx)
        : {},
    };
  }

C'est probablement une bonne idée de généraliser cette fonctionnalité en une fonction qui fonctionne à la fois pendant la SSR et pendant la CSR et de l'appeler aux deux endroits ( getInitialProps et render ).

Cela a fonctionné pour les pages avec getServerSideProps et maintenant les URL avec des barres obliques de fin renvoient la même page sans 404.
Mais il y a un problème, j'ai quelques pages qui utilisent des routes dynamiques et getStaticPaths, je ne peux pas utiliser getServerSideProps dessus et donc lorsque ces routes dynamiques sont parcourues avec une barre oblique, elles renvoient d'abord un 404, puis elles redirigent vers la page .

@gauravkrp Il s'agit en fait d'un ajout extrêmement important, car la solution @AlexSapoznikov renverra toujours un 404 pour la page à Google (puisque la redirection se produit sur le client). J'imagine que le référencement est l'une des principales raisons pour lesquelles beaucoup d'entre nous utilisent Next.js en premier lieu.

Je pense aussi que mettre cela dans getInitialProps devrait fonctionner tout autour, et la pièce à l'intérieur de la fonction principale est inutile à ce stade. La principale mise en garde ici est que vous perdez l'optimisation statique automatique en l'ayant - probablement mieux qu'un tas de 404, cependant.

Pour un peu de partage...

Mon projet est Express + Next.js .
express 4.17.1
next 9.4.5-canary.7

Quand le développement

Temps d'exécution dynamique

// next.config.js
module.exports = {
  exportTrailingSlash: false,
};

// app.js
const Next = require('next').default;
const NextApp = Next({ dev });
const NextHandler = NextApp.getRequestHandler();
NextApp.prepare();
app.get('*', (req, res) => NextHandler(req, res));

Quand la production

Exportation statique
Courez next build et next export -o dist/

// next.config.js
module.exports = {
  exportTrailingSlash: true,
};

// app.js
app.use('/_next', express.static('dist/_next', { etag: true, index: false, maxAge: '365d', redirect: false, dotfiles: 'ignore' }));
app.use('/fonts', express.static('dist/fonts', { etag: true, index: false, maxAge: '365d', redirect: false, dotfiles: 'ignore' }));
app.use('/img', express.static('dist/img', { etag: true, index: false, maxAge: '365d', redirect: false, dotfiles: 'ignore' }));
app.use(express.static('./dist', { index: ['index.html'] }));
app.use((req, res) => {
  res.Redirect('/404'); // <- Express will auto handle both /404 or /404/
});

En conclusion

Je n'ai aucun problème lors de la redirection en cliquant sur l'application cliente,
le rafraîchissement dur fonctionne également sur static route .

Mais il sera 404 lors d'une actualisation matérielle sur dynamic route ,
comme /album/[id].jsx ou /album/123 ,
J'ai donc hâte de résoudre ce problème en utilisant le mécanisme suivant.

par exemple
Lorsque vous frappez 404 à /album/123 ,
le serveur doit continuer à fournir du contenu html,
le navigateur continuera à charger la page sans problème,
au démarrage de Next.js, next/router devrait le gérer automatiquement.

existe-t-il une solution temporaire à ce problème sur la production?

Nous sommes sur le point de débarquer une fonctionnalité corrigeant cela, dans un jour ou deux !

existe-t-il une solution temporaire à ce problème sur la production?

Il y en a beaucoup dans ce fil, mais j'utilise actuellement ce que @gauravkrp a publié récemment, et cela fonctionne bien pour moi.

Vous pouvez suivre le PR ici : #13333

Ceci a maintenant été résolu dans next@^9.4.5-canary.17 !

Combien de temps faut-il pour que la fonctionnalité passe de Canary à Master ?

Ceci a maintenant été résolu dans next@^9.4.5-canary.17 !

Et comment ça se résout exactement ? juste en supprimant la barre oblique de fin ? si j'accède à " www.site.com/help " , pouvons-nous avoir une option là-bas, nous choisissons de laisser la barre oblique finale ? accéder à " www.site.com/help/ " ou " www.site.com/help " quittera ou redirigera ou ajoutera "/" à la fin pour avoir : " www.site.com/help/ "

@Valnexus voir #13333, il inclut une option expérimentale :

module.exports = {
  experimental: {
    trailingSlash: true
  }
}

Combien de temps faut-il pour que la fonctionnalité passe de Canary à Master ?

Quand c'est prêt. Il y a encore des cas limites dans la manipulation qui sont en train d'être résolus. Une fois que ceux-ci ont été corrigés, il peut passer à stable.

@timneutkens @Janpot

J'ai essayé le dernier canary suivant (9.4.5-canary.27) mais lorsque je crée une page test et que j'accède à www.example/test/ il redirige vers www.example/test
Je pense que le comportement dans les deux cas devrait être le même.

Lorsque vous accédez à www.example/test/ il doit rester sur www.example/test/ .
Lorsque vous accédez à www.example/test il doit rester sur www.example/test .
Je le teste sur Nuxt.js, il fonctionne de la même manière que je décris ci-dessus.

Je pense que le comportement dans les deux cas devrait être le même.

La raison d'une redirection est de s'assurer que les moteurs de recherche ne voient pas de contenu en double. Quel est votre cas d'utilisation exact ?

Je ne vois pas pourquoi c'est un problème fermé s'il n'est pas encore fusionné avec une version stable. Si j'ai bien compris, cela n'est corrigé que dans la version Canary pour le moment, n'est-ce pas ?

Les problèmes sont fermés lorsque la demande d'extraction associée arrive, car ils sont disponibles pour une utilisation immédiate sur Canary. Si vous avez besoin de cette fonctionnalité, veuillez passer au canal Canary.

Ça a l'air bien. Merci @Timer !

@Janpot J'ai vu que https://github.com/issues/ et https://github.com/issues peuvent accéder au même comportement sans redirection.

https://twitter.com/explore/ et https://twitter.com/explore , celui-ci aussi.

S'il a un problème avec les moteurs de recherche, pourquoi Github et Twitter ne l'ont pas résolu ?
Je pense que c'est le comportement par défaut pour n'importe quel site Web.

Il n'y a pas de cas d'utilisation spécifique, c'est juste mon avis que cela devrait fonctionner de cette façon.

S'il a un problème avec les moteurs de recherche, pourquoi Github et Twitter ne l'ont pas résolu ?

@armspkt Ce n'est pas un problème car il existe plusieurs façons de le résoudre. Par exemple, Twitter utilise l'attribut <link rel="canonical"> pour indiquer aux robots de recherche quelle page ils doivent explorer et les autres versions doivent être marquées comme dupliquées.

La redirection est donc un moyen viable de faire du référencement sur votre site Web. Vous pouvez lire plus d'informations ici .

@ziserman Si nous avons plusieurs façons de le résoudre, nous devons conserver la même URL sans redirection pour l'expérience utilisateur.

@Janpot https://github.com/nuxt-community/nuxt-i18n/issues/422

Nuxtjs a plusieurs options à choisir (undefined, true, false)

Nextjs devrait-il également avoir plusieurs options à choisir ?

La raison d'une redirection est de s'assurer que les moteurs de recherche ne voient pas de contenu en double. Quel est votre cas d'utilisation exact ?

@Janpot Notre API comporte des barres obliques à de nombreux endroits. La dernière version soulève beaucoup de 404 sur le backend car les URL avec des barres obliques de fin (/api/test/ -> /api/test) ne correspondent pas

Je ne sais pas si cela fonctionnera pour tout le monde, mais j'ai trouvé cette solution qui fonctionne pour moi. Mettez-le dans le fichier _app.js .

static async getInitialProps(ctx) {
    const appProps = await App.getInitialProps(ctx);

    // Remove trailing slash
    const path = ctx.router.asPath,
            res = ctx.ctx.res;

    if (path.length > 1 && /\/$/.test(path)) {
        res.writeHead(301, {Location: path.slice(0, -1)})
        res.end();
    }

    return {...appProps};
}

@mlbonniec J'ai minimisé votre commentaire car il provoque de graves régressions de performances dans une application Next.js.

La dernière version de next@canary corrige ce bogue, veuillez mettre à jour à la place !

@mlbonniec J'ai minimisé votre commentaire car il provoque de graves régressions de performances dans une application Next.js.

La dernière version de next@canary corrige ce bogue, veuillez mettre à jour à la place !

Aucun problème!
Cependant, j'ai mis à jour plus tôt, et cela n'a pas résolu le problème.
Avec npm update

Si le dernier canary Next.js ne résout pas le bogue pour vous, veuillez ouvrir un nouveau problème afin que nous puissions y jeter un coup d'œil. ??

Petite question : comment les projets avec next export géreront-ils ce changement ? En créant une page entièrement nouvelle pour chaque page pour la barre oblique de fin ? Je ne pense pas qu'une application exportée puisse spécifier des redirections HTTP (ou des réécritures).

Les projets qui utilisent next export auront tous leurs <Link /> côté client correctement mis à jour, mais la redirection côté serveur nécessitera une configuration manuelle. Les projets déployés avec la cible sans serveur ou next start configureront ces paramètres automatiquement.

@Timer une fois que cela atteint une version complète, aurions-nous encore besoin d'utiliser l'option expérimentale ?

@Timer une fois que cela atteint une version complète, aurions-nous encore besoin d'utiliser l'option expérimentale ?

Non, il serait juste disponible tel quel.

Je suppose que l'option trailingSlash ne fonctionnera pas pour next export ? Quelle est la meilleure façon de rediriger /page/ vers /page (ou vice versa) dans, disons, les pages github ?

Je suppose que l'option trailingSlash ne fonctionnera pas pour next export ? Quelle est la meilleure façon de rediriger /page/ vers /page (ou vice versa) dans, disons, les pages github ?

Pour autant que je sache, les pages github n'ont pas de fonctionnalité de redirection. Cela fonctionne immédiatement sur vercel.com, qui est également gratuit pour les projets de loisirs (comme les pages github).

Les projets qui utilisent next export auront tous leurs <Link /> côté client correctement mis à jour, mais la redirection côté serveur nécessitera une configuration manuelle. Les projets déployés avec la cible sans serveur ou next start configureront ces paramètres automatiquement.

Salut @Timer Pouvez-vous expliquer plus? Comment puis-je configurer manuellement ? Voici donc ma situation. Sur mon site Web, j'utilise next-i18next . Après avoir déployé avec next build && next export , tous les liens internes fonctionnent, mais lorsque vous entrez manuellement l'URL, AUCUN d'entre eux ne fonctionne et entraîne une erreur 404. À partir de là, j'ai décidé d'utiliser trailingSlash:true et donc d'entrer manuellement /pricing fonctionnera maintenant mais /zh/pricing conduit à des erreurs 404.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

havefive picture havefive  ·  3Commentaires

flybayer picture flybayer  ·  3Commentaires

timneutkens picture timneutkens  ·  3Commentaires

jesselee34 picture jesselee34  ·  3Commentaires

olifante picture olifante  ·  3Commentaires