Next.js: конечная косая черта в ссылке для законной страницы работает для навигации на стороне клиента, но приводит к не найденному пакету и 404 при жестком обновлении (ssr)

Созданный на 20 сент. 2018  ·  119Комментарии  ·  Источник: vercel/next.js

конечная косая черта в ссылке для законной страницы работает для навигации на стороне клиента, но приводит к не найденному пакету и 404 при жестком обновлении (ssr)

Сообщение об ошибке

Опишите ошибку

дайте мне знать, если заголовок нуждается в уточнении.

все соответствующие проблемы были закрыты на основании того, что это было исправлено в 6-канарейке (я считаю, что это не так) или улучшенной подачей (что верно только для, возможно, статического экспорта продукции).

Я переписываю свой блог на next.js и раньше использовал завершающие слэши. Последний serve может помочь с этим, когда я создам свой блог на основе next.js. Но для исправления dev env мне нужно либо избавиться от конечных косых черт, либо использовать 301 Moved Permanently в prod; или живите со сломанной поддержкой косой черты в dev.

Воспроизводить

Вот минимальный воспроизводимый случай (ссылка на репро-репозиторий находится ниже фрагмента):

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

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

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

Минимальное воспроизводимое репо https://github.com/iamstarkov/next.js-trailing-slash-bug-demo

  1. клонировать репо git clone https://github.com/iamstarkov/next.js-trailing-slash-bug-demo
  2. сменить каталог cd next.js-trailing-slash-bug-demo
  3. установить deps yarn
  4. запустить dev: yarn dev
  5. открыть http: // localhost : 3000 /
  6. откройте вкладку сети devtools
  7. наблюдать за тем, как http://localhost:3000/_next/static/development/pages/about.js получают 200
  8. наблюдать за тем, как http://localhost:3000/_next/on-demand-entries-ping?page=/about/ получают 200
  9. наблюдать, как http://localhost:3000/about/ получает ошибку 404
  10. наблюдать настойчивые попытки разрешить http://localhost:3000/about/
  11. наблюдать в терминале Client pings, but there's no entry for page: /about/
  12. обновите страницу
  13. соблюдайте 404 стр.
  14. удалите завершающую косую черту в URL-адресе или щелкните http: // localhost : 3000 / about
  15. наблюдать за страницей 200ed
  16. для обеспечения стойкости ошибки повторите шаги 5-15 один раз.

Ожидаемое поведение

  1. /about/ не должно разрешаться как 404 not found
  2. /about/ должно быть разрешено как 200 ok
  3. Сервер не должен печатать Client pings, but there's no entry for page: /about/
  4. оба /about и /about/ должны работать одинаково

Скриншоты

N / A

