Next.js: a barra final no link para a página legítima funciona para a navegação do lado do cliente, mas leva ao pacote não encontrado e 404 na atualização forçada (ssr)

Criado em 20 set. 2018  ·  119Comentários  ·  Fonte: vercel/next.js

a barra final no link para a página legítima funciona para a navegação do lado do cliente, mas leva ao pacote não encontrado e 404 na atualização forçada (ssr)

Relatório de erro

Descreva o bug

deixe-me saber se o título precisa de mais esclarecimentos.

todas as questões relevantes foram encerradas com o raciocínio de que foi corrigido em 6-canário (eu acredito que não) ou por servir melhorado (o que é verdade apenas em talvez exportação estática de produção).

Estou reescrevendo meu blog existente para next.js e já usei barras finais. O serve mais recente pode ajudar com isso, uma vez que eu construir meu blog com o next.js. Mas para consertar o env eu preciso me livrar das barras finais e utilizar 301 Moved Permanently no produto; ou viver com suporte de barra à direita quebrado em dev.

Reproduzir

Aqui está um caso reproduzível mínimo (o link para repro repo está abaixo do snippet):

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

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

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

Repo reproduzível mínimo https://github.com/iamstarkov/next.js-trailing-slash-bug-demo

  1. clone repo git clone https://github.com/iamstarkov/next.js-trailing-slash-bug-demo
  2. mudar o diretório cd next.js-trailing-slash-bug-demo
  3. instale dependências yarn
  4. execute dev: yarn dev
  5. abra http: // localhost : 3000 /
  6. abrir a aba de rede do devtools
  7. observe http://localhost:3000/_next/static/development/pages/about.js sendo 200ed
  8. observe http://localhost:3000/_next/on-demand-entries-ping?page=/about/ sendo 200ed
  9. observe http://localhost:3000/about/ sendo 404
  10. observe as tentativas persistentes de resolver http://localhost:3000/about/
  11. observe no terminal Client pings, but there's no entry for page: /about/
  12. recarregue a página
  13. observe a página 404.
  14. remova a barra final do url ou clique em http: // localhost : 3000 / about
  15. observar a página sendo 200ed
  16. para garantir a persistência do erro, repita as etapas 5 a 15 uma vez.

Comportamento esperado

  1. /about/ não deve ser resolvido como 404 not found
  2. /about/ deve ser resolvido como 200 ok
  3. O servidor não deve imprimir Client pings, but there's no entry for page: /about/
  4. tanto /about quanto /about/ devem funcionar da mesma maneira

Capturas de tela

N / D

Informação do sistema

  • SO: macOS High Sierra 10.13.6 (17G65)
  • Navegador (não deve importar, mas pode ser reproduzido no Chrome 69.0.3497.100 e no safari Versão 12.0 (13606.2.11) (era o mesmo para o Safari 11)
  • Versão de Next.js: 7.0.0 (pode reproduzir em 5.xe 6.x)

Contexto adicional

Adicione qualquer outro contexto sobre o problema aqui.

Se você alterar este código em https://github.com/zeit/next.js/blob/459c1c13d054b37442126889077b7056269eeb35/server/on-demand-entry-handler.js#L242 -L249

ou node_modules/next/dist/server/on-demand-entry-handler.js 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}`

e reinicie next dev e abra http: // localhost : 3000 / e clique no link sobre:

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

Acho que o problema (pelo menos parte dele) é a incapacidade do middleware do onDemandEntryHandler de localizar a página nas entradas se a página tiver uma barra final.

Espero que minhas 2 horas de investigação e preparação possam ajudar a corrigir esse problema.

story 8 feature request

Comentários muito úteis

Estamos prestes a lançar um recurso para consertar isso - mais ou menos um dia!

Todos 119 comentários

os problemas mais relevantes e notáveis ​​são # 1189 e # 3876

Ansioso para que isso seja finalmente resolvido! @timneutkens Qual é o status dos problemas de barra à direita no Next 7?

@NathanielHill eu poderia reproduzi-lo no próximo @ 7

Estou usando nextjs 7 e a barra final está produzindo um 404 para mim no dev e no prod:

  • no carregamento inicial da página
  • na atualização da página

E afeta:

  • um link externo
  • um link interno
  • URL colado no navegador

A simples remoção da barra final corrige o problema.

As barras finais são frequentemente adicionadas por navegadores, servidores e / ou outros serviços onde os links podem ser colados, então, embora eu possa controlar os links internos, é difícil controlar em quais links os usuários externos podem estar chegando

Também estou vendo esse problema na versão 7. Não tenho certeza se isso é relevante, mas estou criando o alias de um projeto Next.js para uma subpasta de outra implantação do Now. Portanto, nosso url base é primer.style e estamos alterando nosso aplicativo primer-components.now.sh Next.js para primer.style/components . Na produção, a página de índice de primer.style/components funciona bem, mas primer.style/components/ produz um 404.

Tive que pesquisar um pouco para encontrar esse problema. Eu uso implantações estáticas no Netlify, então não é um problema no prod, mas no desenvolvimento (Next 7) a compilação congela se houver uma barra final e é difícil descobrir o porquê. Não acho que isso (não lidar com a barra final no ambiente de desenvolvimento) seja um bom DX.

Também estou tendo esse problema e é realmente irritante, espero que seja corrigido em breve.

Se você quiser uma barra final, basta fazer isso. <Link href='/about' as='/about/'><a>about</a></Link> mas se você estiver usando rotas de sexta / próxima, isso não é possível. Então, eu tenho um fork onde você pode adicionar trailingSlash como prop. Espero que isto ajude

Se você quiser uma barra final, basta fazer isso. <Link href='/about' as='/about/'><a>about</a></Link> mas se você estiver usando rotas de sexta / próxima, isso não é possível. Então, eu tenho um fork onde você pode adicionar trailingSlash como prop. Espero que isto ajude

@aluminick , desculpe, acabei de experimentar e não funciona para mim. Ainda recebo a página com barras cortadas (versão mais recente), que não é encontrada após a atualização (comportamento atual).

também nem # 6664 nem # 6752 ajudam com isso, porque experimental.exportTrailingSlash não ajuda porque é apenas para next export , eu acredito

houve uma solicitação de pull promissora # 6421 por @Janpot que não chegou a nenhum consenso, infelizmente

@iamstarkov Qual é a situação desse problema? Alguma solução além do gancho server.js ?

@dryleaf status: ainda está aberto

Um problema semelhante ... redirecionar quando várias barras forem adicionadas. Exemplo: https://github.com/zeit/next.js////////////issues/5214

Os urls do GitHub são irrelevantes

@iamstarkov Não tenho certeza do que você quer dizer. Mas depois de reler minha postagem original, parece que eu poderia ter sido mais claro.

O url do GitHub pretende ser uma demonstração simples de como os urls devem (preferencialmente) funcionar quando um aplicativo é construído com Next.js. Em outras palavras, se um usuário adicionar uma barra extra, o url ainda funcionará.

Alguma atualização para nextjs 9?

Eu sou novo no Next, mas qual é a solução alternativa que vocês estão usando para esse problema?

@iamstarkov Qual é a situação desse problema?

Estou chocado que esse problema não seja resolvido de forma alguma por cerca de um ano!
A equipe Next.js precisa de algum outro motivo para começar a consertar isso?

O URL deve funcionar independentemente da barra final. Verifique qualquer site na web.

Se isso estiver fora do escopo do Next.js, permita-nos configurar isso agora.
Estou realmente confuso com o fato de a equipe do Zeit ignorar essas questões críticas por anos.

@exentrich Isso é facilmente configurável no Zeit Now simplesmente redirecionando 301 todas as barras finais para a mesma rota sem barras:

now.json :

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

No entanto, também não entendo por que isso não é tratado pelo próprio Next.js e por que a equipe ignorou esse problema.

Este, junto com public/ (em andamento) são os principais problemas para os quais vejo os convertidos do CRA correndo.

@rauchg

@NathanielHill obrigado!
Tentei essa solução, mas os parâmetros de consulta foram eliminados. Por exemplo, /some/?query=1 redirecionará para /some sem consulta. Sabes como arranjar isso?

Sim, isso soa como um problema @exentrich

Eu não teria imaginado esse comportamento, já que me disseram que há ^ e $ implícitos ao redor da regex (o que significa que seu exemplo não corresponderia). Talvez haja uma maneira de acessar a string de consulta por conta própria para adicioná-la de volta: man_shrugging: Boa sorte

Tentando fazê-lo funcionar usando um servidor expresso personalizado e avinoamr / connect-slashes, mas parece estar tendo o mesmo problema

Este é certamente um grande problema, especialmente porque / rotas lançam páginas de erro e isso prejudica o SEO (que é uma das principais atrações do Next).

Os redirecionamentos 301 e os servidores expressos personalizados parecem ser hacks ao invés de consertos. No meu caso, tenho um aplicativo totalmente funcional desenvolvido no Next sem nenhum servidor Express personalizado - todo o resto funciona perfeitamente, mas agora estou tendo que criar um novo servidor Express apenas por causa do problema da barra final. O esforço necessário parece ser desproporcional, considerando que se trata de um hack. Eu adoraria se isso pudesse ser aumentado em prioridade! Por esse motivo, estou ouvindo reclamações em minha equipe sobre ter usado o Next em vez de algo como React / Angular baunilha e isso certamente enfraquece o caso do Next.

PS: Adoro trabalhar com o Next ❤️

Este é certamente um grande problema, especialmente porque as rotas / lançam páginas de erro e isso prejudica o SEO

Não prejudica o seu SEO. o google trata a barra final como uma página diferente. Ter o erro 404 não afeta o SEO mais do que qualquer outra página inexistente em seu site. Além disso, contanto que você nunca faça um link para ele com uma barra, o Google não tentará rastreá-lo em primeiro lugar. Este problema, embora ainda seja um problema válido, é muito menos crítico do que todos vocês imaginam.

@ nik-john @NathanielHill @dkrish @exentrich

Você não deveria ter que usar um servidor expresso para fazer um Redirecionamento 301. Depende dos seus requisitos, mas consegui atender aos meus com um server.js .

Um redirecionamento 301 também é a melhor maneira de ir para o SEO, já que você não terá penalidades de conteúdo duplicado para a rota de barra e não barra.

Eu adoro o ❤️ Next.js, mas voto para que isso seja resolvido sem essa solução alternativa.

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

Não prejudica o seu SEO. o google trata a barra final como uma página diferente. Ter o erro 404 não afeta o SEO mais do que qualquer outra página inexistente em seu site.

Entendo que isso não prejudica o SEO de maneira particular. Mas coloca pressão adicional sobre os desenvolvedores para obter as definições de URL certas sempre, o que está sujeito a erros humanos. Um desenvolvedor que é novo no Next não saberia necessariamente que o seguinte URL (de aparência perfeitamente normal) levará a uma página 404. <Link href='/people/'>

Uma estrutura madura não deveria estar sujeita a tais erros humanos, idealmente imo.

Além disso, contanto que você nunca faça um link para ele com uma barra, o Google não tentará rastreá-lo em primeiro lugar.

Novamente - existe o problema de pessoas acidentalmente ligando para _ www.mysite.com/people/_ em vez de _ www.mysite.com/people_ (ambos parecem ser exatamente os mesmos para os usuários - até mesmo a maioria dos desenvolvedores).

Ambos os cenários _podem_ afetar o SEO.

Agora, sem considerar o impacto de SEO, há também o significado semântico da URL - o que _faz_ _ www.mysite.com/people / _ aponta? Idealmente, porque está apontando para um diretório, Next deve retornar o que quer que esteja em pages > people > index.js (ao contrário de pages > people.js para _www.mysite.com/people_), mas em vez disso, não retorna nada, o que é muito falha de alto nível em como o roteamento funciona.

As principais bibliotecas de roteamento já têm alguma provisão para isso - como isExact no caso do Roteador React

Embora eu entenda de onde você vem, ainda acho que esse é um problema evidente que precisa ser resolvido

Isso também é completamente inevitável no caso de next export

existe o problema de pessoas ligando acidentalmente ...

Existe o problema de pessoas acidentalmente ligando para qualquer url não existente, por que /some/path/ seria menos inexistente do que /some/path/dhgfiuwo ?

há também o significado semântico do URL

Isso é altamente subjetivo, pelo que eu sei, não há nenhuma especificação por aí que dite qual é a diferença semântica. De acordo com a especificação do URL , com e sem barra final são considerados URLs diferentes. Posso pensar em pelo menos 7 comportamentos válidos diferentes:

  • com e sem conteúdo completamente diferente
  • com faz 404, sem resolve
  • com resolve, sem faz 404
  • com redirecionamentos para sem
  • sem redirecionamentos para com
  • com e sem ter o mesmo conteúdo com apontamento canônico com
  • com e sem ter o mesmo conteúdo com apontamento canônico para sem

Combine isso com a possibilidade de ter /pages/some-page.js e /pages/some-page/index.js (ou ambos).

O next.js deve oferecer suporte a todos esses casos de uso? Deve escolher um comportamento padrão?

Não sou contra isso, mas depois de tentar implementar isso antes, acho que há mais nuances do que parece inicialmente.

Existe o problema de pessoas acidentalmente se conectando a qualquer url inexistente, por que / algum / caminho / seria menos inexistente do que / algum / caminho / dhgfiuwo?

Para o caso /some/path/dhgfiuwo - as pessoas esperam que a rota dhgfiuwo possa estar faltando. (Por exemplo, o usuário dhgfiuwo não pode ser encontrado no sistema e a maneira users/dhgfiuwo está errada. A ausência de um usuário no sistema é uma ocorrência esperada.)
Para o caso /some/path/ - as pessoas esperam que este caminho seja igual a /some/path , porque este é o comportamento padrão em outros sites.
Portanto, uma falha em would/some/path/ é menos inexistente do que /some/path/dhgfiuwo .

Vejo que outras pessoas postaram suas soluções, então gostaria de compartilhar minha abordagem: https://github.com/DevSpeak/next-trailingslash

Algumas melhorias e suporte para páginas roteadas dinâmicas quando se trata de? = Devem ser feitas IMO, mas isso é apenas para mostrar a ideia.

Para uma solução rápida, você pode substituir a página _error padrão (como no exemplo de @DevSpeak ).

@DevSpeak , recomendo algumas alterações para o seu repo:

  • Evite redirecionamentos 301 - eles são armazenados em cache permanentemente pelos navegadores e podem causar muitos problemas. Na maioria dos casos, você só precisa de um 302.
  • Seu ternário errorCode pode ser atualizado (ele estava desatualizado nos documentos até a semana passada)
  • Este é apenas do lado do servidor, então você pode envolvê-lo com if (typeof window === 'undefined') { ... } para sacudi-lo do pacote do cliente

Aqui está o que estou usando em um projeto Typescript (com base na página de erro integrada):

/pages/_error.tsx (ou remova os tipos TypeScript e nomeie-os /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
  }
};

Observe que isso também registra um erro quando a página é acessada, para que você possa verificar seus registros para corrigir quaisquer links / outros problemas.

@DevSpeak @bitjson Obrigado por suas sugestões. Essa é certamente uma maneira de fazer isso e certamente resolve o problema muito bem. Mas considerando que _error.jsx foi originalmente criado para lidar com _errors_ e não para abrigar a lógica de roteamento, em minha opinião ter todo esse código é hacky e bastante declarativo. Esperar que cada usuário faça isso em cada base de código não deve ser um requisito - deve sair da caixa. = Sou de opinião que esta condição deve ser incorporada à lógica de roteamento, com uma opção de cancelamento como o React Router.

@NathanielHill

Isso também é totalmente inevitável no caso da próxima exportação

Espere - entendi, ao ler a documentação, que há um código específico para lidar com a condição de barra final:

As páginas serão exportadas como arquivos html, ou seja, / about se tornará /about.html.

É possível configurar Next.js para exportar páginas como arquivos index.html e exigir barras finais, ou seja, / about torna-se /about/index.html e é roteável via / about /. Este era o comportamento padrão antes de Next.js 9. Você pode usar o seguinte next.config.js para voltar a este comportamento:

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

Mesmo que esta não seja realmente uma opção para exportação de HTML estático via next export , não concordo com a lógica de que só porque o Next suporta esse recurso (incrível), outros modos precisam sofrer (eu não conheço as estatísticas de uso, mas presumo que mais pessoas usam o modo normal com servidor em vez de sem servidor), especialmente quando esse é um caso de uso comum

Para sua informação: há um RFC que pode interessar a você https://github.com/zeit/next.js/issues/9081

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

@Janpot Love it - isso nos trará metade do caminho, ou seja, teremos algum tipo de suporte para redirecionamentos sem ter que criar um servidor personalizado. Isso ainda será imperativo porque para cada rota que o usuário adicionar, ele terá que configurar um redirecionamento no next.config.js - ou talvez possamos apenas usar um regex para capturar todos os casos como @bitjson mencionado:

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

Em qualquer caso, se a equipe principal está priorizando este RFC, eu recomendo que avancemos mais um passo e o

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

No geral, acho que este é um grande passo em frente - coisas boas, @Timer !! 🔥

@ nik-john O caminho que eu especifiquei em "/:path*/" Deve pegar todos ( :path pega um único segmento, * faz pegar de 0 a n instâncias.)

@Janpot Ah, meu mal 🤦‍♂ Eu estou supondo que também precisaríamos considerar qualquer parâmetro de consulta final nessa regex

Além disso, ainda mantenho a segunda parte:

Em qualquer caso, se a equipe principal está priorizando este RFC, eu recomendo que avancemos um pouco mais e o tornemos uma configuração embutida que pode ser desativada assim

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

Se você estiver usando um servidor personalizado e quiser ignorar as rotas restritas, também poderá usar um manipulador de rotas personalizadas em vez de fazer um redirecionamento.

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

Desta forma, podemos suportar /path e /path/ e resolver para a mesma página.

Os provedores de federação Oauth geralmente exigem barras à direita, portanto, esse comportamento torna um fluxo simples muito complicado. Qual é o desafio técnico na implementação desse comportamento? Ou isso é uma decisão de design do próximo?

Eu não vi isso mencionado até agora neste tópico, mas não estou tendo esse problema após a implantação com o Now, estou apenas experimentando localmente ao testar com 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();
  }
};

obtive isso do stackoverflow e funcionou perfeitamente. esta solução funciona com express.

@GaneshKathar Não vejo como isso funcionará se você considerar o Next.js que não usa o expresso

Acho que não podemos concordar com isso e deve ser configurável.

Na verdade, eu quero sempre a barra final, os urls relativos são mais fáceis de raciocinar quando todas as páginas terminam com barra final.

Por exemplo, não faz sentido que /about/index.tsx seja /about vez de /about/ , mas é compreensível agora que o próximo espera sem barra final. Se todas as páginas terminassem em barra, isso permitiria que as páginas contivessem subpáginas no futuro, o que eu acho que é uma forma mais extensível para as páginas.

Criar links relativos dentro do arquivo /about/index.tsx agora é complicado. Se você criar um link ./mysubpage/ ele aponta para a raiz do site. Isso faz com que as subpáginas não possam ser renomeadas. Não posso fazer um diretório /about/ cheio de páginas que posso simplesmente renomear, porque devo editar os links relativos também.

Além disso, o site wget -r produz resultados sensíveis por ter sempre barras à direita, produzindo arquivos index.html.

No entanto, alterar essa configuração interrompe maciçamente a alteração, pois todos os sites esperam barras não finais, portanto, deve ser configurável.

Estou usando a versão 9 e este problema ainda não foi resolvido

Consegui fazer funcionar usando algo como o seguinte em meu next.config.js :

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

  return paths;
},

Acessando /authors 302 aponta location para /authors/ . Estou testando com http-serve , não tenho certeza se esse comportamento é específico do servidor.

quando enfrentei esse problema, descobri esta solução

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

é uma boa maneira de superar o problema?

Que tal agora?

pages / _app.jsx

`` `import React de 'react';
importar App de 'next / app';

exportar a classe padrão MyApp extends App {
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 (asPath.length > 1 && asPath.endsWith('/')) {
  const urlWithoutEndingSlash = asPath.replace(/\/*$/gim, '');

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

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

}
}
`` `

@cnblackxp obrigado pela sugestão. Isso me ajudou. Aqui está como eu implementei para manter o comportamento padrão para 404s não posteriores (ou seja, estou simplesmente reexportando a implementação Error padrão):

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

Sim, isso servirá @cansin enquanto nada mais estiver decidido :) saúde!

Uma pequena melhoria na solução 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('/')) {

A única diferença aqui é verificar se o código de status é 404. Tive problemas ao usar Link para rotas dinâmicas em que elas sempre eram renderizadas no servidor por causa do redirecionamento. Se você quiser que o roteamento do lado do cliente funcione, você não pode adicionar uma barra final ao link href prop, mas então você precisa ter certeza de não redirecionar neste caso.

O problema com a implementação de uma solução alternativa no componente Error é que ela gerará uma notificação de erro no desenvolvimento, o que me incomoda. Algumas melhorias em meu redirecionamento anterior do lado do cliente:

O que melhorou é que agora ele usa next / router no lado do cliente e a substituição de url acontece sem recarregar.

pages / _app.jsx

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

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

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

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

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

também obrigado a @mbrowne pela correção 404 :)

Pegou a solução de @cansin e adicionou a capacidade de lidar com 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 você pode fornecer exemplos de url com barra final e parâmetros de consulta que acontecem ao mesmo tempo? Você está pensando na linha de /blog/?123 ?

Obrigado a todos por algumas das soluções alternativas acima. Eles trabalharam!

No entanto, temos alguma maneira oficial de corrigir esse problema da equipe do Next? Este problema está aqui há anos.

As páginas do diretório não são exibidas com uma barra à direita na próxima exportação

@pinpointcoder você pode fornecer exemplos de url com barra final e parâmetros de consulta que acontecem ao mesmo tempo? Você está pensando na linha de /blog/?123 ?

@coodoo Não ele, mas sim, infelizmente isso acontece muito. Atualmente estou no processo de migração incremental de um site WordPress para Next.js e, por algum motivo, os "desenvolvedores" originais decidiram forçar uma barra final em cada URL, portanto, atualmente temos toneladas de solicitações com ambos barra E parâmetros de consulta.

Como estamos prestes a migrar toneladas de postagens de blog para as quais a URL canônica atualmente inclui uma barra final, isso é uma dor gigante na minha bunda agora.

Decidi implementar um servidor personalizado para lidar com isso e descobri que é fácil de fazer e você ainda pode usar o sistema de roteamento baseado em arquivo do next.js. Dessa forma, você pode reescrever o URL que next.js vê e o URL real ainda tem uma barra no 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}`)
    })
})

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

@mbrowne Na verdade, temos

Não precisamos de otimização estática automática para nosso aplicativo no momento, então não olhei para isso.

Também estou usando um servidor personalizado, mas quando você passa a url modificada (sem barra) para handle , o SSR vê uma url diferente do lado do cliente.
Eu preferiria o roteador next para combinar o url com a barra inicial sem esses hacks desagradáveis.

2020 e esse bug ainda acontece. Inacreditável

Este é um bug ruim que realmente precisa ser corrigido. /products funciona, mas /products/ não. Com este link

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

eu recebo

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

No entanto, se eu apontar o link para /products/ , visitar o link e atualizar a página durante o desenvolvimento, obtenho um 404. Esta é uma experiência de desenvolvimento bastante dolorosa.

Esse problema foi relatado pela primeira vez há 1,5 anos; podemos, por favor, obter uma correção oficial? Ainda está presente em 9.3.4.

Fiz redirecionamento para url de barra não final em vez de mostrar o conteúdo, por motivo 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" pode ajudar, mas ainda precisa corrigir este problema 404.

Este é um bug ruim que realmente precisa ser corrigido. /products funciona, mas /products/ não. Com este link

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

eu recebo

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

No entanto, se eu apontar o link para /products/ , visitar o link e atualizar a página durante o desenvolvimento, obtenho um 404. Esta é uma experiência de desenvolvimento bastante dolorosa.

Esse problema foi relatado pela primeira vez há 1,5 anos; podemos, por favor, obter uma correção oficial? Ainda está presente em 9.3.4.

Eu também estou tendo esse problema.

Veja como eu consertei: https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3

Veja como eu consertei: https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3

Obrigado, embora isso exija um servidor personalizado ao desenvolver localmente e não deva ser necessário.

@timneutkens Há alguma chance de uma correção para esse problema ser incluída no cronograma de desenvolvimento?

Mais importante ainda, a solução de redirecionamento não funciona para aqueles que estão mantendo sites nessa área já configurados para adicionar uma barra em vez de remover uma na produção. Não acho que a estrutura deva ditar essa escolha arbitrariamente.

A solução de @AlexSapoznikov funcionou bem para nós com o Netlify (que adiciona uma barra final por padrão). Esta é uma versão avançada que adiciona suporte 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} />;
  }
}

Peço desculpas porque sou um novato do Next JS, embora tenha experiência em desenvolvimento de software em outros SDKs e plataformas.

Acho que esse "bug" foi o que mais me surpreendeu. Para mim, isso violou o "princípio do menor espanto". Eu simplesmente esperava que meu / about / e / about funcionasse da mesma forma, já que coloquei um index.tsx em minha pasta / pages / about /.

Comecei a fazer sites no final da década de 1990 com HTML FTP para meu servidor e, mais tarde, mudei para PHP e Apache e, eventualmente, servidores Java. Agora me especializo em aplicativos móveis. Só me parece estranho que esse comportamento não seja o padrão e que eu tenha que escrever uma página de servidor customizada para consertar no meu servidor de desenvolvimento.

Eu pretendo fazer uma exportação estática, então ela não aparecerá na produção mesmo se eu não escrever o servidor personalizado. No entanto, isso torna o desenvolvimento e a depuração um pouco mais irritantes.

Podemos obter um sinalizador de "próximo desenvolvimento" que corrige isso para que nós, desenvolvedores preguiçosos, não precisemos escrever lógica de roteamento extra apenas para o tempo de desenvolvimento / depuração?

Obrigado!

ps: Sim, eu sei que /about e /about/ são URLs completamente diferentes. Eu fiquei muito confuso quando coloquei um arquivo index.tsx dentro da minha pasta /pages/about/ e descobri que ele só funciona com o caminho /about mas não funciona com /about/ . Eu ficaria menos surpreso se fosse o contrário.

pps: Foi ainda mais confuso quando eu tenho um componente <Link></Link> que aponta para /about/ e funciona como esperado. Então, quando eu clico em atualizar no meu navegador, ele imediatamente atinge o erro 404, embora a URL não tenha mudado. Isso foi muito surpreendente. :-D

Mas espere, fica pior! Adicionamos uma função checkForTrailingSlash dentro de _error.js que removeria a barra final e redirecionaria. Isso funcionou bem por um tempo até que (finalmente) adicionamos uma página 404 personalizada e descobrimos que, com uma página 404 personalizada, Next.js ignora Error . Isso significa que nenhuma das suas lógicas personalizadas dentro de Error.getInitialProps funcionará mais - incluindo uma verificação de barras finais.

Acho que tentarei a solução _app.js que outros mencionaram, pois um servidor personalizado ainda não é uma possibilidade.

A solução de @AlexSapoznikov funcionou bem para nós com o Netlify (que adiciona uma barra final por padrão). Esta é uma versão avançada que adiciona suporte 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} />;
  }
}

Há um erro crítico em seu exemplo de código: solicitações para a rota de índice com um parâmetro de consulta geram um erro, pois você acaba tentando passar apenas a string de consulta para Next.js como asPath .

Isso corrige:

  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 fazer este trabalho com SSR eu tive que adicionar o seguinte ao @pjaws & solução @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)
        : {},
    };
  }

Provavelmente, é uma boa ideia generalizar de alguma forma essa funcionalidade em uma função que funcione durante o SSR e durante o CSR e chamá-la em ambos os lugares ( getInitialProps e render ).

por

isso vai consertar, mas o título está errado. Hmm
image

@AlexSapoznikov @pjaws

Sua solução nos coloca em loop 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

Por motivos fora de nosso controle, temos que usar a opção exportTrailingSlash em next.config.js .

Queremos ter um link para outra página, mas queremos que o link seja /somepage?param=whatever .

Parece que o próximo link converte isso para /somepage/?param=whatever e obtemos a página não encontrada.

Usando a solução acima, resolva o problema dos parâmetros, mas então, ao ir para uma página implementada como /somepage/ ele entra em um loop infinito.

Acho que @ronyeh fez um comentário muito bom aqui, então eu realmente quero uma solução oficial para esse problema :(

Para fazer este trabalho com SSR eu tive que adicionar o seguinte ao @pjaws & solução @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)
        : {},
    };
  }

Provavelmente, é uma boa ideia generalizar de alguma forma essa funcionalidade em uma função que funcione durante o SSR e durante o CSR e chamá-la em ambos os lugares ( getInitialProps e render ).

Isso funcionou para páginas com getServerSideProps e agora os urls com barras finais estão retornando a mesma página sem o erro 404.
Mas há uma falha, tenho poucas páginas que usam rotas dinâmicas e getStaticPaths, não consigo usar getServerSideProps neles e, portanto, quando essas rotas dinâmicas são navegadas com uma barra final, primeiro retornam um 404 e, em seguida, redirecionam para a página .

Estou trabalhando com uma pasta / api / test

  • pages / api / test.tsx
  • páginas / api / test / [id] .tsx

funciona para

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

e acabei de descobrir que isso não funciona

  • GET / api / test /

não tenho certeza se este é um problema relacionado
P / D exportTrailingSlash = true não resolve

Este é um problema muito antigo, há algum motivo para não ser abordado por tanto tempo?

Não tenho certeza do que não está mais funcionando.

Minha compreensão é que os requisitos são os seguintes:

| | exportTrailingSlash: false | exportTrailingSlash: true |
| ------------------------- | ----------------------- ----- | --------------------------- |
| url termina com / | Não deve funcionar | Deve funcionar |
| url não termina com / | Deve funcionar | Não deve funcionar |

Isso funciona conforme o esperado, onde:

  • Usamos localmente exportTrailingSlash: false
  • Para implantações (compilações de produção), usamos exportTrailingSlash: true e um nginx converte url/ em url/index.html

Pelo que posso ver em @ andrescabana86 Isso funciona onde não deveria: GET /api/test/123/ enquanto GET /api/test/ não funciona e não deveria.

@Izhaki tentei os dois, implantando no prod ... e pra mim não está funcionando

  • GET / api / test /

e estou usando exportTrailingSlash: true

Posso tentar criar um repositório público se você quiser, talvez eu tenha esquecido algo no meio.

Obrigado por suas respostas

@ andrescabana86 Não tenho certeza de quanto um

Estamos testando nossas compilações de produção (com exportTrailingSlash: true ) localmente usando este script em package.json :

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

Por favor, deixe-me saber se ir em seu navegador para http://localhost:5000/api/test/ funciona.

(Note que $(pwd) é no Mac / Linux - veja isso para windows)

@Izhaki, o problema era sobre o fato de que (como o relatório inicial sugere) "barra final no link para página legítima funciona para navegação do lado do cliente, mas leva ao pacote não encontrado e 404 na atualização física (ssr)". Portanto, havia uma incompatibilidade entre o comportamento de uma mudança de rota do lado do cliente e uma atualização completa. Não tenho certeza se o problema persiste com a versão mais recente do Next.js. Posso relatar aqui depois de testá-lo.

Acabei de testar com 9.4.1 e exportTrailingSlash: true .

Indo para http://localhost:6500/admin/ retorna 404 ao desenvolver localmente.

Mas o mesmo caminho funciona quando você exporta.

Observe que exportTrailingSlash hints destina-se apenas a _exportações_.

O que fazemos é usar:

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

Isso significa que as coisas funcionam conforme planejado quando desenvolvemos localmente. E funcionar corretamente quando implantado (via exportação).

Não é essa a solução correta e viável para isso?

Se um URL não funciona no desenvolvimento, mas funciona na produção, você não acha que isso vai contra o princípio da menor surpresa? Acho que isso ainda deve ser considerado um bug.

^ Dito isso, tenho quase certeza de que, anteriormente na produção, havia um comportamento conflitante entre uma atualização de página e um evento router.push. Não sei se ainda é o caso.

@ andrescabana86 @Izhaki exportTrailingSlash não tem relação com isso. Essa opção está relacionada à exportação estática de aplicativos Next.js. Quando verdadeiro, example/index.html é gerado, enquanto quando é falso, example.html é gerado. Meu entendimento é que exportTrailingSlash não tem nada a ver com o modo de desenvolvimento.

Acho que uma fonte de confusão é que quando você tem exportTrailingSlash next.js adiciona uma barra final aos links. Isso também acontece no desenvolvimento, não tenho certeza se deve fazer isso? Mas, de qualquer forma, não se trata apenas de example/index.html vs example.html - você também precisa que os links sejam modificados.

Se um URL não funciona no desenvolvimento, mas funciona na produção, você não acha que isso vai contra o princípio da menor surpresa? Acho que isso ainda deve ser considerado um bug.

Posso estar errado, mas a opção exportTrailingSlash era para servidores nginx que não estão configurados para servir /something.html quando o url é /something .

Este não é o caso do próximo servidor usado para desenvolvimento local. Então, o que funciona e o que não depende do que serve ao seu aplicativo.

Você pode argumentar que quando exportTrailingSlash for verdadeiro, o próximo servidor deve suportar rotas que terminam com uma barra final (embora isso torne export em exportTrailingSlash um tanto irrelevante).

FWIW, isso já está sendo trabalhado no # 13333

Não sou um programador muito experiente, usando Next.js principalmente para aterrissagens de várias páginas. Aparentemente, tenho usado a seguinte solução alternativa quase o tempo todo, sem saber de seu efeito. Aqui está uma versão simplificada dele:

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

No meu caso, o código é um pouco mais complicado, porque estou usando-o para oferecer suporte a prefixos de url estáticos adicionais, etc. Mas esta versão simplificada parece estar funcionando muito bem para o problema discutido, independentemente do exportTrailingSlash configuração e seu efeito em Link s. Por exemplo, URLs /about e /about/ funcionam perfeitamente.

Na forma atual, essencialmente imita o roteamento nativo de Next.js. A desvantagem: requer server.js customizado, e você terá que suportá-lo manualmente para URLs "mais profundos" (com "subpastas" adicionais), por exemplo, /company/about/ . Mas parece ser uma solução relativamente simples para aqueles que já usam server.js customizados em seus projetos.

Para fazer este trabalho com SSR eu tive que adicionar o seguinte ao @pjaws & solução @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)
        : {},
    };
  }

Provavelmente, é uma boa ideia generalizar de alguma forma essa funcionalidade em uma função que funcione durante o SSR e durante o CSR e chamá-la em ambos os lugares ( getInitialProps e render ).

Isso funcionou para páginas com getServerSideProps e agora os urls com barras finais estão retornando a mesma página sem o erro 404.
Mas há uma falha, tenho poucas páginas que usam rotas dinâmicas e getStaticPaths, não consigo usar getServerSideProps neles e, portanto, quando essas rotas dinâmicas são navegadas com uma barra final, primeiro retornam um 404 e, em seguida, redirecionam para a página .

@gauravkrp Esta é, na verdade, uma adição extremamente importante, já que a solução @AlexSapoznikov ainda retornará um 404 para a página para o Google (já que o redirecionamento acontece no cliente). Imagino que o SEO seja um dos principais motivos pelos quais muitos de nós estamos usando o Next.js em primeiro lugar.

Eu também acho que colocar isso em getInitialProps deve apenas funcionar, e a parte dentro da função principal é desnecessária neste ponto. A principal ressalva aqui é que você está perdendo a Otimização estática automática por ter isso - provavelmente melhor do que um monte de 404s.

Para compartilhar ...

Meu projeto é Express + Next.js .
express 4.17.1
next 9.4.5-canary.7

Quando o desenvolvimento

Tempo de Execução 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));

Quando a produção

Exportação Estática
Execute next build e 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/
});

Para concluir

Não tenho problemas ao redirecionar clicando no aplicativo cliente,
também o hard refresh está funcionando em static route .

Mas será 404 quando for atualizado em dynamic route ,
como /album/[id].jsx ou /album/123 ,
Portanto, estou ansioso para corrigir esse problema usando o mecanismo a seguir.

por exemplo
Quando atingiu 404 em /album/123 ,
servidor deve continuar a fornecer conteúdo html,
o navegador continuará a carregar a página sem problemas,
quando Next.js inicializa então next/router deve cuidar disso automaticamente.

existe alguma solução temporária para este problema na produção?

Estamos prestes a lançar um recurso para consertar isso - mais ou menos um dia!

existe alguma solução temporária para este problema na produção?

Há muitos neste tópico, mas atualmente estou usando o que @gauravkrp postou recentemente e está funcionando bem para mim.

Você pode acompanhar o PR aqui: # 13333

Isso agora foi resolvido em next@^9.4.5-canary.17 !

Quanto tempo leva para o recurso ir do canário ao master?

Isso agora foi resolvido em next@^9.4.5-canary.17 !

E como exatamente isso é resolvido? apenas removendo a barra final? se eu acessar " www.site.com/help/ ", sou redirecionado para: " www.site.com/help ", podemos ter a opção de deixar a barra final? acessar " www.site.com/help/ " ou " www.site.com/help " sairá, redirecionará ou adicionará "/" no final para ter: " www.site.com/help/ "

@Valnexus veja # 13333, inclui uma opção experimental:

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

Quanto tempo leva para o recurso ir do canário ao master?

Quando estiver pronto. Ainda há casos extremos no manuseio que estão sendo resolvidos. Depois de consertados, ele pode ir para o estável.

@timneutkens @Janpot

Tentei o próximo canário mais recente (9.4.5-canário.27), mas quando crio a página test e acesso www.example/test/ ele redireciona para www.example/test
Acho que o comportamento para ambos os casos deve ser o mesmo.

Ao acessar www.example/test/ ele deve permanecer em www.example/test/ .
Ao acessar www.example/test ele deve permanecer em www.example/test .
Eu o testo no Nuxt.js, ele funciona do mesmo comportamento que descrevi acima.

Acho que o comportamento para ambos os casos deve ser o mesmo.

O motivo de um redirecionamento é garantir que os mecanismos de pesquisa não vejam conteúdo duplicado. Qual é o seu caso de uso exato?

Não vejo por que é um problema encerrado se ainda não foi mesclado com uma versão estável. Se entendi bem, por enquanto só está fixo na versão canário, certo?

Os problemas são fechados quando a solicitação de pull associada chega, pois estão disponíveis para uso imediato no canário. Se você precisar desse recurso, atualize para o canal canário.

Soa bem. Obrigado, @Timer!

@Janpot Eu vi que https://github.com/issues/ e https://github.com/issues podem acessar o mesmo comportamento sem um redirecionamento.

https://twitter.com/explore/ e https://twitter.com/explore , este também.

Se houver um problema com os motores de busca, por que o Github e o Twitter não o corrigiram?
Acho que é o comportamento padrão de qualquer site.

Não há um caso de uso específico, apenas minha opinião que deve funcionar dessa forma.

Se houver um problema com os motores de busca, por que o Github e o Twitter não o corrigiram?

@armspkt Não é um problema, pois existem várias maneiras de resolvê-lo. Por exemplo, o Twitter usa o atributo <link rel="canonical"> para dizer aos bots de pesquisa qual página eles devem rastrear e outras versões devem ser marcadas como duplicadas.

Portanto, redirecionar é uma forma viável de fazer SEO em seu site. Você pode ler mais informações aqui .

@ziserman Se tivermos várias maneiras de resolver, devemos manter a mesma url sem redirecionar para a experiência do usuário.

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

Nuxtjs tem várias opções para escolher (indefinido, verdadeiro, falso)

O Nextjs deve ter opções de servidor para escolher também?

O motivo de um redirecionamento é garantir que os mecanismos de pesquisa não vejam conteúdo duplicado. Qual é o seu caso de uso exato?

@Janpot Nossa API tem barras finais em vários lugares. A versão mais recente gera muitos erros 404 no back-end, pois os URLs com barras finais (/ api / test / -> / api / test) não correspondem

Não sei se vai funcionar para todos, mas descobri esta solução que funciona para mim. Coloque-o no arquivo _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 Minimizei seu comentário porque causa graves regressões de desempenho em um aplicativo Next.js.

A versão next@canary recente corrige esse bug, atualize!

@mlbonniec Minimizei seu comentário porque causa graves regressões de desempenho em um aplicativo Next.js.

A versão next@canary recente corrige esse bug, atualize!

Sem problemas!
No entanto, eu atualizei antes, e isso não resolveu o problema.
Com npm update

Se o canário Next.js mais recente não corrigir o bug para você, abra um novo problema para que possamos dar uma olhada. 🙏

Pergunta rápida: como os projetos com next export lidarão com essa mudança? Criando uma página inteiramente nova para cada página para a barra final? Eu não acho que um aplicativo exportado pode especificar redirecionamentos HTTP (ou regravações).

Projetos que usam next export terão todos os seus <Link /> s no lado do cliente atualizados corretamente, mas o redirecionamento do lado do servidor exigirá configuração manual. Projetos implantados com o destino sem servidor ou next start definirão essas configurações automaticamente.

@Timer assim que atingir o lançamento completo, ainda precisaríamos usar a opção experimental?

@Timer assim que atingir o lançamento completo, ainda precisaríamos usar a opção experimental?

Não, só estaria disponível no estado em que se encontra.

Eu acho que a opção trailingSlash não funcionará para next export ? Qual é a melhor maneira de redirecionar /page/ para /page (ou vice-versa) em, digamos, páginas do github?

Eu acho que a opção trailingSlash não funcionará para next export ? Qual é a melhor maneira de redirecionar /page/ para /page (ou vice-versa) em, digamos, páginas do github?

Pelo que eu sei, as páginas do github não têm um recurso de redirecionamento. Isso funciona fora da caixa em vercel.com, que também é gratuito para projetos de hobby (como as páginas do github são).

Projetos que usam next export terão todos os seus <Link /> s no lado do cliente atualizados corretamente, mas o redirecionamento do lado do servidor exigirá configuração manual. Projetos implantados com o destino sem servidor ou next start definirão essas configurações automaticamente.

Olá @Timer. Pode explicar mais? Como posso configurar manualmente? Então aqui está minha situação. No meu site, eu uso next-i18next . Depois de implantar next build && next export , todos os links internos funcionam, mas ao inserir manualmente a URL, NENHUM deles funciona e leva ao erro 404. A partir daqui , decidi usar trailingSlash:true e inserir manualmente /pricing funcionará agora, mas /zh/pricing leva a erros 404.

Esta página foi útil?
0 / 5 - 0 avaliações