Next.js: Nachgestellter Schrägstrich im Link für legitime Seite funktioniert für die clientseitige Navigation, führt jedoch zu nicht gefundenem Bundle und 404 bei harter Aktualisierung (ssr)

Erstellt am 20. Sept. 2018  ·  119Kommentare  ·  Quelle: vercel/next.js

Nachgestellter Schrägstrich im Link für legitime Seite funktioniert für die clientseitige Navigation, führt jedoch zu nicht gefundenem Bundle und 404 bei harter Aktualisierung (ssr)

Fehlerbericht

Beschreibe den Fehler

lass es mich wissen, wenn der Titel weitere Erläuterungen benötigt.

Alle relevanten Probleme wurden mit der Begründung geschlossen, dass es in 6-Canary behoben wurde (ich glaube, es ist nicht) oder durch einen verbesserten Aufschlag (was nur beim statischen Export der Produktion zutrifft).

Ich schreibe meinen bestehenden Blog in next.js um und habe zuvor nachgestellte Schrägstriche verwendet. Das neueste serve kann dabei helfen, sobald ich meinen next.js-basierten Blog erstellt habe. Aber um dev env zu reparieren, muss ich entweder nachgestellte Schrägstriche loswerden und 301 Moved Permanently in prod verwenden; oder leben Sie mit Unterstützung für unterbrochene Schrägstriche in dev.

Fortpflanzen

Hier ist ein minimal reproduzierbarer Fall (Link zum Repro-Repo befindet sich unter dem Ausschnitt):

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

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

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

Minimal reproduzierbares Repo https://github.com/iamstarkov/next.js-trailing-slash-bug-demo

  1. Klon-Repository git clone https://github.com/iamstarkov/next.js-trailing-slash-bug-demo
  2. Verzeichnis wechseln cd next.js-trailing-slash-bug-demo
  3. installiere deps yarn
  4. Entwickler ausführen: yarn dev
  5. öffne http://localhost :3000/
  6. Öffne den Netzwerk-Tab von Devtools
  7. beobachte, dass http://localhost:3000/_next/static/development/pages/about.js 200ed ist
  8. beobachte, dass http://localhost:3000/_next/on-demand-entries-ping?page=/about/ 200ed ist
  9. beobachte, dass http://localhost:3000/about/ 404ed wird
  10. Beobachte anhaltende Versuche, http://localhost:3000/about/ zu lösen
  11. im Terminal beobachten Client pings, but there's no entry for page: /about/
  12. Lade die Seite neu
  13. Beachten Sie die 404-Seite.
  14. Entfernen Sie den abschließenden Schrägstrich in der URL oder klicken Sie auf http://localhost :3000/about
  15. Beachten Sie, dass die Seite 200ed ist
  16. Um die Fehlerbeständigkeit sicherzustellen, wiederholen Sie die Schritte 5-15 einmal.

Erwartetes Verhalten

  1. /about/ sollte nicht als 404 not found
  2. /about/ sollte als 200 ok
  3. Server sollte Client pings, but there's no entry for page: /about/ nicht drucken
  4. sowohl /about als auch /about/ sollten auf die gleiche Weise funktionieren

Screenshots

N / A

