Next.js: barra diagonal en el enlace para la página legítima funciona para la navegación del lado del cliente, pero conduce a un paquete no encontrado y 404 en la actualización completa (ssr)

Creado en 20 sept. 2018  ·  119Comentarios  ·  Fuente: vercel/next.js

barra diagonal en el enlace para la página legítima funciona para la navegación del lado del cliente, pero conduce a un paquete no encontrado y 404 en la actualización completa (ssr)

Informe de error

Describe el error

avíseme si el título necesita más aclaraciones.

Todos los problemas relevantes se han cerrado con el razonamiento de que se han solucionado en 6-canary (creo que no lo es) o mediante un servicio mejorado (que es cierto solo en quizás la exportación estática de producción).

Estoy reescribiendo mi blog existente a next.js y anteriormente usé barras inclinadas. Los últimos serve pueden ayudar con eso una vez que cree mi blog impulsado por next.js. Pero para arreglar dev env necesito deshacerme de las barras inclinadas y utilizar 301 Moved Permanently en prod; o vivir con soporte de barra diagonal rota en dev.

Reproducir

Aquí hay un caso mínimo reproducible (el enlace al repositorio de reproducción se encuentra debajo del fragmento):

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

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

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

Repositorio mínimo reproducible https://github.com/iamstarkov/next.js-trailing-slash-bug-demo

  1. clonar repositorio git clone https://github.com/iamstarkov/next.js-trailing-slash-bug-demo
  2. cambiar directorio cd next.js-trailing-slash-bug-demo
  3. instalar departamentos yarn
  4. ejecutar dev: yarn dev
  5. abre http: // localhost : 3000 /
  6. abre la pestaña de red de devtools
  7. observar http://localhost:3000/_next/static/development/pages/about.js siendo 200ed
  8. observar http://localhost:3000/_next/on-demand-entries-ping?page=/about/ siendo 200ed
  9. observar http://localhost:3000/about/ siendo 404ed
  10. observar los intentos persistentes de resolver http://localhost:3000/about/
  11. observar en la terminal Client pings, but there's no entry for page: /about/
  12. recarga la página
  13. observe la página 404.
  14. elimine la barra diagonal al final de la URL o haga clic en http: // localhost : 3000 / about
  15. observar la página 200ed
  16. Para garantizar la persistencia del error, repita los pasos 5 a 15 una vez.

Comportamiento esperado

  1. /about/ no debe resolverse como 404 not found
  2. /about/ debe resolverse como 200 ok
  3. El servidor no debe imprimir Client pings, but there's no entry for page: /about/
  4. tanto /about como /about/ deberían funcionar de la misma manera

Capturas de pantalla

N / A

Información del sistema

  • SO: macOS High Sierra 10.13.6 (17G65)
  • Navegador (no debería importar, pero se puede reproducir en chrome 69.0.3497.100 y safari Version 12.0 (13606.2.11) (fue lo mismo para safari 11)
  • Versión de Next.js: 7.0.0 (se puede reproducir en 5.xy 6.x)

Contexto adicional

Agregue aquí cualquier otro contexto sobre el problema.

Si cambia este código en https://github.com/zeit/next.js/blob/459c1c13d054b37442126889077b7056269eeb35/server/on-demand-entry-handler.js#L242 -L249

o node_modules/next/dist/server/on-demand-entry-handler.js localmente

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

y reinicie next dev y abra http: // localhost : 3000 / y haga clic en el enlace y luego:

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

Creo que el problema (al menos en parte) es la incapacidad del middleware de onDemandEntryHandler para encontrar la página en las entradas si la página tiene una barra inclinada.

Espero que mis 2 horas de investigación y preparación puedan ayudar a solucionar este problema.

story 8 feature request

Comentario más útil

Estamos a punto de conseguir una función que solucione este problema, ¡más o menos un día!

Todos 119 comentarios

Los temas más relevantes y notables son # 1189 y # 3876

¡Espero que esto se resuelva finalmente! @timneutkens ¿Cuál es el estado de los problemas de barra diagonal para Next 7?

@NathanielHill Podría reproducirlo en el próximo @ 7

Estoy usando nextjs 7 y la barra diagonal me está produciendo un 404 tanto en dev como en prod:

  • en la carga de la página inicial
  • en la actualización de la página

Y afecta:

  • un enlace externo
  • un enlace interno
  • URL pegada en el navegador

Simplemente eliminar la barra inclinada soluciona el problema.

Los navegadores, servidores y / u otros servicios a menudo agregan barras al final donde se pueden pegar los enlaces, por lo que, aunque puedo controlar los enlaces internos, es difícil controlar a qué enlaces pueden llegar los usuarios externos.

También veo este problema en la versión 7. No estoy seguro de si esto es relevante, pero estoy asignando un alias a un proyecto de Next.js a una subcarpeta de otra implementación de Now. Entonces, nuestra URL base es primer.style y estamos asignando un alias a nuestra aplicación primer-components.now.sh Next.js a primer.style/components . En producción, la página de índice de primer.style/components funciona bien, pero primer.style/components/ produce un 404.

Tuve que buscar un poco para encontrar este problema. Utilizo implementaciones estáticas en Netlify, por lo que no es un problema en prod, pero en desarrollo (Next 7) la compilación simplemente se congela si había una barra al final, y era difícil averiguar por qué. No creo que esto (no manejar barra final en el entorno de desarrollo) sea un buen DX.

También tengo este problema y es realmente molesto, espero que se solucione pronto.

Si desea una barra inclinada final, puede hacer esto. <Link href='/about' as='/about/'><a>about</a></Link> pero si estás usando fridays / next-route, esto no es posible. Entonces tengo una bifurcación donde puedes agregar trailingSlash como prop. Espero que esto ayude

Si desea una barra inclinada final, puede hacer esto. <Link href='/about' as='/about/'><a>about</a></Link> pero si estás usando fridays / next-route, esto no es posible. Entonces tengo una bifurcación donde puedes agregar trailingSlash como prop. Espero que esto ayude

@aluminick Lo siento, acabo de probar esto y no me funciona. Todavía llego a la página traling-slashed (última versión), que no se encuentra después de la actualización (comportamiento actual).

Además, ni # 6664 ni # 6752 ayudan con estos, porque experimental.exportTrailingSlash no ayuda porque es solo por next export , creo

hubo una solicitud de extracción prometedora # 6421 por @Janpot que no llegó a ningún consenso, desafortunadamente

@iamstarkov ¿Cuál es el estado de este problema? ¿Alguna solución al lado del gancho server.js ?

@dryleaf status: todavía está abierto

Un problema similar ... redirigir cuando se agregan varias barras diagonales. Ejemplo: https://github.com/zeit/next.js////////////issues/5214

Las URL de GitHub son irrelevantes

@iamstarkov No estoy seguro de lo que quieres decir. Pero después de releer mi publicación original, parece que podría haber sido más claro.

La URL de GitHub está destinada a ser una demostración simple de cómo deberían funcionar (preferiblemente) las URL cuando se crea una aplicación con Next.js. En otras palabras, si un usuario agrega una barra oblicua adicional, la URL aún debería funcionar.

¿Alguna actualización para nextjs 9?

Soy nuevo en Next, pero ¿cuál es la solución alternativa que están usando para este problema?

@iamstarkov ¿Cuál es el estado de este problema?

¡Me sorprende que este problema no se haya resuelto de ninguna manera durante aproximadamente un año!
¿El equipo de Next.js necesita alguna otra razón para comenzar a solucionar este problema?

Las URL deberían funcionar independientemente de la barra al final. Consulta cualquier sitio de la web.

Si esto está fuera del alcance de Next.js, danos la posibilidad de configurarlo en Now.
Estoy realmente confundido de que el equipo de Zeit ignore cuestiones tan críticas durante años.

@exentrich Esto es fácilmente configurable en Zeit Now simplemente redirigiendo 301 todas las barras finales a la misma ruta sin barras:

now.json :

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

Sin embargo, tampoco entiendo por qué Next.js no maneja esto y por qué el equipo ha ignorado este problema.

Esto, junto con un public/ (en proceso) son los principales problemas en los que veo que se están ejecutando los conversos de CRA.

@rauchg

@NathanielHill gracias!
Probé esta solución, pero los parámetros de consulta están eliminados. Por ejemplo, /some/?query=1 redirigirá a /some sin consulta. ¿Sabes cómo solucionarlo?

Sí, eso suena como un problema @exentrich

No habría adivinado ese comportamiento, ya que me han dicho que hay un ^ y $ implícitos envueltos alrededor de la expresión regular (lo que significa que su ejemplo no coincidiría). Tal vez haya una forma de acceder a la cadena de consulta por sí misma para volver a agregarla: man_shrugging: Buena suerte

Intento que funcione con un servidor expreso personalizado y avinoamr / connect-slashes, pero parece que se está encontrando con el mismo problema

Este es sin duda un problema enorme, especialmente porque las rutas / arrojan páginas de error y eso perjudica al SEO (que es uno de los principales atractivos de Next).

Los redireccionamientos 301 y los servidores expresos personalizados parecen ser hacks en lugar de arreglos. En mi caso, tengo una aplicación de trabajo completa construida en Next sin un servidor Express personalizado ; todo lo demás funciona perfectamente, pero ahora tengo que crear un nuevo servidor Express solo por el problema de la barra diagonal. El esfuerzo requerido parece desproporcionado considerando que se trata de un truco. ¡Me encantaría que esto tuviera prioridad! Debido a esta razón, escucho quejas en mi equipo sobre haber usado Next en lugar de algo como Vanilla React / Angular y ciertamente debilita el caso de Next.

PD: Me encanta trabajar con Next ❤️

Este es sin duda un problema enorme, especialmente porque las rutas / arrojan páginas de error y eso perjudica al SEO

No daña tu SEO. Google trata la barra diagonal como una página diferente. Tenerlo 404 no afecta al SEO más que cualquier otra página que no exista en su sitio. Además, siempre que nunca lo vincule con una barra diagonal, Google no intentará rastrearlo en primer lugar. Este problema, aunque sigue siendo un problema válido, es mucho menos crítico de lo que todos creen.

@ nik-john @NathanielHill @dkrish @exentrich

No debería tener que usar un servidor Express para hacer un redireccionamiento 301. Depende de sus requisitos, pero he podido cumplir con los míos con un server.js .

Un redireccionamiento 301 también es la mejor manera de hacer SEO, ya que no obtendrá penalizaciones por contenido duplicado por la ruta de barra oblicua y no barra oblicua.

Me encanta ❤️ Next.js, pero voto para que esto se maneje sin esta solución.

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

No daña tu SEO. Google trata la barra diagonal como una página diferente. Tenerlo 404 no afecta al SEO más que cualquier otra página que no exista en su sitio.

Entiendo tu punto de que no daña particularmente al SEO de forma innata. Pero ejerce una presión adicional sobre los desarrolladores para que obtengan las definiciones de URL correctas en todo momento, lo que está sujeto a errores humanos. Un desarrollador que sea nuevo en Next no sabría necesariamente que la siguiente URL (de aspecto perfectamente normal) conducirá a una página 404. <Link href='/people/'>

Idealmente, un marco maduro no debería estar sujeto a tales errores humanos.

Además, siempre que nunca lo vincule con una barra diagonal, Google no intentará rastrearlo en primer lugar.

Nuevamente, existe el problema de las personas que se vinculan accidentalmente a _ www.mysite.com/people/_ en lugar de a _ www.mysite.com/people_ (ambos parecen ser exactamente iguales para los usuarios, incluso para la mayoría de los desarrolladores).

Ambos escenarios _pueden_ afectar al SEO.

Ahora, sin considerar el impacto de SEO, también existe el significado semántico de la URL: ¿a qué apunta _ _ www.mysite.com/people / _? Idealmente porque apunta a un directorio, Next debería devolver lo que esté en pages > people > index.js (a diferencia de pages > people.js para _www.mysite.com/people_) pero en su lugar no devuelve nada, lo cual es muy Defecto de alto nivel en el funcionamiento del enrutamiento.

Las principales bibliotecas de enrutamiento ya tienen alguna disposición para esto, como isExact en el caso de React Router

Si bien entiendo de dónde vienes, sigo pensando que este es un problema evidente que debe mejorarse.

Esto también es completamente inevitable en el caso de next export

existe el problema de que la gente se vincule accidentalmente ...

Existe el problema de las personas que se vinculan accidentalmente a cualquier URL inexistente, ¿por qué /some/path/ sería menos inexistente que /some/path/dhgfiuwo ?

también está el significado semántico de la URL

Esto es muy subjetivo, hasta donde yo sé, no hay especificaciones que dicten cuál es la diferencia semántica. De acuerdo con la especificación de URL , con y sin barra al final se consideran URL diferentes. Puedo pensar en al menos 7 comportamientos válidos diferentes:

  • con y sin contenido completamente diferente
  • con 404, sin resuelve
  • con resuelve, sin hace 404
  • con redireccionamientos a sin
  • sin redireccionamientos a con
  • con y sin tienen el mismo contenido con canonical apuntando a con
  • con y sin tienen el mismo contenido con apuntar canónico a sin

Combine esto con la posibilidad de tener /pages/some-page.js y /pages/some-page/index.js (o ambos).

¿Next.js debería admitir todos esos casos de uso? ¿Debería elegir un comportamiento predeterminado?

No estoy en contra de esto, pero después de intentar implementar esto antes, creo que hay más matices de lo que parece inicialmente.

Existe el problema de las personas que se vinculan accidentalmente a cualquier URL no existente, ¿por qué / some / path / sería menos inexistente que / some / path / dhgfiuwo?

Para el caso /some/path/dhgfiuwo - la gente espera que falte la ruta dhgfiuwo . (Por ejemplo, el usuario dhgfiuwo no se puede encontrar en el sistema y la forma users/dhgfiuwo es incorrecta. La ausencia de un usuario en el sistema es una ocurrencia esperada).
Para el caso /some/path/ , la gente espera que esta ruta sea la misma que /some/path , porque este es el comportamiento predeterminado en otros sitios.
Por lo tanto, una falla en would/some/path/ es menos inexistente que /some/path/dhgfiuwo .

Veo que otros han publicado sus soluciones, así que quería compartir mi enfoque: https://github.com/DevSpeak/next-trailingslash

Algunas mejoras y soporte para páginas enrutadas dinámicas cuando se trata de? = Deberían hacerse en mi opinión, pero esto es solo para mostrar la idea.

Para una solución rápida, puede reemplazar la página predeterminada _error (como en el ejemplo de @DevSpeak ).

@DevSpeak , recomendaría algunos cambios para su repositorio:

  • Evite los redireccionamientos 301 : los navegadores los almacenan en caché de forma permanente y pueden causarle mucho dolor. En la mayoría de los casos, todo lo que necesita es un 302.
  • Su ternario errorCode se puede actualizar (estaba desactualizado en los documentos hasta la semana pasada)
  • Esto es solo del lado del servidor, por lo que puede envolverlo con if (typeof window === 'undefined') { ... } para agitarlo desde el paquete del cliente

Esto es lo que estoy usando en un proyecto de TypeScript (basado en la página de error incorporada):

/pages/_error.tsx (o elimine los tipos de TypeScript y asígnele el nombre /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
  }
};

Tenga en cuenta que esto también registra un error cuando se accede a la página, por lo que puede verificar sus registros para solucionar cualquier enlace / otro problema.

@DevSpeak @bitjson Gracias por sus sugerencias. Sin duda, esa es una forma de hacerlo y, sin duda, resuelve muy bien el problema. Pero teniendo en cuenta que _error.jsx originalmente está destinado a manejar _errores_ y no a la lógica de enrutamiento de la casa, en mi opinión, tener todo este código es hacky y bastante declarativo. Esperar que todos los usuarios hagan esto en cada base de código no debería ser un requisito, esto debería salir de la caja. = Soy de la opinión de que esta condición debe integrarse con la lógica de enrutamiento, con una opción para optar por no participar como React Router.

@NathanielHill

Esto también es completamente inevitable en el caso de la próxima exportación.

Espere, al leer la documentación, entendí que hay un código específico para manejar la condición de barra diagonal final:

Las páginas se exportarán como archivos html, es decir, / about se convertirá en /about.html.

Es posible configurar Next.js para exportar páginas como archivos index.html y requerir barras inclinadas, es decir, / about se convierte en /about/index.html y se puede enrutar a través de / about /. Este era el comportamiento predeterminado antes de Next.js 9. Puede usar el siguiente next.config.js para volver a este comportamiento:

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

Incluso si esta no es realmente una opción para la exportación de HTML estático a través de next export , no estoy de acuerdo con la lógica de que solo porque Next admite esta característica (increíble), otros modos deben sufrir (yo no Conozca las estadísticas de uso, pero supongo que más personas usan el modo regular con servidor en lugar de sin servidor), especialmente cuando se sabe que este es un caso de uso común.

Para su información: hay un RFC que podría interesarle https://github.com/zeit/next.js/issues/9081

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

@Janpot Me encanta: esto nos llevará a la mitad del camino, es decir, tendrá algún tipo de soporte para redireccionamientos sin tener que crear un servidor personalizado. Esto seguirá siendo imperativo porque para cada ruta que agregue el usuario, tendrían que configurar una redirección en el next.config.js , o tal vez podríamos usar una expresión regular para detectar todos los casos como el mencionado @bitjson :

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

En cualquier caso, si el equipo central está priorizando este RFC, recomiendo encarecidamente que vayamos un paso más allá y lo convierta en una _config_ incorporada que uno pueda _opt out_ de esa manera.

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

Considerándolo todo, creo que este es un gran paso adelante, ¡¡buenas cosas @Timer !! 🔥

@ nik-john La ruta que especifiqué en "/:path*/" Debería capturar todo ( :path captura un solo segmento, * hace que capture 0 an instancias).

@Janpot Ah, mi culpa 🤦‍♂ Supongo que también deberíamos considerar cualquier parámetro de consulta final en esa expresión regular

Además, sigo apoyando la segunda parte:

En cualquier caso, si el equipo central está priorizando este RFC, recomiendo encarecidamente que vayamos un paso más allá y lo convierta en una configuración incorporada de la que uno pueda optar por no participar.

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

Si está utilizando un servidor personalizado y desea ignorar rutas estrictas, también puede usar un controlador de ruta personalizado en lugar de realizar una redirección.

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

De esta manera podemos admitir tanto /path como /path/ y resolverlos en la misma página.

Los proveedores de federación de Oauth a menudo requieren barras inclinadas hacia adelante, por lo que este comportamiento hace que un flujo simple sea muy complicado. ¿Cuál es el desafío técnico al implementar este comportamiento? ¿O es esta una decisión de diseño de la siguiente?

No lo he visto mencionado hasta ahora en este hilo, pero no estoy experimentando este problema después de la implementación con Now, solo lo estoy experimentando localmente cuando pruebo con 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();
  }
};

Obtuve esto de stackoverflow y funcionó perfectamente. esta solución funciona con express.

@GaneshKathar No veo cómo funcionará esto si tiene en cuenta que Next.js no usa express

Creo que no podemos ponernos de acuerdo en esto y debería ser configurable.

De hecho, siempre quiero la barra al final, las URL relativas son más fáciles de razonar cuando todas las páginas terminan con una barra al final.

Por ejemplo, no tiene sentido que /about/index.tsx sea /about lugar de /about/ , pero es comprensible ahora que la próxima espera sin barra diagonal. Si todas las páginas terminaran en barra, permitiría que las páginas contuvieran subpáginas en el futuro, lo que creo que es una forma más extensible para las páginas.

Hacer enlaces relativos dentro del archivo /about/index.tsx ahora es engorroso. Si crea un enlace ./mysubpage/ su lugar, apunta a la raíz del sitio. Esto hace que las subpáginas no se puedan cambiar de nombre. No puedo hacer un directorio /about/ lleno de páginas que simplemente puedo cambiar de nombre, porque debería ir y editar los enlaces relativos también.

Además, el sitio wget -r produce resultados sensibles al tener siempre barras inclinadas, produciendo archivos index.html.

Sin embargo, cambiar esta configuración es un cambio enorme, ya que todos los sitios esperan barras inclinadas no finales, por lo que debe ser configurable.

Estoy usando la versión 9 y este problema aún no se ha resuelto.

Pude hacerlo funcionar usando algo como lo siguiente en mi next.config.js :

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

  return paths;
},

Acceder a /authors da 302 apuntando location a /authors/ . Estoy probando con http-serve , no estoy seguro de si este comportamiento es específico del servidor.

cuando me enfrenté a este problema, se me ocurrió esta solución

en mi página _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 };
}

¿Es una buena forma de superar el problema?

¿Qué tal esto?

páginas / _app.jsx

`` importar React de 'reaccionar';
importar la aplicación desde 'siguiente / aplicación';

exportar la clase predeterminada MyApp extiende la aplicación {
render () {
const {Componente, pageProps, enrutador: {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 gracias por la sugerencia. Eso me ayudó. Así es como lo implementé para mantener el comportamiento predeterminado para los 404 no finales (es decir, simplemente estoy reexportando la implementación Error predeterminada):

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

Sí, eso servirá @cansin siempre y cuando no se decida nada más :) ¡Salud!

Pequeña mejora a la solución alternativa 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 única diferencia aquí es verificar que el código de estado sea 404. Tuve problemas al usar Link para rutas dinámicas donde siempre se mostraban en el servidor debido a la redirección. Si desea que el enrutamiento del lado del cliente funcione, no puede agregar una barra al final del enlace href prop, pero debe asegurarse de no redireccionar en este caso.

El problema con la implementación de una solución alternativa en el componente Error es que arrojará un error de notificación en el desarrollo, lo que me molesta. Alguna mejora con respecto a mi redirección anterior del lado del cliente:

Lo que mejoró es que ahora usa next / enrutador en el lado del cliente y el reemplazo de la URL ocurre sin una recarga.

páginas / _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} />;
  }
}

también gracias a @mbrowne por la corrección 404 :)

Tomó la solución de @cansin y agregó la capacidad de manejar parámetros de consulta

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 ¿ puede proporcionar ejemplos de una URL con barra diagonal y parámetros de consulta que ocurren al mismo tiempo? ¿Estás pensando en la línea de /blog/?123 ?

Gracias a todos por algunas de las soluciones anteriores. ¡Ellos trabajaron!

Sin embargo, ¿tenemos alguna forma oficial de solucionar este problema por parte del equipo de Next? Este problema ha estado aquí durante años.

Las páginas de directorio no se muestran con barra al final en la siguiente exportación

@pinpointcoder ¿ puede proporcionar ejemplos de una URL con barra diagonal y parámetros de consulta que ocurren al mismo tiempo? ¿Estás pensando en la línea de /blog/?123 ?

@coodoo No es él, pero sí, lamentablemente esto pasa mucho. Actualmente estoy en el proceso de migrar gradualmente un sitio de WordPress a Next.js, y por alguna razón, los "desarrolladores" originales decidieron forzar una barra al final en cada URL, por lo que actualmente tenemos toneladas de solicitudes con ambos barra oblicua Y parámetros de consulta.

Como estamos a punto de migrar toneladas de publicaciones de blog para las que la URL canónica actualmente incluye una barra diagonal, esto es un gran dolor de cabeza en este momento.

Decidí implementar un servidor personalizado para manejar esto y resulta que es fácil de hacer, y aún puedes usar el sistema de enrutamiento basado en archivos de next.js. De esa manera, puede reescribir la URL que ve next.js y la URL real todavía tiene una barra al final:

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

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

@mbrowne En realidad, tenemos un montón de razones para usar un servidor personalizado, pero lo principal que me ha impedido implementar uno hasta ahora es el hecho de que pierde la Optimización estática automática. ¿Sabes si es posible especificar rutas estáticas manualmente?

No necesitamos optimización estática automática para nuestra aplicación en este momento, así que no la he investigado.

También estoy usando un servidor personalizado, pero cuando pasa la URL modificada (sin barra inclinada) a handle , SSR ve una URL diferente del lado del cliente.
Preferiría que el enrutador next haga coincidir la URL con la barra inclinada sin esos desagradables trucos.

2020 y este error todavía ocurre. Increíble

Este es un error grave que realmente debe corregirse. /products funciona, pero /products/ no. Con este enlace

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

yo obtengo

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

Sin embargo, si apunto el enlace a /products/ , visito el enlace y actualizo la página durante el desarrollo, obtengo un 404. Esta es una experiencia de desarrollo bastante dolorosa.

Este problema se informó por primera vez hace 1,5 años; ¿Podemos obtener una solución oficial? Todavía está presente en 9.3.4.

Hice una redirección a una URL de barra inclinada no final en lugar de mostrar contenido, por razones de SEO.

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

Para SEO, rel="canonical" puede ayudar, pero aún necesita solucionar este problema 404.

Este es un error grave que realmente debe corregirse. /products funciona, pero /products/ no. Con este enlace

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

yo obtengo

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

Sin embargo, si apunto el enlace a /products/ , visito el enlace y actualizo la página durante el desarrollo, obtengo un 404. Esta es una experiencia de desarrollo bastante dolorosa.

Este problema se informó por primera vez hace 1,5 años; ¿Podemos obtener una solución oficial? Todavía está presente en 9.3.4.

Actualmente también estoy recibiendo este problema.

Así es como lo arreglé, https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3

Así es como lo arreglé, https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3

Gracias, aunque esto requiere un servidor personalizado cuando se desarrolla localmente, y no debería ser necesario.

@timneutkens ¿ Alguna posibilidad de que se pueda incluir una solución para este problema en el programa de desarrollo?

Más importante aún, la solución de redireccionamiento no funciona para aquellos que mantienen sitios que ya están configurados para agregar una barra en lugar de eliminar una en producción. No creo que el marco deba dictar esta elección de forma arbitraria.

La solución de @AlexSapoznikov funcionó bien para nosotros con Netlify (que agrega una barra al final de forma predeterminada). Aquí hay una versión avanzada que agrega soporte para parámetros de consulta:

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

Pido disculpas porque soy un novato en Next JS, aunque tengo experiencia en desarrollo de software en otros SDK y plataformas.

Creo que este "error" fue el que más me sorprendió. Para mí, violó el "principio del menor asombro". Simplemente esperaba que mi / about / y / about funcionaran igual, ya que coloqué un index.tsx en mi carpeta / pages / about /.

Comencé a crear sitios web a fines de la década de 1990 con HTML FTP en mi servidor, y luego pasé a PHP y Apache y, finalmente, a servidores Java. Ahora me especializo en aplicaciones móviles. Me parece extraño que este comportamiento no sea el predeterminado y que tenga que escribir una página de servidor personalizada para solucionarlo en mi servidor de desarrollo.

Planeo hacer una exportación estática, por lo que no aparecerá en producción incluso si no escribo el servidor personalizado. Sin embargo, hace que el desarrollo y la depuración sean un poco más molestos.

¿Podemos obtener un indicador de "siguiente desarrollo" que solucione esto para que los desarrolladores perezosos no necesitemos escribir lógica de enrutamiento adicional solo para el tiempo de desarrollo / depuración?

¡Gracias!

ps: Sí, sé que /about y /about/ son URL completamente diferentes. Me confundí mucho cuando puse un archivo index.tsx dentro de mi carpeta /pages/about/ , y descubrí que solo funciona con la ruta /about pero no funciona con /about/ . Me sorprendería menos si fuera al revés.

pps: Fue más confuso cuando tengo un componente <Link></Link> que apunta a /about/ y funciona como se esperaba. Luego, cuando presiono actualizar en mi navegador, inmediatamente aparece un error 404, a pesar de que la URL no cambió. Eso fue muy sorprendente. :-D

¡Pero espera, se pone peor! Agregamos una función checkForTrailingSlash dentro de _error.js que eliminaría la barra inclinada y la redirección. Esto funcionó bien por un tiempo hasta que (finalmente) agregamos una página 404 personalizada y descubrimos que con una página 404 personalizada, Next.js omite completamente Error . Esto significa que ninguna de su lógica personalizada dentro de Error.getInitialProps funcionará más, incluida una comprobación de barras al final.

Supongo que probaré la solución _app.js que otros mencionaron, ya que un servidor personalizado todavía no es una posibilidad.

La solución de @AlexSapoznikov funcionó bien para nosotros con Netlify (que agrega una barra al final de forma predeterminada). Aquí hay una versión avanzada que agrega soporte para parámetros de consulta:

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

Hay un error crítico en su muestra de código: las solicitudes a la ruta de índice con un parámetro de consulta arrojarán un error, ya que termina intentando pasar solo la cadena de consulta a Next.js como asPath .

Esto lo arregla:

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

Para que esto funcione con SSR, tuve que agregar lo siguiente a la solución @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)
        : {},
    };
  }

Probablemente sea una buena idea generalizar de alguna manera esta funcionalidad en una función que funcione tanto durante SSR como durante CSR y llamarla en ambos lugares ( getInitialProps y render ).

por

esto arreglará pero el título está mal. mmm
image

@AlexSapoznikov @pjaws

Tu solución nos pone en bucle infinito:

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

Contexto

Debido a razones fuera de nuestro control, tenemos que usar la opción exportTrailingSlash en next.config.js .

Queremos tener un enlace a otra página, pero queremos que el enlace sea /somepage?param=whatever .

Parece que el siguiente enlace convierte esto en /somepage/?param=whatever y obtenemos la página no encontrada.

El uso de la solución anterior resuelve el problema de los parámetros, pero luego, al ir a una página implementada como /somepage/ , entra en un bucle infinito.

Creo que @ronyeh hizo un muy buen punto aquí, así que realmente quiero una solución oficial para este problema :(

Para que esto funcione con SSR, tuve que agregar lo siguiente a la solución @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)
        : {},
    };
  }

Probablemente sea una buena idea generalizar de alguna manera esta funcionalidad en una función que funcione tanto durante SSR como durante CSR y llamarla en ambos lugares ( getInitialProps y render ).

Esto ha funcionado para páginas con getServerSideProps y ahora las URL con barras inclinadas devuelven la misma página sin 404.
Pero hay una falla, tengo pocas páginas que usan rutas dinámicas y getStaticPaths, no puedo usar getServerSideProps en ellas y, por lo tanto, cuando estas rutas dinámicas se exploran con una barra diagonal, primero devuelven un 404 y luego redirigen a la página .

Estoy trabajando con una carpeta / api / test

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

funciona para

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

y acabo de descubrir que esto no funciona

  • GET / api / test /

no estoy seguro si este es un problema relacionado
P / D exportTrailingSlash = true no lo resuelve

Este es un problema muy antiguo, ¿hay alguna razón por la que no se aborda durante tanto tiempo?

No estoy seguro de qué es lo que ya no funciona.

Mi entendimiento es que los requisitos son los siguientes:

| | exportTrailingSlash: falso | exportTrailingSlash: true |
| ------------------------- | ----------------------- ----- | --------------------------- |
| URL termina con / | No debería funcionar | Debería funcionar |
| url no termina con / | Debería funcionar | No debería funcionar |

Esto funciona como se esperaba donde:

  • Localmente usamos exportTrailingSlash: false
  • Para implementaciones (compilaciones de producción) usamos exportTrailingSlash: true y un nginx convierte url/ en url/index.html

Por lo que puedo ver en @ andrescabana86, esto funciona donde no debería: GET /api/test/123/ mientras que GET /api/test/ no funciona y no debería.

@Izhaki Probé ambos, implementando en prod ... y para mí no funciona

  • GET / api / test /

y estoy usando exportTrailingSlash: true

Puedo intentar crear un repositorio público si lo desea, tal vez olvidé algo en el medio.

Gracias por sus respuestas

@ andrescabana86 No estoy seguro de cuánto ayudará un repositorio público aquí; esto bien puede ser alguna configuración en el servidor en el que implementa.

Estamos probando nuestras compilaciones de producción (con exportTrailingSlash: true ) localmente usando este script en package.json :

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

Por favor, avíseme si va en su navegador a http://localhost:5000/api/test/ funciona.

(Tenga en cuenta que $(pwd) está en Mac / Linux; consulte esto para Windows)

@Izhaki, el problema era sobre el hecho de que (como sugiere el informe inicial) "la barra diagonal en el enlace de la página legítima funciona para la navegación del lado del cliente, pero conduce a un paquete no encontrado y 404 en una actualización completa (ssr)". Por lo tanto, hubo una discrepancia entre el comportamiento de un cambio de ruta del lado del cliente y una actualización completa. No estoy seguro de si el problema persiste con la última versión de Next.js. Puedo informar aquí una vez que lo pruebe.

Acabo de probar con 9.4.1 y exportTrailingSlash: true .

Ir a http://localhost:6500/admin/ devuelve 404 cuando se desarrolla localmente.

Pero la misma ruta funciona cuando exporta.

Tenga en cuenta que exportTrailingSlash sugiere que esto es solo para _exportaciones_.

Lo que hacemos es usar:

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

Eso significa que las cosas funcionan según lo previsto cuando desarrollamos localmente. Y funciona correctamente cuando se implementa (mediante exportación).

¿No es esa la solución correcta y viable para esto?

Si una URL no funciona en el desarrollo pero sí en la producción, ¿no cree que va en contra del principio de la menor sorpresa? Creo que esto todavía debería considerarse un error.

^ Dicho esto, estoy bastante seguro de que anteriormente en producción había un comportamiento conflictivo entre una actualización de página y un evento router.push. No sé si sigue siendo así.

@ andrescabana86 @Izhaki exportTrailingSlash no está relacionado con esto. Esa opción se relaciona con la exportación estática de aplicaciones Next.js. Cuando es verdadero, se genera example/index.html , mientras que cuando es falso, se genera example.html . Tengo entendido que exportTrailingSlash no tiene nada que ver con el modo de desarrollo.

Creo que una fuente de confusión es que cuando tienes exportTrailingSlash next.js agrega una barra al final de los enlaces. Esto también sucede en el desarrollo. No estoy seguro de que deba hacer esto. Pero de todos modos, esto no se trata solo de example/index.html vs example.html , también necesita que se modifiquen los enlaces.

Si una URL no funciona en el desarrollo pero sí en la producción, ¿no cree que va en contra del principio de la menor sorpresa? Creo que esto todavía debería considerarse un error.

Puedo estar equivocado, pero la opción exportTrailingSlash era para servidores nginx que no están configurados para servir /something.html cuando la URL es /something .

Este no es el caso del siguiente servidor que se usa para el desarrollo local. Entonces, lo que funciona y lo que no depende de lo que sirva a su aplicación.

Puede argumentar que cuando exportTrailingSlash es verdadero, el siguiente servidor debería admitir rutas que terminen con una barra diagonal (aunque esto hará que export en exportTrailingSlash algo irrelevante).

FWIW ya se está trabajando en esto # 13333

No soy un programador con mucha experiencia, y utilizo Next.js principalmente para aterrizajes de varias páginas. Aparentemente, he estado usando la siguiente solución casi todo el tiempo, sin saber cuál es su efecto. Aquí está la versión simplificada:

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

En mi caso, el código es un poco más complicado, porque lo estoy usando para admitir prefijos de URL estáticas adicionales, etc. Pero esta versión simplificada parece funcionar bien para el problema discutido, independientemente del exportTrailingSlash y su efecto en Link s. Por ejemplo, las URL /about y /about/ funcionan bien.

En su forma actual, esencialmente imita el enrutamiento nativo de Next.js. La desventaja: requiere server.js , y tendrá que admitirlo manualmente para URL "más profundas" (con "subcarpetas" adicionales), por ejemplo, /company/about/ . Pero parece ser una solución relativamente simple para aquellos que ya usan server.js en su proyecto.

Para que esto funcione con SSR, tuve que agregar lo siguiente a la solución @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)
        : {},
    };
  }

Probablemente sea una buena idea generalizar de alguna manera esta funcionalidad en una función que funcione tanto durante SSR como durante CSR y llamarla en ambos lugares ( getInitialProps y render ).

Esto ha funcionado para páginas con getServerSideProps y ahora las URL con barras inclinadas devuelven la misma página sin 404.
Pero hay una falla, tengo pocas páginas que usan rutas dinámicas y getStaticPaths, no puedo usar getServerSideProps en ellas y, por lo tanto, cuando estas rutas dinámicas se exploran con una barra diagonal, primero devuelven un 404 y luego redirigen a la página .

@gauravkrp Esta es en realidad una adición extremadamente importante, ya que la solución @AlexSapoznikov en realidad devolverá un 404 para la página a Google (ya que la redirección ocurre en el cliente). Imagino que el SEO es una de las principales razones por las que muchos de nosotros estamos usando Next.js en primer lugar.

También creo que poner esto en getInitialProps debería funcionar en todos lados, y la pieza dentro de la función principal es innecesaria en este punto. La principal advertencia aquí es que está perdiendo la optimización estática automática al tener esto, aunque probablemente sea mejor que un montón de 404.

Para compartir ...

Mi proyecto es Express + Next.js .
express 4.17.1
next 9.4.5-canary.7

Cuando el desarrollo

Tiempo de ejecución dinámico

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

Cuando la producción

Exportación estática
Ejecute next build y 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 conclusión

No tengo ningún problema al redirigir haciendo clic en la aplicación cliente,
también se está trabajando en la actualización completa en static route .

Pero lo hará 404 cuando se actualice por completo en dynamic route ,
como /album/[id].jsx o /album/123 ,
Así que espero solucionar este problema utilizando el siguiente mecanismo.

p.ej
Cuando llega a 404 a /album/123 ,
el servidor debe continuar proporcionando contenido html,
el navegador continuará cargando la página sin problemas,
cuando Next.js arranca, next/router debería manejarlo automáticamente.

¿Existe alguna solución temporal a este problema de producción?

Estamos a punto de conseguir una función que solucione este problema, ¡más o menos un día!

¿Existe alguna solución temporal a este problema de producción?

Hay muchos en este hilo, pero actualmente estoy usando lo que @gauravkrp publicó recientemente y me está funcionando bien.

Puede realizar un seguimiento de las relaciones públicas aquí: # 13333

¡Esto ahora se ha resuelto en next@^9.4.5-canary.17 !

¿Cuánto tiempo tarda la función en pasar de canary a master?

¡Esto ahora se ha resuelto en next@^9.4.5-canary.17 !

¿Y cómo se resuelve exactamente? simplemente quitando la barra al final? si accedo a " www.site.com/help ", ¿podemos tener una opción allí si optamos por dejar la barra final? al acceder a " www.site.com/help/ " o " www.site.com/help " saldrá o redireccionará o agregará "/" al final para tener: " www.site.com/help/ "

@Valnexus ver # 13333, incluye una opción experimental:

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

¿Cuánto tiempo tarda la función en pasar de canary a master?

Cuando está listo. Todavía hay casos extremos en el manejo que se están resolviendo. Una vez que se hayan solucionado, se puede estabilizar.

@timneutkens @Janpot

Probé el próximo canario más reciente (9.4.5-canary.27) pero cuando creo la página test y accedo a www.example/test/ se redirige a www.example/test
Creo que el comportamiento para ambos casos debería ser el mismo.

Cuando acceda a www.example/test/ , debería permanecer en www.example/test/ .
Cuando acceda a www.example/test , debería permanecer en www.example/test .
Lo pruebo en Nuxt.js, funciona el mismo comportamiento que describo anteriormente.

Creo que el comportamiento para ambos casos debería ser el mismo.

El motivo de una redirección es asegurarse de que los motores de búsqueda no vean contenido duplicado. ¿Cuál es tu caso de uso exacto?

No veo por qué es un problema cerrado si aún no se ha fusionado con una versión estable. Si entendí correctamente, solo está arreglado en la versión canary por ahora, ¿verdad?

Los problemas se cierran cuando aterriza su solicitud de extracción asociada, ya que están disponibles para uso inmediato en canary. Si necesita esta función, actualice al canal canary.

Suena bien. ¡Gracias, @Timer!

@Janpot Vi que https://github.com/issues/ y https://github.com/issues pueden acceder al mismo comportamiento sin una redirección.

https://twitter.com/explore/ y https://twitter.com/explore , este también.

Si tiene un problema con los motores de búsqueda, ¿por qué Github y Twitter no lo solucionaron?
Creo que es el comportamiento predeterminado de cualquier sitio web.

No hay un caso de uso específico, solo creo que debería funcionar de esa manera.

Si tiene un problema con los motores de búsqueda, ¿por qué Github y Twitter no lo solucionaron?

@armspkt No es un problema ya que hay varias formas de solucionarlo. Por ejemplo, Twitter usa el atributo <link rel="canonical"> para indicar a los robots de búsqueda qué página deben rastrear y otras versiones deben marcarse como duplicadas.

Por lo tanto, la redirección es una forma viable de hacer SEO en su sitio web. Puedes leer más información aquí .

@ziserman Si tenemos varias formas de resolverlo, deberíamos mantener la misma url sin redireccionar para la experiencia del usuario.

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

Nuxtjs tienen varias opciones para elegir (indefinido, verdadero, falso)

¿Nextjs debería tener varias opciones para elegir también?

El motivo de una redirección es asegurarse de que los motores de búsqueda no vean contenido duplicado. ¿Cuál es tu caso de uso exacto?

@Janpot Nuestra API tiene barras diagonales en muchos lugares. La última versión genera muchos 404 en el backend, ya que las URL con barras inclinadas (/ api / test / -> / api / test) no coinciden

No sé si funcionará para todos, pero encontré esta solución que funciona para mí. Ponlo en el archivo _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 He minimizado su comentario porque provoca graves regresiones de rendimiento en una aplicación Next.js.

La última versión next@canary corrige este error, ¡actualice en su lugar!

@mlbonniec He minimizado su comentario porque provoca graves regresiones de rendimiento en una aplicación Next.js.

La última versión next@canary corrige este error, ¡actualice en su lugar!

¡No hay problema!
Sin embargo, lo actualicé antes y eso no resolvió el problema.
Con npm update

Si la última versión de Next.js canary no soluciona el error por usted, abra un nuevo problema para que podamos echar un vistazo. 🙏

Pregunta rápida, ¿cómo manejarán este cambio los proyectos con next export ? ¿Creando una página completamente nueva para cada página para la barra inclinada? No creo que una aplicación exportada pueda especificar redireccionamientos HTTP (o reescrituras).

Los proyectos que usan next export tendrán todos sus <Link /> s en el lado del cliente correctamente actualizados, pero la redirección del lado del servidor requerirá una configuración manual. Los proyectos implementados con el destino sin servidor o next start configurarán estos ajustes automáticamente.

@Timer una vez que esto alcance una versión completa, ¿aún necesitaríamos usar la opción experimental?

@Timer una vez que esto alcance una versión completa, ¿aún necesitaríamos usar la opción experimental?

No, solo estaría disponible como está.

¿Supongo que la opción trailingSlash no funcionará para next export ? ¿Cuál es la mejor manera de redirigir /page/ a /page (o viceversa) en, digamos, páginas de github?

¿Supongo que la opción trailingSlash no funcionará para next export ? ¿Cuál es la mejor manera de redirigir /page/ a /page (o viceversa) en, digamos, páginas de github?

Hasta donde yo sé, las páginas de github no tienen una función de redireccionamiento. Esto funciona desde el primer momento en vercel.com, aunque también es gratuito para proyectos de pasatiempos (como las páginas de github).

Los proyectos que usan next export tendrán todos sus <Link /> s en el lado del cliente correctamente actualizados, pero la redirección del lado del servidor requerirá una configuración manual. Los proyectos implementados con el destino sin servidor o next start configurarán estos ajustes automáticamente.

Hola @Timer ¿Puedes explicar más? ¿Cómo puedo configurar manualmente? Así que aquí esta mi situación. En mi sitio web, uso next-i18next . Después de implementar con next build && next export , todos los enlaces internos funcionan, pero cuando se ingresa manualmente la URL, NINGUNO de ellos funciona y genera un error 404. Desde aquí , decidí usar trailingSlash:true y, por lo tanto, ingresar manualmente /pricing funcionará ahora, pero /zh/pricing genera errores 404.

¿Fue útil esta página
0 / 5 - 0 calificaciones