Системная информация

  • ОС: macOS High Sierra 10.13.6 (17G65)
  • Браузер (не имеет значения, но его можно воспроизвести в chrome 69.0.3497.100 и Safari версии 12.0 (13606.2.11) (было то же самое для safari 11)
  • Версия Next.js: 7.0.0 (можно воспроизводить на 5.x и 6.x)

Дополнительный контекст

Добавьте сюда любой другой контекст проблемы.

Если вы измените этот код в https://github.com/zeit/next.js/blob/459c1c13d054b37442126889077b7056269eeb35/server/on-demand-entry-handler.js#L242 -L249

или node_modules/next/dist/server/on-demand-entry-handler.js локально

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

и перезапустите next dev и откройте http: // localhost : 3000 / и щелкните ссылку о том:

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

Я думаю, что проблема (по крайней мере, ее часть) заключается в неспособности промежуточного программного обеспечения onDemandEntryHandler найти страницу в записях, если на странице есть завершающая косая черта.

Я надеюсь, что мои 2 часа исследования и подготовки помогут решить эту проблему.

story 8 feature request

Самый полезный комментарий

Мы собираемся выпустить функцию, исправляющую это - день или около того!

Все 119 Комментарий

наиболее актуальными и заметными проблемами являются № 1189 и № 3876.

С нетерпением жду окончательного решения этой проблемы! @timneutkens Каков статус проблем с

@NathanielHill Я мог бы воспроизвести его на следующем @ 7

Я использую nextjs 7, а конечная косая черта выдает 404 для меня как на dev, так и на prod:

  • при начальной загрузке страницы
  • при обновлении страницы

И влияет:

  • внешняя ссылка
  • внутренняя ссылка
  • URL вставлен в браузер

Простое удаление завершающей косой черты решает проблему.

Конечные косые черты часто добавляются браузерами, серверами и / или другими службами, куда могут быть вставлены ссылки, поэтому, хотя я могу контролировать внутренние ссылки, трудно контролировать, по каким ссылкам могут приходить внешние пользователи.

Я также вижу эту проблему в версии 7. Не уверен, что это актуально, но я добавляю один проект Next.js в подпапку другого развертывания Now. Итак, наш базовый URL-адрес - primer.style и мы назначаем нашему приложению primer-components.now.sh Next.js псевдоним primer.style/components . В рабочей среде индексная страница primer.style/components работает нормально, но primer.style/components/ возвращает 404.

Мне пришлось немного поискать, чтобы найти эту проблему. Я использую статические развертывания на Netlify, поэтому это не проблема для prod, но при разработке (Next 7) компиляция просто зависает, если была завершающая косая черта, и было трудно понять, почему. Я не думаю, что это (без обработки косой черты в среде разработки) хороший DX.

У меня тоже есть эта проблема, и это действительно раздражает, я надеюсь, что скоро она будет исправлена.

Если вам нужна косая черта в конце, вы можете просто сделать это. <Link href='/about' as='/about/'><a>about</a></Link> но если вы используете fridays / next-routes, это невозможно. Итак, у меня есть вилка, в которой вы можете добавить trailingSlash качестве опоры. Надеюсь это поможет

Если вам нужна косая черта в конце, вы можете просто сделать это. <Link href='/about' as='/about/'><a>about</a></Link> но если вы используете fridays / next-routes, это невозможно. Итак, у меня есть вилка, в которой вы можете добавить trailingSlash качестве опоры. Надеюсь это поможет

@aluminick Извините, я только что попробовал это, и у меня это не работает. Я все еще попадаю на страницу с косой чертой (последняя версия), которая не обнаруживается после обновления (текущее поведение).

также ни # 6664, ни # 6752 не помогают с этим, потому что experimental.exportTrailingSlash не помогает, потому что он предназначен только для next export , я считаю

был многообещающий запрос на перенос # 6421 от

@iamstarkov Каков статус этой проблемы? Какие-нибудь решения, кроме хука server.js ?

Статус @dryleaf : он все еще открыт

Похожая проблема ... перенаправление при добавлении нескольких косых черт. Пример: https://github.com/zeit/next.js////////////issues/5214

URL-адреса GitHub не имеют значения

@iamstarkov Не

URL-адрес GitHub предназначен для простой демонстрации того, как URL-адреса должны (желательно) работать, когда приложение создается с помощью Next.js. Другими словами, если пользователь добавляет лишнюю косую черту, URL-адрес все равно должен работать.

Есть какие-нибудь обновления для nextjs 9?

Я новичок в Next, но какой обходной путь вы используете для решения этой проблемы?

@iamstarkov Каков статус этой проблемы?

Я в шоке, что этот вопрос никак не решался около года!
Нужны ли команде Next.js какие-то другие причины, чтобы начать исправлять это?

URL-адреса должны работать независимо от завершающей косой черты. Проверьте любой сайт в сети.

Если это выходит за рамки Next.js, дайте нам возможность настроить это прямо сейчас.
Меня очень смущает то, что команда Zeit годами игнорирует такие критические вопросы.

@exentrich Это легко настроить в Zeit Now, просто перенаправив 301 все конечные косые черты на один и тот же маршрут без косых черт:

now.json :

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

Однако я также не понимаю, почему этим не занимается сам Next.js и почему команда проигнорировала эту проблему.

Это, наряду с public/ (в разработке), являются основными проблемами, с которыми я вижу, как работают преобразования CRA.

@rauchg

@NathanielHill, спасибо!
Я пробовал это решение, но параметры запроса удалены. Например, /some/?query=1 перенаправит на /some без запроса. Вы знаете, как это исправить?

Да, похоже, проблема @exentrich

Я бы не догадался о таком поведении, поскольку мне сказали, что есть неявные ^ и $ обернутые вокруг регулярного выражения (что означает, что ваш пример не будет соответствовать). Может быть, есть способ получить доступ к строке запроса самостоятельно, чтобы добавить ее обратно: man_shrugging: Удачи

Попытка заставить его работать с использованием настраиваемого экспресс-сервера и avinoamr / connect-slashes, но, похоже, сталкивается с той же проблемой

Это, безусловно, серьезная проблема, особенно потому, что маршруты / выбрасывают страницы с ошибками, и это вредит SEO (что является одним из основных недостатков Next).

Редирект 301 и пользовательские экспресс-серверы кажутся взломами, а не исправлениями. В моем случае у меня есть полностью работающее приложение, построенное на Next, без специального сервера Express - все остальное работает отлично, но теперь мне нужно создать новый сервер Express только из-за проблемы с конечной косой чертой. Требуемые усилия кажутся непропорциональными, учитывая, что это взлом. Я был бы рад, если бы это было в приоритете! По этой причине в моей команде я слышу ворчание по поводу использования Next вместо чего-то вроде ванильного React / Angular, и это, безусловно, ослабляет аргументы в пользу Next.

PS: Мне очень нравится работать с Next ❤️

Это, безусловно, серьезная проблема, особенно потому, что маршруты / выдают страницы ошибок, и это вредит SEO.

Это не повредит вашему SEO. Google рассматривает завершающую косую черту как другую страницу. Наличие 404 не влияет на SEO больше, чем любая другая несуществующая страница на вашем сайте. Кроме того, если вы никогда не ссылаетесь на него с косой чертой в конце, Google не будет пытаться сканировать его в первую очередь. Эта проблема, хотя и является актуальной, но гораздо менее критична, чем вы все ее представляете.

@ nik-john @NathanielHill @dkrish @exentrich

Вам не нужно использовать сервер Express для перенаправления 301. Зависит от ваших требований, но я смог удовлетворить свои с пользовательским server.js .

Редирект 301 также является лучшим способом для SEO, поскольку вы не получите штрафов за дублирование контента для маршрута с косой чертой и без косой черты.

Мне нравится ❤️ Next.js, но я голосую за то, чтобы с этим справились без этой работы.

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

Это не повредит вашему SEO. Google рассматривает завершающую косую черту как другую страницу. Наличие 404 не влияет на SEO больше, чем любая другая несуществующая страница на вашем сайте.

Я понимаю вашу точку зрения, что это не особо вредит SEO изначально. Но это заставляет разработчиков каждый раз получать правильные определения URL, что может быть связано с человеческими ошибками. Разработчик, который не знаком с Next, не обязательно будет знать, что следующий (совершенно нормальный) URL-адрес приведет к странице 404. <Link href='/people/'>

В идеале зрелый фреймворк не должен подвергаться таким человеческим ошибкам.

Кроме того, если вы никогда не ссылаетесь на него с косой чертой в конце, Google не будет пытаться сканировать его в первую очередь.

Опять же - существует проблема, когда люди случайно ссылаются на _ www.mysite.com/people/_ вместо _ www.mysite.com/people_ (оба сайта кажутся одинаковыми для пользователей - даже для большинства разработчиков).

Оба этих сценария могут повлиять на SEO.

Теперь, не принимая во внимание влияние SEO, есть также семантический смысл URL - что _does_ _ www.mysite.com/people / _ точка? В идеале, поскольку он указывает на каталог, Next должен возвращать все, что находится в pages > people > index.js (в отличие от pages > people.js для _www.mysite.com/people_), но вместо этого он ничего не возвращает, что очень ошибка высокого уровня в том, как работает маршрутизация.

В основных библиотеках маршрутизации уже есть кое-что для этого - например, isExact в случае React Router.

Хотя я понимаю, откуда вы пришли, я все же думаю, что это вопиющая проблема, которую необходимо решить.

Это также совершенно неизбежно в случае next export

существует проблема людей, случайно связывающих ...

Существует проблема, когда люди случайно ссылаются на любой несуществующий URL. Почему /some/path/ меньше несуществующего, чем /some/path/dhgfiuwo ?

есть также семантическое значение URL

Это в высшей степени субъективно, насколько я знаю, не существует спецификации, определяющей семантическую разницу. В соответствии со спецификацией URL-адреса с косой чертой и без нее считаются разными URL-адресами. Я могу придумать как минимум 7 различных допустимых вариантов поведения:

  • с и без совершенно разного содержания
  • с 404, без разрешений
  • с разрешением, без разрешения 404
  • с перенаправлениями на без
  • без перенаправлений на с
  • с и без имеют одинаковое содержание с каноническим указанием на с
  • с и без имеют одинаковое содержание с каноническим указанием на без

Соедините это с возможностью наличия /pages/some-page.js и /pages/some-page/index.js (или обоих).

Должен ли next.js поддерживать все эти варианты использования? Следует ли выбрать поведение по умолчанию?

Я не против этого, но после попытки реализовать это раньше я просто думаю, что в этом больше нюансов, чем кажется на первый взгляд.

Существует проблема, когда люди случайно ссылаются на любой несуществующий URL-адрес, почему / some / path / может быть менее несуществующим, чем / some / path / dhgfiuwo?

Для случая /some/path/dhgfiuwo - люди ожидают, что маршрут dhgfiuwo может отсутствовать. (Например, пользователь dhgfiuwo не может быть найден в системе, и способ users/dhgfiuwo неверен. Отсутствие пользователя в системе является ожидаемым явлением.)
Для случая /some/path/ - люди ожидают, что этот путь будет таким же, как /some/path , потому что это поведение по умолчанию на других сайтах.
Следовательно, сбой в would/some/path/ менее несуществующий, чем /some/path/dhgfiuwo .

Я вижу, что другие опубликовали свои решения, поэтому я хотел поделиться своим подходом: https://github.com/DevSpeak/next-trailingslash

Некоторые улучшения и поддержка динамически маршрутизируемых страниц, когда дело доходит до? =, Должны быть выполнены IMO, но это только для демонстрации идеи.

Для быстрого решения вы можете заменить страницу по умолчанию _error (как в примере @DevSpeak ).

@DevSpeak , я бы порекомендовал несколько изменений для вашего репо:

  • Избегайте переадресации 301 - они постоянно кэшируются браузерами и могут причинить вам много боли. В большинстве случаев все, что вам нужно, это 302.
  • Ваш тернар errorCode может быть обновлен (он был устаревшим в документации до прошлой недели)
  • Это только на стороне сервера, поэтому вы можете обернуть его if (typeof window === 'undefined') { ... } чтобы вытряхнуть его из клиентского пакета.

Вот что я использую в проекте Typescript (на основе встроенной страницы ошибок):

/pages/_error.tsx (или удалите типы TypeScript и назовите его /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
  }
};

Обратите внимание: это также регистрирует ошибку при переходе на страницу, поэтому вы можете проверить свои журналы, чтобы исправить любые ссылки / другие проблемы.

@DevSpeak @bitjson Спасибо за ваши предложения. Это, безусловно, один из способов решить эту проблему и, безусловно, очень хорошо решает проблему. Но учитывая, что _error.jsx изначально предназначен для обработки _errors_, а не внутренней логики маршрутизации, на мой взгляд, наличие всего этого кода является хакерским и довольно декларативным. Ожидать, что каждый пользователь сделает это в каждой базе кода, не должно быть требованием - это должно выходить из коробки. = Я считаю, что это условие должно быть встроено в логику маршрутизации с возможностью отказа, как в React Router.

@NathanielHill

Это также совершенно неизбежно в случае следующего экспорта.

Подождите - я понял из документации, что есть специальный код для обработки условия конечной косой черты:

Страницы будут экспортированы как файлы html, т.е. / about станет /about.html.

Можно настроить Next.js для экспорта страниц в виде файлов index.html и требовать завершающих слэшей, т. Е. / About становится /about/index.html и маршрутизируется через / about /. Это было поведение по умолчанию до Next.js 9. Вы можете использовать следующий файл next.config.js, чтобы вернуться к этому поведению:

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

Даже если на самом деле это не вариант для статического экспорта HTML через next export , я не согласен с логикой, что только потому, что Next поддерживает эту (потрясающую) функцию, другие режимы должны пострадать (я не знать статистику использования, но я предполагаю, что больше людей используют обычный режим с сервером, а не без сервера), особенно когда это было известно, что это такой распространенный вариант использования

К вашему сведению: есть RFC, который может вас заинтересовать https://github.com/zeit/next.js/issues/9081

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

@Janpot Люблю это - это приведет нас к полпути, т.е. будет некоторая поддержка переадресации без необходимости создавать собственный сервер. Это по-прежнему будет обязательно, потому что для каждого маршрута, добавляемого пользователем, им нужно будет настроить перенаправление в next.config.js - или, может быть, мы могли бы просто использовать регулярное выражение, чтобы поймать все случаи, подобные упомянутому @bitjson :

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

В любом случае, если основная группа уделяет приоритетное внимание этому RFC, я настоятельно рекомендую сделать еще один шаг и сделать его

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

В общем, я считаю, что это большой шаг вперед - хорошие вещи @Timer !! 🔥

@ nik-john Путь, который я указал в "/:path*/" Должен перехватить все ( :path ловит один сегмент, * заставляет перехватить от 0 до n экземпляров.)

@Janpot Ах, мой плохой 🤦‍♂ Я предполагаю, что нам также нужно будет учитывать любые параметры конечного запроса в этом регулярном выражении

Кроме того, я все еще поддерживаю вторую часть:

В любом случае, если основная команда уделяет приоритетное внимание этому RFC, я настоятельно рекомендую сделать еще один шаг и сделать его встроенной конфигурацией, от которой можно отказаться вот так.

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

Если вы используете собственный сервер и хотите игнорировать строгие маршруты, вы также можете использовать собственный обработчик маршрута вместо перенаправления.

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

Таким образом, мы можем поддерживать как /path и /path/ и разрешать одну и ту же страницу.

Поставщики федерации Oauth часто требуют завершающих косых черт, поэтому такое поведение очень усложняет простой поток. В чем заключается техническая сложность реализации такого поведения? Или это дизайнерское решение от следующего?

Я не видел, чтобы это упоминалось до сих пор в этом потоке, но я не испытываю этой проблемы после развертывания с помощью Now, я испытываю ее только локально при тестировании с помощью 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();
  }
};

получил это из stackoverflow и отлично работал. это решение работает с экспрессом.

@GaneshKathar Я не понимаю, как это будет работать, если вы учитываете Next.js, не используя экспресс

Я думаю, что мы не можем договориться об этом, и это должно быть настраиваемым.

На самом деле я всегда хочу, чтобы конечная косая черта была всегда, относительные URL-адреса легче рассуждать, когда все страницы заканчиваются конечной косой чертой.

Например, не имеет смысла, что /about/index.tsx - это /about вместо /about/ , но теперь понятно, что следующий ожидает без завершающей косой черты. Если бы все страницы заканчивались косой чертой, это позволило бы страницам содержать подстраницы в будущем, что, на мой взгляд, является более расширяемым способом для страниц.

Создание относительных ссылок внутри файла /about/index.tsx теперь громоздко. Если вы сделаете ссылку ./mysubpage/ она будет указывать на корень сайта. Это делает подстраницы недоступными для переименования. Я не могу сделать каталог /about/ заполненным страницами, которые можно просто переименовать, потому что я должен пойти и отредактировать относительные ссылки.

Кроме того, сайт wget -r дает разумные результаты с всегда завершающимися косыми чертами, создавая файлы index.html.

Однако изменение этого параметра является серьезным нарушением изменений, поскольку все сайты ожидают незавершенных косых черт, поэтому его необходимо настраивать.

Я использую версию 9, но проблема все еще не решена.

Мне удалось заставить его работать, используя что-то вроде следующего на моем next.config.js :

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

  return paths;
},

Доступ к /authors дает 302 указания location на /authors/ . Я тестирую с помощью http-serve , не уверен, что это поведение зависит от сервера.

когда я столкнулся с этой проблемой, я придумал это решение

на моей странице _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 };
}

это хороший способ решить проблему?

Как насчет этого?

страницы / _app.jsx

`` импортировать React из 'react';
импортировать приложение из 'next / app';

экспорт класса по умолчанию MyApp extends App {
оказывать() {
const {Компонент, pageProps, маршрутизатор: {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 благодарит за предложение. Это мне помогло. Вот как я реализовал это, чтобы сохранить поведение по умолчанию для неконтролируемых 404 (т.е. я просто повторно экспортирую реализацию по умолчанию Error ):

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

export default Error;

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

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

  return { statusCode };
};

да, это будет @cansin, пока ничего не решено :) Ура!

Небольшое улучшение обходного пути

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

Единственная разница здесь - проверка того, что код состояния - 404. Я столкнулся с проблемами при использовании Link для динамических маршрутов, когда они всегда отображались на сервере из-за перенаправления. Если вы хотите, чтобы маршрутизация на стороне клиента работала, вы не можете добавить завершающую косую черту к опоре Link href , но тогда вам нужно убедиться, что в этом случае вы не перенаправляете.

Проблема с реализацией обходного пути в компоненте Error заключается в том, что он выдает уведомление об ошибке при разработке, что меня беспокоит. Некоторое улучшение моего предыдущего перенаправления на стороне клиента:

Что улучшено, так это то, что теперь он использует следующий / маршрутизатор на стороне клиента, и замена URL-адреса происходит без перезагрузки.

страницы / _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} />;
  }
}

также спасибо @mbrowne за исправление

Взял решение @cansin и добавил возможность обработки параметров запроса

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, можете ли вы предоставить примеры URL- выполняются одновременно? Вы думаете о /blog/?123 ?

Спасибо всем за некоторые из ваших обходных путей выше. Они работали!

Однако есть ли у нас какой-либо официальный способ решить эту проблему от команды Next? Этот выпуск существует уже много лет.

Страницы каталога не обслуживаются с косой чертой в конце следующего экспорта

@pinpointcoder, можете ли вы предоставить примеры URL- выполняются одновременно? Вы думаете о /blog/?123 ?

@coodoo Не он, но да, к сожалению, такое часто случается. В настоящее время я нахожусь в процессе постепенного переноса сайта WordPress на Next.js, и по какой-то причине первоначальные «разработчики» решили принудительно использовать косую черту в конце каждого URL-адреса, поэтому в настоящее время у нас есть множество запросов с обоими конечными косая черта И параметры запроса.

Поскольку мы собираемся перенести тонны сообщений в блогах, канонический URL-адрес которых в настоящее время включает в себя косую черту в конце, сейчас это большая головная боль в моей заднице.

Я решил реализовать собственный сервер, чтобы справиться с этим, и оказалось, что это легко сделать, и вы все еще можете использовать файловую систему маршрутизации next.js. Таким образом, вы можете переписать URL-адрес, который видит next.js, и настоящий URL-адрес по-прежнему будет иметь косую черту в конце:

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

См. Https://nextjs.org/docs/advanced-features/custom-server.

@mbrowne На самом деле у нас есть множество причин использовать собственный сервер, но главное, что мешает мне реализовать его до сих пор, - это то, что вы теряете автоматическую статическую оптимизацию. Вы знаете, можно ли вручную указать статические маршруты?

На данный момент нам не нужна автоматическая статическая оптимизация для нашего приложения, поэтому я не рассматривал ее.

Я также использую собственный сервер, но когда вы передаете измененный (без косой черты) URL-адрес на handle , SSR видит другой URL-адрес со стороны клиента.
Я бы предпочел, чтобы маршрутизатор next соответствовал URL-адресу с ведущей косой чертой без этих неприятных хаков.

2020, и эта ошибка все еще существует. Невероятный

Это плохая ошибка, которую действительно нужно исправить. /products работает, а /products/ - нет. По этой ссылке

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

я получил

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

Однако, если я укажу ссылку на /products/ , перейду по ссылке и обновлю страницу во время разработки, я получу 404. Это довольно болезненный опыт разработки.

Об этой проблеме впервые сообщили 1,5 года назад; мы можем получить официальное исправление? Он все еще присутствует в 9.3.4.

Я сделал перенаправление на URL-адрес без косой черты вместо отображения содержимого по причинам 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}`)
  })
})

Для SEO может помочь rel="canonical" , но все же необходимо исправить эту ошибку 404.

Это плохая ошибка, которую действительно нужно исправить. /products работает, а /products/ - нет. По этой ссылке

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

я получил

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

Однако, если я укажу ссылку на /products/ , перейду по ссылке и обновлю страницу во время разработки, я получу 404. Это довольно болезненный опыт разработки.

Об этой проблеме впервые сообщили 1,5 года назад; мы можем получить официальное исправление? Он все еще присутствует в 9.3.4.

У меня тоже сейчас возникает эта проблема.

Вот как я это исправил: https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3.

Вот как я это исправил: https://medium.com/@thisisayush/handling -404-trailing-slash-error-in-nextjs-f8844545afe3.

Спасибо, хотя для локальной разработки требуется собственный сервер, и он не нужен.

@timneutkens Есть ли шанс, что исправление этой проблемы будет

Что еще более важно, решение для перенаправления не работает для тех, кто поддерживает сайты в этой области, уже настроенные для добавления косой черты, а не для ее удаления в рабочей среде. Я не думаю, что структура должна диктовать этот выбор произвольно.

@AlexSapoznikov «s решение хорошо работает для нас с Netlify (который добавляет слэш по умолчанию). Вот расширенная версия, которая добавляет поддержку параметров запроса:

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

Прошу прощения, потому что я новичок в Next JS, хотя у меня есть опыт разработки программного обеспечения на других SDK и платформах.

Думаю, этот "баг" меня больше всего удивил. Для меня это нарушило «принцип наименьшего удивления». Я просто ожидал, что мои / about / и / будут работать так же, поскольку я поместил index.tsx в свою папку / pages / about /.

Впервые я начал создавать веб-сайты в конце 1990-х с HTML-протоколом FTP на моем сервере, а затем перешел на PHP и Apache, а затем и на серверы Java. Сейчас я специализируюсь на мобильных приложениях. Мне просто кажется странным, что такое поведение не по умолчанию, и что мне придется написать настраиваемую страницу сервера, чтобы исправить это на моем сервере разработки.

Я планирую сделать статический экспорт, поэтому он не будет отображаться в производстве, даже если я не напишу собственный сервер. Тем не менее, это делает разработку и отладку немного более раздражающими.

Можем ли мы получить флаг «следующий разработчик», который исправит это, чтобы нам, ленивым разработчикам, не нужно было писать дополнительную логику маршрутизации только на время разработки / отладки?

Спасибо!

ps: Да, я знаю, что /about и /about/ - это совершенно разные URL-адреса. Я только что получил действительно путают , когда я ставлю index.tsx файл в моем /pages/about/ папку, и обнаружил , что он работает только с /about пути , но не работает с /about/ . Я был бы менее удивлен, если бы все было наоборот.

pps: Было очень запутанно, когда у меня есть компонент <Link></Link> который указывает на /about/ и он работает должным образом. Затем, когда я нажимаю «Обновить» в своем браузере, он сразу же выдает ошибку 404, хотя URL-адрес не изменился. Это было очень удивительно. :-D

Но подождите, становится еще хуже! Мы добавили пользовательскую функцию checkForTrailingSlash внутри _error.js , которая удаляла бы косую черту в конце и перенаправляла. Некоторое время это работало нормально, пока мы (наконец) не добавили пользовательскую страницу 404 и не обнаружили, что с пользовательской страницей 404 Next.js полностью обходит Error . Это означает, что никакая ваша пользовательская логика внутри Error.getInitialProps больше не будет работать, включая проверку на конечные косые черты.

Думаю, я попробую решение _app.js упоминали другие, поскольку собственный сервер просто пока невозможен.

@AlexSapoznikov «s решение хорошо работает для нас с Netlify (который добавляет слэш по умолчанию). Вот расширенная версия, которая добавляет поддержку параметров запроса:

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

В вашем примере кода есть критическая ошибка: запросы к маршруту индекса с параметром запроса выдадут ошибку, поскольку вы в конечном итоге попытаетесь передать только строку запроса в Next.js как asPath .

Это исправляет:

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

Чтобы эта работа работала с SSR, мне пришлось добавить в решение @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)
        : {},
    };
  }

Вероятно, неплохо было бы как-то обобщить эту функциональность в функцию, которая работает как во время SSR, так и во время CSR, и вызывать ее в обоих местах ( getInitialProps и render ).

к

это исправит, но это неправильно. Хм
image

@AlexSapoznikov @pjaws

Ваше решение ставит нас в бесконечный цикл:

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

Контекст

По независящим от нас причинам мы должны использовать параметр exportTrailingSlash в next.config.js .

Нам нужна ссылка на другую страницу, но мы хотим, чтобы ссылка была /somepage?param=whatever .

Кажется, что следующая ссылка преобразует это в /somepage/?param=whatever и мы получаем, что страница не найдена.

Использование вышеприведенного решения позволяет решить проблему с параметрами, но затем при переходе на развернутую страницу, например /somepage/ она входит в бесконечный цикл.

Я думаю, что @ronyeh сделал здесь действительно хороший

Чтобы эта работа работала с SSR, мне пришлось добавить в решение @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)
        : {},
    };
  }

Вероятно, неплохо было бы как-то обобщить эту функциональность в функцию, которая работает как во время SSR, так и во время CSR, и вызывать ее в обоих местах ( getInitialProps и render ).

Это сработало для страниц с getServerSideProps, и теперь URL-адреса с завершающей косой чертой возвращают ту же страницу без 404.
Но есть один сбой, у меня есть несколько страниц, которые используют динамические маршруты и getStaticPaths, я не могу использовать на них getServerSideProps, и поэтому, когда эти динамические маршруты просматриваются с завершающей косой чертой, они сначала возвращают 404, а затем перенаправляют на страницу .

Я работаю с папкой / api / test

  • страницы / api / test.tsx
  • страницы / api / test / [идентификатор] .tsx

это работает для

  • ПОЛУЧИТЬ / api / test
  • GET / api / test / 123
  • ПОЛУЧИТЬ / api / test / 123 /

и я только что обнаружил, что это не работает

  • ПОЛУЧИТЬ / api / test /

не уверен, связана ли это с проблемой
P / D exportTrailingSlash = true не решает эту проблему

Это очень старая проблема, есть ли причина, по которой ее так долго не решают?

Я не уверен, что больше не работает.

Я недооцениваю, что требования следующие:

| | exportTrailingSlash: false | exportTrailingSlash: true |
| ------------------------- | ----------------------- ----- | --------------------------- |
| url заканчивается на / | Не должно работать | Должен работать |
| url не заканчивается на / | Должен работать | Не должно работать |

Это работает, как ожидалось, где:

  • Локально мы используем exportTrailingSlash: false
  • Для развертываний (производственных сборок) мы используем exportTrailingSlash: true а nginx конвертирует url/ в url/index.html

Из того, что я вижу в @ andrescabana86. Это работает там, где не должно: GET /api/test/123/ тогда как GET /api/test/ не работает и не должно.

@Izhaki Я пробовал оба, развертывание на прод ... и у меня не работает

  • ПОЛУЧИТЬ / api / test /

и я использую exportTrailingSlash: true

Я могу попробовать создать публичное репо, если хотите, может, я что-то забыл посередине.

Спасибо за ответ

@ andrescabana86 Я не уверен, насколько здесь поможет публичное репо - это вполне может быть некоторая конфигурация на сервере, на котором вы развертываете.

Мы тестируем наши производственные сборки (с exportTrailingSlash: true ) локально, используя этот скрипт в package.json :

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

Пожалуйста, дайте мне знать, работает ли в вашем браузере http://localhost:5000/api/test/ .

(Обратите внимание, что $(pwd) находится на Mac / Linux - см. Это для Windows)

@Izhaki проблема заключалась в том, что (как следует из первоначального отчета) «конечная косая черта в ссылке для законной страницы работает для навигации на стороне клиента, но приводит к не найденному пакету и 404 при жестком обновлении (ssr)». Таким образом, было несоответствие между поведением изменения маршрута на стороне клиента и жестким обновлением. Я не уверен, сохраняется ли проблема с последней версией Next.js. Я могу доложить здесь, как только протестирую это.

Только что протестировал с 9.4.1 и exportTrailingSlash: true .

Переход к http://localhost:6500/admin/ возвращает 404 при локальной разработке.

Но тот же путь работает при экспорте.

Обратите внимание, что exportTrailingSlash намекает, что это только для _exports_.

Что мы делаем, так это используем:

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

Это означает, что при локальном развитии все работает так, как задумано. И нормально работать при развертывании (через экспорт).

Разве это не правильное и жизнеспособное решение?

Если URL-адрес не работает при разработке, но работает в производственной среде, не думаете ли вы, что это противоречит принципу наименьшего удивления? Я думаю, это все равно следует считать ошибкой.

^ Тем не менее, я почти уверен, что ранее на производстве было конфликтующее поведение между обновлением страницы и событием router.push. Я не знаю, так ли это до сих пор.

@ andrescabana86 @Izhaki exportTrailingSlash имеет отношения к этому. Этот вариант относится к статическому экспорту приложений Next.js. При значении true создается example/index.html , а при значении false создается example.html . Насколько я понимаю, exportTrailingSlash имеет ничего общего с режимом разработки.

Я думаю, что одним из источников путаницы является то, что когда у вас есть exportTrailingSlash next.js добавляет к ссылкам косую черту в конце. Это происходит и в разработке, я не уверен, что это должно происходить? Но в любом случае речь идет не только о example/index.html vs example.html - вам также нужно изменить ссылки.

Если URL-адрес не работает при разработке, но работает в производственной среде, не думаете ли вы, что это противоречит принципу наименьшего удивления? Я думаю, это все равно следует считать ошибкой.

Возможно, я ошибаюсь, но опция exportTrailingSlash предназначалась для серверов nginx, которые не настроены для обслуживания /something.html когда URL-адрес равен /something .

Это не относится к следующему серверу, используемому для локального разработчика. Итак, что работает, а что нет, зависит от того, что обслуживает ваше приложение.

Вы можете сделать так, чтобы, когда exportTrailingSlash истинно, следующий сервер должен поддерживать маршруты, заканчивающиеся косой чертой (хотя это сделает export в exportTrailingSlash несколько неактуальным).

FWIW, над этим уже работают # 13333

Я не очень опытный программист, использую Next.js в основном для многостраничных лендингов. По-видимому, я почти все время использовал следующий обходной путь, не зная о его эффекте. Вот его урезанная версия:

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

В моем случае код немного сложнее, потому что я использую его для поддержки дополнительных префиксов статических URL-адресов и т. Д. Но эта урезанная версия, похоже, отлично работает для обсуждаемой проблемы, независимо от exportTrailingSlash настройка и ее влияние на Link s. Например, URL-адреса /about и /about/ работают нормально.

В нынешнем виде он по сути имитирует собственную маршрутизацию Next.js. Обратная сторона: для этого требуется специальный server.js , и вам придется вручную поддерживать его для «более глубоких» URL-адресов (с дополнительными «подпапками»), например, /company/about/ . Но это кажется относительно простым решением для тех, кто уже использует пользовательские server.js в своем проекте.

Чтобы эта работа работала с SSR, мне пришлось добавить в решение @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)
        : {},
    };
  }

Вероятно, неплохо было бы как-то обобщить эту функциональность в функцию, которая работает как во время SSR, так и во время CSR, и вызывать ее в обоих местах ( getInitialProps и render ).

Это сработало для страниц с getServerSideProps, и теперь URL-адреса с завершающей косой чертой возвращают ту же страницу без 404.
Но есть один сбой, у меня есть несколько страниц, которые используют динамические маршруты и getStaticPaths, я не могу использовать на них getServerSideProps, и поэтому, когда эти динамические маршруты просматриваются с завершающей косой чертой, они сначала возвращают 404, а затем перенаправляют на страницу .

@gauravkrp На самом деле это чрезвычайно важное дополнение, поскольку решение @AlexSapoznikov фактически все равно будет возвращать 404 для страницы в Google (поскольку перенаправление происходит на клиенте). Я полагаю, что SEO - основная причина, по которой многие из нас в первую очередь используют Next.js.

Я также думаю, что включение этого параметра в getInitialProps должно работать повсюду, а часть внутри основной функции здесь не нужна. Главное предостережение здесь заключается в том, что вы теряете автоматическую статическую оптимизацию, имея это - хотя, вероятно, лучше, чем связка 404.

Для некоторого обмена ...

Мой проект - Express + Next.js .
express 4.17.1
next 9.4.5-canary.7

Когда развитие

Динамическое время выполнения

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

Когда Производство

Статический экспорт
Запускаем next build и 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/
});

В заключение

У меня нет проблем с перенаправлением, нажав на клиентское приложение,
также трудное обновление работает на static route .

Но это будет 404 при резком обновлении на dynamic route ,
например /album/[id].jsx или /album/123 ,
Поэтому я с нетерпением жду возможности исправить эту проблему с помощью следующего механизма.

например
При достижении 404 /album/123 ,
сервер должен продолжать предоставлять html-контент,
браузер продолжит загружать страницу без проблем,
когда Next.js загружается, next/router должен автоматически обрабатывать его.

есть ли временное решение этой проблемы на продакшене?

Мы собираемся выпустить функцию, исправляющую это - день или около того!

есть ли временное решение этой проблемы на продакшене?

В этой теме их много, но в настоящее время я использую то, что недавно опубликовал

Следить за PR можно здесь: # 13333

Теперь это решено в next@^9.4.5-canary.17 !

Сколько времени нужно, чтобы функция перешла от канарейки к мастеру?

Теперь это решено в next@^9.4.5-canary.17 !

И как именно разрешается? просто убрав косую черту в конце? если я перейду на сайт « www.site.com/help », можем ли мы выбрать вариант, чтобы оставить конечную косую черту? доступ к « www.site.com/help/ » или « www.site.com/help » оставит или перенаправит или добавит «/» в конце, чтобы иметь: « www.site.com/help/ »

@Valnexus см. # 13333, он включает экспериментальную опцию:

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

Сколько времени нужно, чтобы функция перешла от канарейки к мастеру?

Когда все будет готово. Есть еще крайние случаи в обработке, которые решаются. Как только они будут исправлены, он может перейти в стабильную версию.

@timneutkens @Janpot

Я пробовал последнюю следующую канарейку (9.4.5-canary.27), но когда я создаю страницу test и получаю доступ к www.example/test/ она перенаправляется на www.example/test
Я считаю, что поведение в обоих случаях должно быть одинаковым.

При доступе к www.example/test/ он должен оставаться на www.example/test/ .
При доступе к www.example/test он должен оставаться на www.example/test .
Я тестирую его на Nuxt.js, он работает так же, как я описал выше.

Я считаю, что поведение в обоих случаях должно быть одинаковым.

Причина перенаправления - убедиться, что поисковые системы не видят дублированный контент. Какой у вас точный вариант использования?

Я не понимаю, почему это закрытая проблема, если она еще не включена в стабильную версию. Если я правильно понял, это пока исправлено только в канарейке, верно?

Проблемы закрываются, когда появляется связанный с ними запрос на вытягивание, поскольку они доступны для немедленного использования на canary. Если вам нужна эта функция, обновите ее до канареечного канала.

Звучит отлично. Спасибо, @Timer!

@Janpot Я видел, что https://github.com/issues/ и https://github.com/issues могут получить доступ к одному и тому же поведению без перенаправления.

https://twitter.com/explore/ и https://twitter.com/explore , и этот тоже.

Если есть проблема с поисковыми системами, почему Github и Twitter не исправили ее?
Я думаю, что это поведение по умолчанию для любого веб-сайта.

Нет конкретного варианта использования, это просто мое мнение, что так должно работать.

Если есть проблема с поисковыми системами, почему Github и Twitter не исправили ее?

@armspkt Это не проблема, потому что есть несколько способов ее решить. Например, Twitter использует атрибут <link rel="canonical"> чтобы сообщить поисковым роботам, какую страницу они должны сканировать, а другие версии должны быть помечены как дублированные.

Итак, перенаправление - это жизнеспособный способ улучшить SEO на вашем сайте. Вы можете прочитать больше здесь .

@ziserman Если у нас есть несколько способов решить эту проблему, мы должны сохранить тот же URL-адрес без перенаправления для взаимодействия с пользователем.

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

У Nuxtjs есть несколько вариантов на выбор (undefined, true, false)

Следует ли у Nextjs выбрать несколько вариантов?

Причина перенаправления - убедиться, что поисковые системы не видят дублированный контент. Какой у вас точный вариант использования?

@Janpot Наш API имеет косые черты во многих местах. Последний выпуск вызывает много ошибок 404 на бэкэнде, поскольку URL-адреса с завершающими косыми чертами (/ api / test / -> / api / test) не совпадают.

Я не знаю, сработает ли это для всех, но я нашел это решение, которое работает для меня. Поместите его в файл _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 Я

Последняя версия next@canary исправляет эту ошибку, пожалуйста, обновите ее!

@mlbonniec Я

Последняя версия next@canary исправляет эту ошибку, пожалуйста, обновите ее!

Без проблем!
Однако я обновился раньше, и это не решило проблему.
С npm update

Если последняя версия канарейки Next.js не исправляет ошибку, пожалуйста, откройте новую проблему, чтобы мы могли взглянуть на нее. 🙏

Быстрый вопрос, как проекты с next export справятся с этим изменением? Создавая совершенно новую страницу для каждой страницы для завершающей косой черты? Я не думаю, что экспортированное приложение может указывать перенаправления (или перезаписи) HTTP.

У проектов, которые используют next export , все <Link /> правильно обновлены на стороне клиента, но перенаправление на стороне сервера потребует ручной настройки. В проектах, развернутых с бессерверной целью или next start , эти параметры настраиваются автоматически.

@Timer, когда будет полная версия, нужно ли нам использовать экспериментальную опцию?

@Timer, когда будет полная версия, нужно ли нам использовать экспериментальную опцию?

Нет, будет просто доступно как есть.

Я полагаю, что опция trailingSlash не будет работать для next export ? Как лучше всего перенаправить /page/ на /page (или наоборот), скажем, на страницах github?

Я полагаю, что опция trailingSlash не будет работать для next export ? Как лучше всего перенаправить /page/ на /page (или наоборот), скажем, на страницах github?

Насколько мне известно, на страницах github нет функции перенаправления. Это работает из коробки на vercel.com, хотя это также бесплатно для хобби-проектов (например, на страницах github).

У проектов, которые используют next export , все <Link /> правильно обновлены на стороне клиента, но перенаправление на стороне сервера потребует ручной настройки. В проектах, развернутых с бессерверной целью или next start , эти параметры настраиваются автоматически.

Привет @Timer Не могли бы вы объяснить больше? Как я могу настроить вручную? Итак, вот моя ситуация. На своем веб-сайте я использую next-i18next . После развертывания с помощью next build && next export все внутренние ссылки работают, но при вводе URL вручную НИКТО из них не работает и приводит к ошибке 404. Отсюда я решил использовать trailingSlash:true поэтому вручную введите /pricing Теперь будет работать, но /zh/pricing приведет к ошибке 404.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

jesselee34 picture jesselee34  ·  3Комментарии

formula349 picture formula349  ·  3Комментарии

ghost picture ghost  ·  3Комментарии

lixiaoyan picture lixiaoyan  ·  3Комментарии

knipferrc picture knipferrc  ·  3Комментарии