System Information

  • Betriebssystem: macOS High Sierra 10.13.6 (17G65)
  • Browser (sollte keine Rolle spielen, kann aber in Chrome 69.0.3497.100 und Safari Version 12.0 (13606.2.11) repro't werden (war das gleiche für Safari 11)
  • Version von Next.js: 7.0.0 (könnte auf 5.x und 6.x reproduzieren)

Zusätzlicher Kontext

Fügen Sie hier jeden weiteren Kontext zu dem Problem hinzu.

Wenn Sie diesen Code in https://github.com/zeit/next.js/blob/459c1c13d054b37442126889077b7056269eeb35/server/on-demand-entry-handler.js#L242 -L249 ändern

oder node_modules/next/dist/server/on-demand-entry-handler.js lokal

          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}`

und starte next dev und öffne http://localhost :3000/ und klicke dann auf den Link:

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

Ich denke, das Problem (zumindest ein Teil davon) liegt darin, dass die Middleware von onDemandEntryHandler Seite in Einträgen nicht finden kann, wenn die Seite einen nachgestellten Schrägstrich hat.

Ich hoffe, dass meine 2 Stunden Untersuchung und Vorbereitung bei der Behebung dieses Problems helfen können.

story 8 feature request

Hilfreichster Kommentar

Wir sind dabei, ein Feature zu veröffentlichen, das dies behebt - einen Tag oder so!

Alle 119 Kommentare

relevanteste und bemerkenswerteste Probleme sind #1189 und #3876

Ich freue mich darauf, dass dies endlich gelöst wird! @timneutkens Wie ist der Status von Problemen mit nachgestellten Schrägstrichen für Next 7?

@NathanielHill Ich könnte es auf next@7 reproduzieren

Ich verwende nextjs 7 und der nachgestellte Schrägstrich erzeugt einen 404 für mich sowohl auf dev als auch auf prod:

  • beim ersten Laden der Seite
  • auf der Seite aktualisieren

Und beeinflusst:

  • ein externer Link
  • ein interner Link
  • URL in den Browser eingefügt

Das Entfernen des abschließenden Schrägstrichs behebt das Problem.

Nachgestellte Schrägstriche werden oft von Browsern, Servern und/oder anderen Diensten hinzugefügt, bei denen Links eingefügt werden könnten. Obwohl ich interne Links kontrollieren kann, ist es schwer zu kontrollieren, auf welche Links externe Benutzer möglicherweise gelangen

Ich sehe dieses Problem auch in Version 7. Ich bin mir nicht sicher, ob dies relevant ist, aber ich verknüpfe ein Next.js-Projekt mit einem Unterordner einer anderen Now-Bereitstellung. Unsere Basis-URL lautet also primer.style und wir aliasen unsere primer-components.now.sh Next.js-App auf primer.style/components . In der Produktion funktioniert die Indexseite von primer.style/components einwandfrei, aber primer.style/components/ erzeugt einen 404.

Ich musste ein bisschen suchen, um dieses Problem zu finden. Ich verwende statische Bereitstellungen auf Netlify, daher ist dies kein Problem bei Prod, aber bei der Entwicklung (Next 7) friert die Kompilierung einfach ein, wenn es einen nachgestellten Schrägstrich gab, und es war schwer herauszufinden, warum. Ich glaube nicht, dass dies (kein nachlaufender Schrägstrich in der Entwicklungsumgebung) ein guter DX ist.

Ich habe auch dieses Problem und es ist wirklich ärgerlich, ich hoffe es wird bald behoben.

Wenn Sie einen nachgestellten Schrägstrich wünschen, können Sie dies einfach tun. <Link href='/about' as='/about/'><a>about</a></Link> aber wenn du freitags/nächste-routen benutzt ist dies nicht möglich. Also habe ich eine Gabel, wo Sie trailingSlash als Requisite hinzufügen können. Hoffe das hilft

Wenn Sie einen nachgestellten Schrägstrich wünschen, können Sie dies einfach tun. <Link href='/about' as='/about/'><a>about</a></Link> aber wenn du freitags/nächste-routen benutzt ist dies nicht möglich. Also habe ich eine Gabel, wo Sie trailingSlash als Requisite hinzufügen können. Hoffe das hilft

@aluminick Es tut mir leid, ich habe es gerade ausprobiert und es funktioniert bei mir nicht. Ich komme immer noch zu einer traling-slashed-Seite (neueste Version), die nach dem Aktualisieren nicht gefunden wird (aktuelles Verhalten).

auch helfen weder #6664 noch #6752, weil experimental.exportTrailingSlash nicht hilft, weil es nur für next export , glaube ich

es gab einen vielversprechenden Pull-Request #6421 von @Janpot , der leider keinen Konsens gefunden hat

@iamstarkov Wie ist der Status dieses Problems? Irgendwelche Lösungen neben dem server.js Hook?

@dryleaf- Status: es ist noch geöffnet

Ein ähnliches Problem ... Umleitung, wenn mehrere Schrägstriche hinzugefügt werden. Beispiel: https://github.com/zeit/next.js////////////issues/5214

GitHub-URLs sind irrelevant

@iamstarkov Ich

Die GitHub-URL soll eine einfache Demonstration dafür sein, wie URLs (vorzugsweise) funktionieren sollten, wenn eine App mit Next.js erstellt wird. Mit anderen Worten, wenn ein Benutzer einen zusätzlichen Schrägstrich hinzufügt, sollte die URL weiterhin funktionieren.

Irgendein Update für nextjs 9?

Ich bin neu bei Next, aber was ist die Problemumgehung, die Sie für dieses Problem verwenden?

@iamstarkov Wie ist der Status dieses Problems?

Ich bin schockiert, dass dieses Problem seit ungefähr einem Jahr in keiner Weise gelöst wurde!
Braucht das Next.js-Team noch andere Gründe, um dieses Problem zu beheben?

URLs sollten unabhängig vom abschließenden Schrägstrich funktionieren. Überprüfen Sie eine beliebige Website im Web.

Wenn dies außerhalb des Geltungsbereichs von Next.js liegt, geben Sie uns die Möglichkeit, dies in Now zu konfigurieren.
Ich bin wirklich verwirrt, dass das Zeit-Team solche kritischen Themen jahrelang ignoriert.

@exentrich Dies ist in Zeit Now leicht konfigurierbar, indem einfach alle nachfolgenden Schrägstriche ohne Schrägstriche auf dieselbe Route umgeleitet werden:

now.json :

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

Ich verstehe jedoch auch nicht, warum dies nicht von Next.js selbst behandelt wird und warum das Team dieses Problem ignoriert hat.

Dies, zusammen mit einem public/ (in Arbeit) sind die Hauptprobleme, mit denen ich CRA-Konvertiten konfrontiert sehe.

@rauchg

@NathanielHill danke!
Ich habe diese Lösung ausprobiert, aber Abfrageparameter werden entfernt. Zum Beispiel wird /some/?query=1 /some ohne Abfrage zu

Ja, das klingt nach einem Problem @exentrich

Ich hätte dieses Verhalten nicht vermutet, da mir gesagt wurde, dass es ein implizites ^ und $ die um die Regex gewickelt sind (was bedeutet, dass Ihr Beispiel nicht übereinstimmen würde). Vielleicht gibt es eine Möglichkeit, selbst auf die Abfragezeichenfolge zuzugreifen, um sie wieder hinzuzufügen :man_shrugging: Viel Glück

Ich versuche, es mit einem benutzerdefinierten Express-Server und Avinoamr/Connect-Slashes zum Laufen zu bringen, scheint aber auf das gleiche Problem zu stoßen

Dies ist sicherlich ein massives Problem, insbesondere weil / Routen Fehlerseiten werfen und das SEO schadet (was eine der Hauptattraktionen von Next ist).

Die 301-Weiterleitungen und die benutzerdefinierten Express-Server scheinen alle eher Hacks als Fixes zu sein. In meinem Fall habe ich eine voll funktionsfähige Anwendung, die auf Next basiert, ohne benutzerdefinierten Express-Server - alles andere funktioniert perfekt, aber jetzt muss ich nur wegen des Problems mit dem abschließenden Schrägstrich einen neuen Express-Server erstellen. Der Aufwand scheint unverhältnismäßig, wenn man bedenkt, dass es sich um einen Hack handelt. Ich würde mich freuen, wenn dies nach Priorität gehoben werden könnte! Aus diesem Grund höre ich in meinem Team Murren darüber, Next im Gegensatz zu so etwas wie Vanilla React/Angular verwendet zu haben, und dies schwächt sicherlich die Argumente für Next.

PS: Ich arbeite total gerne mit Next ❤️

Dies ist sicherlich ein massives Problem, insbesondere weil / Routen Fehlerseiten werfen und das schadet SEO

Es schadet Ihrem SEO nicht. Google behandelt nachgestellte Schrägstriche als eine andere Seite. Wenn es 404 hat, wirkt sich das SEO nicht mehr aus als jede andere nicht vorhandene Seite auf Ihrer Website. Außerdem wird Google nicht versuchen, es zu crawlen, solange Sie nie mit einem abschließenden Schrägstrich darauf verlinken. Dieses Problem ist zwar immer noch ein gültiges Problem, aber viel weniger kritisch, als Sie es alle machen.

@nik-john @NathanielHill @dkrish @exentrich

Sie sollten keinen Express-Server verwenden müssen, um eine 301-Umleitung durchzuführen. Hängt von Ihren Anforderungen ab, aber ich konnte meine mit einem benutzerdefinierten server.js erfüllen.

Eine 301-Weiterleitung ist auch der beste Weg für SEO, da Sie keine Strafen für doppelten Inhalt für die Slash- und Non-Slash-Route erhalten.

Ich liebe ❤️ Next.js, aber ich stimme dafür, dass dies ohne diese Umgehung gehandhabt wird.

// 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

Es schadet Ihrem SEO nicht. Google behandelt nachgestellte Schrägstriche als eine andere Seite. Wenn es 404 hat, wirkt sich das SEO nicht mehr aus als jede andere nicht vorhandene Seite auf Ihrer Website.

Ich nehme Ihren Punkt an, dass es SEO von Natur aus nicht besonders schadet. Aber es übt zusätzlichen Druck auf die Entwickler aus, die URL-Definitionen jedes Mal richtig zu machen, was menschlichen Fehlern unterliegt. Ein Entwickler, der neu bei Next ist, würde nicht unbedingt wissen, dass die folgende (ganz normal aussehende) URL zu einer 404-Seite führt. <Link href='/people/'>

Ein ausgereiftes Framework sollte idealerweise imo nicht solchen menschlichen Fehlern unterliegen.

Außerdem wird Google nicht versuchen, es zu crawlen, solange Sie nie mit einem abschließenden Schrägstrich darauf verlinken.

Auch hier gibt es das Problem, dass Leute versehentlich auf _ www.mysite.com/people/_ anstatt auf _ www.mysite.com/people_ verlinken (beide scheinen für Benutzer genau gleich zu sein - sogar für die meisten Entwickler).

Beide Szenarien _können_ sich auf SEO auswirken.

Abgesehen von den SEO-Auswirkungen gibt es auch die semantische Bedeutung der URL - worauf _zeigt_ _ www.mysite.com/people / _? Im Idealfall, weil es auf ein Verzeichnis verweist, sollte Next alles zurückgeben, was in pages > people > index.js (im Gegensatz zu pages > people.js für _www.mysite.com/people_), aber stattdessen gibt es nichts zurück, was sehr Fehler auf hohem Niveau in der Funktionsweise des Routings.

Große Routing-Bibliotheken haben dafür bereits Vorkehrungen getroffen - wie isExact im Fall von React Router

Obwohl ich verstehe, woher du kommst, denke ich immer noch, dass dies ein eklatantes Problem ist, das auf den Punkt gebracht werden muss

Dies ist auch bei next export völlig unvermeidlich

Es gibt das Problem, dass sich Leute versehentlich verlinken...

Es gibt das Problem, dass Leute versehentlich auf eine nicht vorhandene URL verlinken. Warum sollte /some/path/ weniger nicht vorhanden sein als /some/path/dhgfiuwo ?

es gibt auch die semantische Bedeutung der URL

Dies ist sehr subjektiv, soweit ich weiß, gibt es keine Spezifikation, die den semantischen Unterschied vorschreibt. Gemäß der URL- Spezifikation gelten mit und ohne abschließenden Schrägstrich als unterschiedliche URLs. Ich kann mir mindestens 7 verschiedene gültige Verhaltensweisen vorstellen:

  • mit und ohne haben ganz unterschiedliche Inhalte
  • mit tut 404, ohne löst
  • mit beschließt, ohne tut 404
  • mit Weiterleitungen nach ohne
  • ohne Weiterleitungen zu mit
  • mit und ohne gleichen Inhalt mit kanonischem Hinweis auf mit
  • mit und ohne gleichen Inhalt mit kanonischem Hinweis auf ohne

Kombinieren Sie dies mit der Möglichkeit, entweder /pages/some-page.js und /pages/some-page/index.js (oder beides) zu haben.

Sollte next.js all diese Anwendungsfälle unterstützen? Sollte es ein Standardverhalten auswählen?

Ich bin nicht dagegen, aber nachdem ich zuvor versucht habe, dies zu implementieren, denke ich einfach, dass es mehr Nuancen hat, als es zunächst scheint.

Es gibt das Problem, dass Leute versehentlich auf eine nicht vorhandene URL verlinken, warum sollte /some/path/ weniger nicht vorhanden sein als /some/path/dhgfiuwo?

Für den Fall /some/path/dhgfiuwo - die Leute erwarten, dass die Route dhgfiuwo fehlt. (Beispiel: Benutzer dhgfiuwo kann im System nicht gefunden werden und users/dhgfiuwo ist falsch. Die Abwesenheit eines Benutzers im System ist ein erwartetes Ereignis.)
Für den Fall /some/path/ - Leute erwarten, dass dieser Pfad derselbe ist wie /some/path , da dies das Standardverhalten auf anderen Sites ist.
Daher ist ein Fehler in would/some/path/ weniger nicht vorhanden als in /some/path/dhgfiuwo .

Ich sehe, dass andere ihre Lösungen gepostet haben, also wollte ich meinen Ansatz teilen: https://github.com/DevSpeak/next-trailingslash

Einige Verbesserungen und Unterstützung für dynamische geroutete Seiten in Bezug auf ?= sollten IMO durchgeführt werden, aber dies dient nur der Veranschaulichung der Idee.

Für eine schnelle Lösung können Sie die Standardseite _error ersetzen (wie im Beispiel von @DevSpeak ).

@DevSpeak , ich würde ein paar Änderungen für Ihr

Folgendes verwende ich in einem Typescript-Projekt (basierend auf der integrierten Fehlerseite):

/pages/_error.tsx (oder entfernen Sie die TypeScript-Typen und nennen Sie sie /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
  }
};

Beachten Sie, dass dies auch einen Fehler protokolliert, wenn die Seite aufgerufen wird, sodass Sie Ihre Protokolle überprüfen können, um Links/andere Probleme zu beheben.

@DevSpeak @bitjson Danke für deine Vorschläge. Das ist sicherlich eine Möglichkeit, dies zu tun, und löst das Problem sicherlich sehr gut. Aber wenn man bedenkt, dass _error.jsx ursprünglich dafür gedacht ist, _Fehler_ zu behandeln und nicht die Routing-Logik, ist es meiner Meinung nach hackig und ziemlich deklarativ, all diesen Code dort zu haben. Zu erwarten, dass jeder Benutzer dies in jeder Codebasis tut, sollte keine Voraussetzung sein - dies sollte sofort einsatzbereit sein. = Ich bin der Meinung, dass diese Bedingung in die Routing-Logik eingebaut werden muss, mit einer Option zum Opt-out wie bei React Router.

@NathanielHill

Dies ist auch beim nächsten Export völlig unvermeidbar

Warten Sie - ich habe beim Lesen der Dokumentation verstanden, dass es einen bestimmten Code gibt, um die Bedingung mit dem abschließenden Schrägstrich zu behandeln:

Die Seiten werden als html-Dateien exportiert, dh /about wird zu /about.html.

Es ist möglich, Next.js so zu konfigurieren, dass Seiten als index.html-Dateien exportiert werden und nachgestellte Schrägstriche erforderlich sind, dh /about wird zu /about/index.html und ist über /about/ routbar. Dies war das Standardverhalten vor Next.js 9. Sie können die folgende next.config.js verwenden, um zu diesem Verhalten zurückzukehren:

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

Auch wenn dies nicht wirklich eine Option für den statischen HTML-Export über next export , stimme ich der Logik nicht zu, dass nur weil Next diese (erstaunliche) Funktion unterstützt, andere Modi darunter leiden müssen (ich tue es nicht) kennen die Nutzungsstatistiken, aber ich würde davon ausgehen, dass mehr Leute den regulären Modus mit Server im Gegensatz zum Serverless-Modus verwenden), insbesondere wenn dies bekannt ist, dass dies ein so häufiger Anwendungsfall ist

Zu Ihrer Information: Es gibt einen RFC, der Sie interessieren könnte https://github.com/zeit/next.js/issues/9081

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

@Janpot Liebe es - das bringt uns auf halbem Weg, dh wir haben eine Art Unterstützung für Weiterleitungen, ohne einen benutzerdefinierten Server erstellen zu müssen. Dies wird immer noch zwingend erforderlich sein, da er für jede Route, die der Benutzer hinzufügt, eine Umleitung in next.config.js einrichten müsste - oder vielleicht könnten wir einfach eine Regex verwenden, um alle Fälle abzufangen, wie

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

In jedem Fall, wenn das Kernteam diesem RFC Priorität einräumt, würde ich dringend empfehlen, noch einen Schritt weiter zu gehen und es zu einer _optieren_ kann

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

Alles in allem denke ich, dass dies ein großer Schritt nach vorne ist - gute Sachen @Timer!! 🔥

@nik-john Der Pfad, den ich in "/:path*/" sollte alle abfangen ( :path fängt ein einzelnes Segment ab, * fängt 0 bis n Instanzen ab.)

@Janpot Ah my bad 🤦‍♂ Ich vermute, wir müssten auch alle nachgestellten Abfrageparameter in diesem Regex berücksichtigen

Auch zum zweiten Teil bleibe ich noch stehen:

In jedem Fall, wenn das Kernteam diesem RFC Priorität einräumt, würde ich dringend empfehlen, noch einen Schritt weiter zu gehen und es zu einer integrierten Konfiguration zu machen, die man so deaktivieren kann

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

Wenn Sie einen benutzerdefinierten Server verwenden und strikte Routen ignorieren möchten, können Sie anstelle einer Umleitung auch einen benutzerdefinierten Routenhandler verwenden.

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

Auf diese Weise können wir sowohl /path als auch /path/ und auf dieselbe Seite auflösen.

Oauth-Verbundanbieter erfordern häufig nachlaufende Schrägstriche, sodass dieses Verhalten einen einfachen Ablauf sehr kompliziert macht. Was ist die technische Herausforderung bei der Umsetzung dieses Verhaltens? Oder ist das eine Designentscheidung von next?

Ich habe es in diesem Thread bisher nicht erwähnt gesehen, aber ich habe dieses Problem nicht nach der Bereitstellung mit Now, ich habe es nur lokal beim Testen mit 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();
  }
};

Habe dies von Stackoverflow und funktioniert perfekt. Diese Lösung funktioniert mit Express.

@GaneshKathar Ich sehe nicht, wie das funktioniert, wenn Sie Next.js nicht mit Express verwenden

Ich denke, darauf können wir uns nicht einigen und es sollte konfigurierbar sein.

Ich möchte eigentlich immer den abschließenden Schrägstrich, die relativen URLs sind einfacher zu begründen, wenn alle Seiten mit einem abschließenden Schrägstrich enden.

Zum Beispiel macht es keinen Sinn, dass /about/index.tsx /about anstelle von /about/ , aber jetzt verständlich, da next ohne nachgestellten Schrägstrich erwartet. Wenn alle Seiten mit einem Schrägstrich enden würden, könnten Seiten in Zukunft Unterseiten enthalten, was meiner Meinung nach für Seiten erweiterbarer ist.

Das Erstellen relativer Links innerhalb der /about/index.tsx Datei ist jetzt umständlich. Wenn Sie einen Link ./mysubpage/ erstellen, zeigt er stattdessen auf das Stammverzeichnis der Site. Dadurch sind die Unterseiten nicht umbenennbar. Ich kann kein Verzeichnis /about/ voller Seiten erstellen, die ich einfach umbenennen kann, weil ich auch die relativen Links bearbeiten sollte.

Auch die wget -r Site liefert vernünftige Ergebnisse, da sie immer nachgestellte Schrägstriche hat und index.html-Dateien erzeugt.

Das Ändern dieser Einstellung ist jedoch eine massive Änderung, da alle Sites nicht nachgestellte Schrägstriche erwarten, daher muss sie konfigurierbar sein.

Ich verwende Version 9 und dieses Problem ist immer noch nicht behoben

Ich konnte es zum Laufen bringen, indem ich etwas wie das Folgende auf meinem next.config.js :

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

  return paths;
},

Der Zugriff auf /authors ergibt 302, der location auf /authors/ . Ich teste mit http-serve , bin mir nicht sicher, ob dieses Verhalten serverspezifisch ist.

Als ich mit diesem Problem konfrontiert war, kam ich auf diese Lösung

auf meiner _error.js Seite

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 };
}

ist es eine gute Möglichkeit, das Problem zu lösen?

Wie wäre es damit?

Seiten/_app.jsx

```React aus 'react' importieren;
App von 'next/app' importieren;

Standardklasse exportieren MyApp erweitert App {
render() {
const { Komponente, 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 (asPath.length > 1 && asPath.endsWith('/')) {
  const urlWithoutEndingSlash = asPath.replace(/\/*$/gim, '');

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

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

}
}
```

@cnblackxp danke für den Vorschlag. Das hat mir geholfen. So habe ich es implementiert, um das Standardverhalten für nicht nachlaufende 404s beizubehalten (dh ich exportiere einfach die Standardimplementierung von 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 };
};

Ja, das wird @cansin tun, solange nichts anderes entschieden wird :) Prost!

Kleine Verbesserung der Problemumgehung von

  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('/')) {

Der einzige Unterschied besteht darin, zu überprüfen, ob der Statuscode 404 ist. Bei der Verwendung von Link für dynamische Routen sind Probleme aufgetreten, bei denen sie aufgrund der Umleitung immer auf dem Server gerendert wurden. Wenn das clientseitige Routing funktionieren soll, können Sie dem Link href Requisite keinen abschließenden Schrägstrich hinzufügen, aber dann müssen Sie in diesem Fall sicherstellen, dass Sie nicht umleiten.

Das Problem bei der Implementierung einer Problemumgehung in der Error-Komponente besteht darin, dass sie einen Benachrichtigungsfehler in der Entwicklung auslöst, der mich stört. Einige Verbesserungen an meiner vorherigen clientseitigen Weiterleitung:

Was verbessert wurde, ist, dass jetzt next/router auf der Clientseite verwendet wird und der URL-Ersatz ohne Neuladen erfolgt.

Seiten/_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} />;
  }
}

Danke auch an @mbrowne für 404 Fix :)

Habe die Lösung von

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 Können Sie Beispiele für eine URL mit angeben ? Denken Sie an die Linie von /blog/?123 ?

Vielen Dank an alle für einige Ihrer Workarounds oben. Sie arbeiteten!

Haben wir jedoch eine offizielle Möglichkeit, dieses Problem vom Next-Team zu beheben? Dieses Thema gibt es schon seit Jahren.

Verzeichnisseiten werden beim nächsten Export nicht mit nachgestelltem Schrägstrich bereitgestellt

@pinpointcoder Können Sie Beispiele für eine URL mit angeben ? Denken Sie an die Linie von /blog/?123 ?

@coodoo Nicht er, aber ja, das passiert leider

Da wir im Begriff sind, Tonnen von Blog-Posts zu migrieren, deren kanonische URL derzeit einen nachgestellten Schrägstrich enthält, ist dies im Moment ein Riesenschmerz.

Ich habe mich entschieden, einen benutzerdefinierten Server zu implementieren, um dies zu handhaben, und es stellte sich heraus, dass es einfach ist, und Sie können immer noch das dateibasierte Routing-System von next.js verwenden. Auf diese Weise können Sie die URL, die next.js sieht, umschreiben und die echte URL hat immer noch einen Schrägstrich am Ende:

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}`)
    })
})

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

@mbrowne Wir haben eigentlich eine Reihe von Gründen, einen benutzerdefinierten Server zu verwenden, aber die Hauptsache, die mich bisher davon

Wir brauchen derzeit keine automatische statische Optimierung für unsere App, daher habe ich mich damit nicht befasst.

Ich verwende auch einen benutzerdefinierten Server, aber wenn Sie eine geänderte URL (ohne führenden Schrägstrich) an handle , sieht SSR eine andere URL von der Clientseite.
Ich würde es vorziehen, dass der next Router die URL mit dem führenden Schrägstrich ohne diese bösen Hacks abgleicht.

2020 und dieser Fehler tritt immer noch auf. Nicht zu glauben

Dies ist ein schlimmer Fehler, der wirklich behoben werden muss. /products funktioniert, aber /products/ nicht. Mit diesem Link

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

Ich bekomme

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

Wenn ich jedoch den Link auf /products/ zeige, den Link besuche und die Seite während der Entwicklung aktualisiere, erhalte ich einen 404. Dies ist eine ziemlich schmerzhafte Entwicklungserfahrung.

Dieses Problem wurde erstmals vor 1,5 Jahren gemeldet; Können wir bitte eine offizielle Lösung bekommen? Es ist immer noch in 9.3.4 vorhanden.

Ich habe aus SEO-Gründen eine Umleitung zu einer nicht nachgestellten Slash-URL vorgenommen, anstatt Inhalte anzuzeigen.

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}`)
  })
})

Für SEO kann rel="canonical" helfen, muss aber dieses 404-Problem noch beheben.

Dies ist ein schlimmer Fehler, der wirklich behoben werden muss. /products funktioniert, aber /products/ nicht. Mit diesem Link

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

Ich bekomme

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

Wenn ich jedoch den Link auf /products/ zeige, den Link besuche und die Seite während der Entwicklung aktualisiere, erhalte ich einen 404. Dies ist eine ziemlich schmerzhafte Entwicklungserfahrung.

Dieses Problem wurde erstmals vor 1,5 Jahren gemeldet; Können wir bitte eine offizielle Lösung bekommen? Es ist immer noch in 9.3.4 vorhanden.

Dieses Problem bekomme ich derzeit auch.

So habe ich es behoben, https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3

So habe ich es behoben, https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3

Vielen Dank, obwohl dies bei der lokalen Entwicklung einen benutzerdefinierten Server erfordert, der nicht erforderlich sein sollte.

@timneutkens Kann eine Lösung für dieses Problem in den Entwicklungsplan eingearbeitet werden?

Noch wichtiger ist, dass die Umleitungslösung nicht für diejenigen funktioniert, die Websites verwalten, deren Bereich bereits eingerichtet ist, um einen Schrägstrich hinzuzufügen, anstatt einen in der Produktion zu entfernen. Ich denke nicht, dass das Framework diese Wahl willkürlich diktieren sollte.

Die Lösung von @AlexSapoznikov hat für uns mit Netlify (das standardmäßig einen nachgestellten Schrägstrich hinzufügt) gut funktioniert. Hier ist eine erweiterte Version, die Unterstützung für Abfrageparameter hinzufügt:

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} />;
  }
}

Ich entschuldige mich, weil ich ein Next JS-Neuling bin, obwohl ich Erfahrung in der Softwareentwicklung auf anderen SDKs und Plattformen habe.

Ich glaube, dieser "Fehler" hat mich am meisten überrascht. Für mich verstieß es gegen das "Prinzip des geringsten Erstaunens". Ich habe einfach erwartet, dass /about/ und /about gleich funktionieren, da ich eine index.tsx in meinem /pages/about/-Ordner abgelegt habe.

Ich begann in den späten 1990er Jahren mit der Erstellung von Websites mit HTML-FTP auf meinem Server und wechselte später zu PHP & Apache und schließlich zu Java-Servern. Jetzt habe ich mich auf mobile Apps spezialisiert. Es fühlt sich für mich nur seltsam an, dass dieses Verhalten nicht die Standardeinstellung ist und ich eine benutzerdefinierte Serverseite schreiben müsste, um es auf meinem Entwicklungsserver zu beheben.

Ich habe vor, einen statischen Export durchzuführen, damit er nicht in der Produktion angezeigt wird, selbst wenn ich den benutzerdefinierten Server nicht schreibe. Es macht jedoch die Entwicklung und das Debuggen etwas nerviger.

Können wir ein Flag "next dev" erhalten, das dies behebt, damit wir faulen Entwickler keine zusätzliche Routing-Logik nur für die Entwicklungs- / Debug-Zeit schreiben müssen?

Vielen Dank!

ps: Ja, ich weiß, dass /about und /about/ völlig unterschiedliche URLs sind. Ich war gerade wirklich verwirrt, als ich eine index.tsx Datei in meinen /pages/about/ Ordner lege und entdeckte, dass sie nur mit dem /about Pfad funktioniert , /about/ . Ich wäre weniger überrascht, wenn es umgekehrt wäre.

pps: Es war besonders verwirrend, wenn ich eine <Link></Link> Komponente habe, die auf /about/ und sie wie erwartet funktioniert. Wenn ich dann in meinem Browser auf Aktualisieren klicke, werden sofort 404-Fehler angezeigt, obwohl sich die URL nicht geändert hat. Das war sehr überraschend. :-D

Aber warte, es wird schlimmer! Wir haben eine benutzerdefinierte checkForTrailingSlash Funktion in _error.js hinzugefügt, die den abschließenden Schrägstrich entfernt und umleitet. Dies funktionierte eine Weile gut, bis wir (endlich) eine benutzerdefinierte 404-Seite hinzugefügt haben und festgestellt haben, dass Next.js mit einer benutzerdefinierten 404-Seite Error vollständig umgeht . Dies bedeutet, dass keine Ihrer benutzerdefinierten Logiken in Error.getInitialProps mehr funktioniert - einschließlich einer Überprüfung auf nachgestellte Schrägstriche.

Ich schätze, ich werde die _app.js anderen erwähnte

Die Lösung von @AlexSapoznikov hat für uns mit Netlify (das standardmäßig einen nachgestellten Schrägstrich hinzufügt) gut funktioniert. Hier ist eine erweiterte Version, die Unterstützung für Abfrageparameter hinzufügt:

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} />;
  }
}

Ihr Codebeispiel enthält einen kritischen Fehler: Anforderungen an die Indexroute mit einem Abfrageparameter lösen einen Fehler aus, da Sie am Ende versuchen, nur die Abfragezeichenfolge als asPath an Next.js zu übergeben.

Dies behebt es:

  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;
      }
    }
  }

Damit dies mit SSR funktioniert, musste ich der @pjaws & @AlexSapoznikov- Lösung Folgendes hinzufügen:

  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)
        : {},
    };
  }

Wahrscheinlich ist es eine gute Idee, diese Funktionalität irgendwie in eine Funktion zu verallgemeinern, die sowohl während SSR als auch während CSR funktioniert, und sie an beiden Stellen aufzurufen ( getInitialProps und render ).

von

Dies wird behoben, aber der Titel falsch. Hmm
image

@AlexSapoznikov @pjaws

Ihre Lösung versetzt uns in eine Endlosschleife:

  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;
      }
    }
  }

Kontext

Aus Gründen, auf die wir keinen Einfluss haben, müssen wir die Option exportTrailingSlash in next.config.js .

Wir möchten einen Link zu einer anderen Seite haben, aber der Link soll /somepage?param=whatever .

Es scheint, dass der nächste Link dies in /somepage/?param=whatever konvertiert und wir erhalten die Seite nicht gefunden.

Verwenden Sie die obige Lösung, um das params-Problem zu lösen, aber wenn Sie dann zu einer bereitgestellten Seite wie /somepage/ , tritt sie in eine Endlosschleife ein.

Ich denke, @ronyeh hat hier einen wirklich guten Punkt gemacht, also möchte ich wirklich eine offizielle Lösung für dieses Problem :(

Damit dies mit SSR funktioniert, musste ich der @pjaws & @AlexSapoznikov- Lösung Folgendes hinzufügen:

  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)
        : {},
    };
  }

Wahrscheinlich ist es eine gute Idee, diese Funktionalität irgendwie in eine Funktion zu verallgemeinern, die sowohl während SSR als auch während CSR funktioniert, und sie an beiden Stellen aufzurufen ( getInitialProps und render ).

Dies hat für Seiten mit getServerSideProps funktioniert und jetzt geben URLs mit nachgestellten Schrägstrichen dieselbe Seite ohne 404 zurück.
Aber es gibt einen Fehler, ich habe einige Seiten, die dynamische Routen und getStaticPaths verwenden, ich kann getServerSideProps nicht für sie verwenden und wenn diese dynamischen Routen mit einem abschließenden Schrägstrich durchsucht werden, geben sie zuerst einen 404 zurück und leiten dann auf die Seite um .

Ich arbeite mit einem /api/test-Ordner

  • Seiten/api/test.tsx
  • Seiten/Api/Test/[id].tsx

es funktioniert für

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

Und ich habe gerade festgestellt, dass das nicht funktioniert

  • GET /api/test/

Ich bin mir nicht sicher, ob dies ein verwandtes Problem ist
P/D exportTrailingSlash = true löst es nicht

Dies ist ein sehr altes Problem, gibt es einen Grund, warum es so lange nicht behandelt wird?

Ich bin mir nicht sicher, was nicht mehr funktioniert.

Mein Verständnis ist, dass die Anforderungen wie folgt sind:

| | exportTrailingSlash: false | exportTrailingSlash: true |
|-------------------------|---------------------- ------|---------------------------|
| URL endet mit / | Sollte nicht funktionieren | Sollte funktionieren |
| URL endet nicht mit / | Sollte funktionieren | Sollte nicht funktionieren |

Dies funktioniert wie erwartet, wenn:

  • Lokal verwenden wir exportTrailingSlash: false
  • Für Bereitstellungen (Produktions-Builds) verwenden wir exportTrailingSlash: true und ein nginx konvertiert url/ in url/index.html

Von dem, was ich in @andrescabana86 sehen kann, funktioniert das dort, wo es nicht funktionieren sollte: GET /api/test/123/ wohingegen GET /api/test/ nicht funktioniert und nicht sollte.

@Izhaki Ich habe beides ausprobiert, auf Prod

  • GET /api/test/

und ich benutze exportTrailingSlash: true

Ich kann versuchen, ein öffentliches Repo zu erstellen, wenn Sie möchten, vielleicht habe ich etwas in der Mitte vergessen.

Danke für deine Antworten

@andrescabana86 Ich bin mir nicht sicher, wie sehr ein öffentliches Repo hier hilft - dies kann durchaus eine Konfiguration auf dem Server sein, auf dem Sie bereitstellen.

Wir testen unsere Produktions-Builds (mit exportTrailingSlash: true ) lokal mit diesem Skript in package.json :

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

Bitte lassen Sie mich wissen, ob es funktioniert, in Ihrem Browser zu http://localhost:5000/api/test/ .

(Beachten Sie, dass $(pwd) auf Mac/Linux ist - siehe dies für Windows)

@Izhaki das Problem bestand darin, dass (wie der erste Bericht vorschlägt) "der nachgestellte Schrägstrich im Link für die legitime Seite für die clientseitige Navigation funktioniert, aber bei der harten Aktualisierung (ssr) zu nicht gefundenem Bundle und 404 führt". Es gab also eine Diskrepanz zwischen dem Verhalten einer clientseitigen Routenänderung und einer harten Aktualisierung. Ich bin mir nicht sicher, ob das Problem mit der neuesten Version von Next.js weiterhin besteht. Sobald ich es getestet habe, kann ich mich hier wieder melden.

Gerade mit 9.4.1 und exportTrailingSlash: true getestet.

Der Wechsel zu http://localhost:6500/admin/ gibt 404 bei lokaler Entwicklung zurück.

Aber der gleiche Pfad funktioniert beim Exportieren.

Beachten Sie, dass exportTrailingSlash hinweist, dass dies nur für _Exporte_ gilt.

Was wir tun, ist:

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

Das heißt, die Dinge funktionieren wie gewünscht, wenn wir lokal entwickeln. Und funktionieren ordnungsgemäß, wenn sie bereitgestellt werden (über Export).

Ist das nicht die richtige und praktikable Lösung dafür?

Wenn eine URL nicht in der Entwicklung, aber in der Produktion funktioniert, verstößt das nicht gegen das Prinzip der geringsten Überraschung? Ich denke, das sollte immer noch als Bug angesehen werden.

^ Allerdings bin ich mir ziemlich sicher, dass es zuvor in der Produktion ein widersprüchliches Verhalten zwischen einer Seitenaktualisierung und einem router.push-Ereignis gab. Ich weiß nicht, ob es noch so ist.

@andrescabana86 @Izhaki exportTrailingSlash hat nichts damit zu tun . Diese Option bezieht sich auf den statischen Export von Next.js-Anwendungen. Bei true wird example/index.html generiert, während bei false example.html generiert wird. Mein Verständnis ist, dass exportTrailingSlash nichts mit dem Entwicklungsmodus zu tun hat.

Ich denke, eine Quelle der Verwirrung ist, dass next.js einen nachgestellten Schrägstrich zu Links hinzufügt, wenn Sie exportTrailingSlash haben. Das passiert auch in der Entwicklung Ich bin mir nicht sicher, ob es das tun sollte? Aber wie auch immer, es geht hier nicht nur um example/index.html vs. example.html - es müssen auch Links geändert werden.

Wenn eine URL nicht in der Entwicklung, aber in der Produktion funktioniert, verstößt das nicht gegen das Prinzip der geringsten Überraschung? Ich denke, das sollte immer noch als Bug angesehen werden.

Ich kann mich irren, aber die Option exportTrailingSlash war für nginx-Server, die nicht für die Bereitstellung von /something.html konfiguriert sind, wenn die URL /something lautet.

Dies ist beim nächsten Server, der für die lokale Entwicklung verwendet wird, nicht der Fall. Was also funktioniert und was nicht, hängt davon ab, was Ihrer App dient.

Sie können argumentieren, dass, wenn exportTrailingSlash wahr ist, der nächste Server Routen unterstützen sollte, die mit einem abschließenden Schrägstrich enden (obwohl dies export in exportTrailingSlash etwas irrelevant macht).

FWIW daran wird bereits gearbeitet #13333

Ich bin kein sehr erfahrener Programmierer und verwende Next.js hauptsächlich für mehrseitige Landungen. Anscheinend habe ich die folgende Problemumgehung fast die ganze Zeit verwendet, ohne ihre Wirkung zu kennen. Hier ist eine abgespeckte Version davon:

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

In meinem Fall ist der Code etwas komplizierter, da ich ihn verwende, um zusätzliche statische URL-Präfixe usw. zu unterstützen. Aber diese abgespeckte Version scheint für das besprochene Problem gut zu funktionieren, unabhängig von den exportTrailingSlash Einstellung und ihre Auswirkung auf Link s. ZB URLs /about und /about/ funktionieren gut.

In der gegenwärtigen Form ahmt es im Wesentlichen das native Routing von Next.js nach. Der Nachteil: Es erfordert benutzerdefiniertes server.js , und Sie müssen es für "tiefere" URLs (mit zusätzlichen "Unterordnern") manuell unterstützen, zB /company/about/ . Aber es scheint eine relativ einfache Lösung für diejenigen zu sein, die bereits benutzerdefinierte server.js in ihrem Projekt verwenden.

Damit dies mit SSR funktioniert, musste ich der @pjaws & @AlexSapoznikov- Lösung Folgendes hinzufügen:

  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)
        : {},
    };
  }

Wahrscheinlich ist es eine gute Idee, diese Funktionalität irgendwie in eine Funktion zu verallgemeinern, die sowohl während SSR als auch während CSR funktioniert, und sie an beiden Stellen aufzurufen ( getInitialProps und render ).

Dies hat für Seiten mit getServerSideProps funktioniert und jetzt geben URLs mit nachgestellten Schrägstrichen dieselbe Seite ohne 404 zurück.
Aber es gibt einen Fehler, ich habe einige Seiten, die dynamische Routen und getStaticPaths verwenden, ich kann getServerSideProps nicht für sie verwenden und wenn diese dynamischen Routen mit einem abschließenden Schrägstrich durchsucht werden, geben sie zuerst einen 404 zurück und leiten dann auf die Seite um .

@gauravkrp Dies ist tatsächlich eine äußerst wichtige Ergänzung, da die @AlexSapoznikov- Lösung tatsächlich immer noch einen 404 für die Seite an Google

Ich denke auch, dass das Einfügen in getInitialProps einfach rundherum funktionieren sollte, und der Teil innerhalb der Hauptfunktion ist an dieser Stelle unnötig. Der größte Vorbehalt hier ist, dass Sie dadurch die automatische statische Optimierung verlieren – wahrscheinlich jedoch besser als ein Haufen 404er.

Zum Teilen...

Mein Projekt ist Express + Next.js .
express 4.17.1
next 9.4.5-canary.7

Wenn Entwicklung

Dynamische Laufzeit

// 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));

Wenn Produktion

Statischer Export
next build und 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/
});

Abschließend

Ich habe kein Problem bei der Umleitung, indem ich auf die Client-App klicke.
auch Hard Refresh arbeitet an static route .

Aber es wird 404 bei einer harten Aktualisierung auf dynamic route ,
wie /album/[id].jsx oder /album/123 ,
Ich freue mich daher, dieses Problem mithilfe des folgenden Mechanismus zu beheben.

z.B
Wenn Sie 404 bei /album/123 treffen,
Server sollte weiterhin HTML-Inhalte bereitstellen,
Browser lädt die Seite weiterhin ohne Probleme,
Wenn Next.js hochfährt, sollte next/router automatisch damit umgehen.

Gibt es eine vorübergehende Lösung für dieses Problem in der Produktion?

Wir sind dabei, ein Feature zu veröffentlichen, das dies behebt - einen Tag oder so!

Gibt es eine vorübergehende Lösung für dieses Problem in der Produktion?

Es gibt viele in diesem Thread, aber ich verwende derzeit das, was @gauravkrp kürzlich gepostet hat, und es funktioniert gut für mich.

Hier können Sie die PR verfolgen: #13333

Dies wurde jetzt in next@^9.4.5-canary.17 gelöst!

Wie lange dauert es, bis das Feature vom Canary zum Master gelangt?

Dies wurde jetzt in next@^9.4.5-canary.17 gelöst!

Und wie genau wird es gelöst? einfach den nachgestellten Schrägstrich entfernen? Wenn ich auf " www.site.com/help/ " zugreife, www.site.com/help " , können wir dort eine Option haben, den Schrägstrich zu lassen? Wenn Sie auf " www.site.com/help " www.site.com/help/ "

@Valnexus siehe #13333, es enthält eine experimentelle Option:

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

Wie lange dauert es, bis das Feature vom Canary zum Master gelangt?

Wenn es fertig ist. Es gibt noch Randfälle im Handling, die gelöst werden. Sobald diese behoben wurden, kann es in den Stall gehen.

@timneutkens @Janpot

Ich habe den neuesten nächsten Kanarienvogel (9.4.5-canary.27) ausprobiert, aber wenn ich eine test Seite erstelle und auf www.example/test/ zugreife, wird er zu www.example/test umgeleitet
Ich denke, das Verhalten sollte in beiden Fällen gleich sein.

Beim Zugriff auf www.example/test/ sollte es auf www.example/test/ bleiben.
Beim Zugriff auf www.example/test sollte es auf www.example/test bleiben.
Ich teste es auf Nuxt.js, es funktioniert das gleiche Verhalten, das ich oben beschrieben habe.

Ich denke, das Verhalten sollte in beiden Fällen gleich sein.

Der Grund für eine Weiterleitung besteht darin, sicherzustellen, dass Suchmaschinen keine doppelten Inhalte sehen. Was ist Ihr genauer Anwendungsfall?

Ich verstehe nicht, warum es ein geschlossenes Problem ist, wenn es noch nicht zu einer stabilen Version zusammengeführt wurde. Wenn ich das richtig verstanden habe, ist es vorerst nur in der Canary-Version behoben, oder?

Issues werden geschlossen, wenn die zugehörigen Pull-Requests landen, da sie für die sofortige Verwendung auf Canary verfügbar sind. Wenn Sie diese Funktion benötigen, aktualisieren Sie bitte auf den Canary-Kanal.

Hört sich gut an. Danke, @Timer!

@Janpot Ich habe gesehen, dass https://github.com/issues/ und https://github.com/issues ohne Umleitung auf das gleiche Verhalten zugreifen können.

https://twitter.com/explore/ und https://twitter.com/explore , auch dieses hier.

Wenn es ein Problem mit Suchmaschinen gibt, warum haben Github und Twitter es nicht behoben?
Ich denke, es ist das Standardverhalten für jede Website.

Es gibt keinen speziellen Anwendungsfall, nur meiner Meinung nach sollte es so funktionieren.

Wenn es ein Problem mit Suchmaschinen gibt, warum haben Github und Twitter es nicht behoben?

@armspkt Es ist kein Problem, da es mehrere Möglichkeiten gibt, es zu lösen. Zum Beispiel verwendet Twitter das <link rel="canonical"> Attribut, um Such-Bots mitzuteilen, welche Seite sie crawlen sollen, und andere Versionen sollten als dupliziert markiert werden.

Die Weiterleitung ist also eine praktikable Möglichkeit, SEO auf Ihrer Website zu machen. Weitere Informationen können Sie hier lesen.

@ziserman Wenn wir mehrere Möglichkeiten haben, das beibehalten .

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

Nuxtjs hat mehrere Optionen zur Auswahl (undefiniert, wahr, falsch)

Sollte Nextjs auch mehrere Optionen zur Auswahl haben?

Der Grund für eine Weiterleitung besteht darin, sicherzustellen, dass Suchmaschinen keine doppelten Inhalte sehen. Was ist Ihr genauer Anwendungsfall?

@Janpot Unsere API hat an vielen Stellen nachgestellte Schrägstriche. Die neueste Version wirft viele 404 im Backend auf, da die URLs mit abschließenden Schrägstrichen (/api/test/ -> /api/test) nicht übereinstimmen

Ich weiß nicht, ob es für alle funktioniert, aber ich habe diese Lösung gefunden, die für mich funktioniert. Fügen Sie es in die Datei _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 Ich habe Ihren Kommentar minimiert, da er in einer Next.js-App zu schwerwiegenden Leistungseinbußen führt.

Die neueste Version von next@canary behebt diesen Fehler, bitte aktualisieren Sie stattdessen!

@mlbonniec Ich habe Ihren Kommentar minimiert, da er in einer Next.js-App zu schwerwiegenden Leistungseinbußen führt.

Die neueste Version von next@canary behebt diesen Fehler, bitte aktualisieren Sie stattdessen!

Kein Problem!
Ich habe jedoch früher aktualisiert, und das hat das Problem nicht gelöst.
Mit npm update

Wenn der neueste Canary von Next.js den Fehler nicht für Sie behebt, öffnen Sie bitte ein neues Problem, damit wir uns das ansehen können. 🙏

Kurze Frage, wie gehen Projekte mit next export mit dieser Änderung um? Indem Sie für jede Seite für den abschließenden Schrägstrich eine völlig neue Seite erstellen? Ich glaube nicht, dass eine exportierte App HTTP-Weiterleitungen (oder Umschreibungen) angeben kann.

Projekte, die next export , haben alle ihre <Link /> s auf der Clientseite korrekt aktualisiert, aber die serverseitige Umleitung erfordert eine manuelle Konfiguration. Projekte, die mit dem serverlosen Ziel oder next start bereitgestellt werden, konfigurieren diese Einstellungen automatisch.

@Timer, sobald dies eine vollständige Veröffentlichung erreicht, müssen wir dann immer noch die experimentelle Option verwenden?

@Timer, sobald dies eine vollständige Veröffentlichung erreicht, müssen wir dann immer noch die experimentelle Option verwenden?

Nein, wäre nur so verfügbar wie es ist.

Ich denke, die Option trailingSlash funktioniert nicht für next export ? Was ist der beste Weg, um /page/ zu /page (oder umgekehrt) umzuleiten, sagen wir, auf Github-Seiten?

Ich denke, die Option trailingSlash funktioniert nicht für next export ? Was ist der beste Weg, um /page/ zu /page (oder umgekehrt) umzuleiten, sagen wir, auf Github-Seiten?

Soweit mir bekannt ist, haben Github-Seiten keine Weiterleitungsfunktion. Dies funktioniert jedoch sofort auf vercel.com, das auch für Hobbyprojekte kostenlos ist (wie Github-Seiten).

Projekte, die next export , haben alle ihre <Link /> s auf der Clientseite korrekt aktualisiert, aber die serverseitige Umleitung erfordert eine manuelle Konfiguration. Projekte, die mit dem serverlosen Ziel oder next start bereitgestellt werden, konfigurieren diese Einstellungen automatisch.

Hallo @Timer Kannst du mehr erklären? Wie kann ich manuell konfigurieren? Also hier ist meine Situation. Auf meiner Website verwende ich next-i18next . Nachdem ich mit next build && next export bereitgestellt habe, funktionieren alle internen Links, aber wenn die URL manuell eingegeben wird, funktioniert KEINER von ihnen und führt zu einem 404-Fehler. Von hier aus habe ich beschlossen, trailingSlash:true und so wird die manuelle Eingabe von /pricing jetzt funktionieren, aber /zh/pricing führt zu 404-Fehlern.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen