1. ホーム
  2. html

[解決済み] Next.js 別ページにリダイレクトさせる

2022-03-08 19:20:01

質問

私は Next.js で、スタートページ( / ) から /hello-nextjs を例にとって説明します。ユーザーがページを読み込んだ後、パスが === かどうかを判断します。 / にリダイレクトします。 /hello-nextjs

react-router のようなことをしています。

<Switch>
  <Route path="/hello-nextjs" exact component={HelloNextjs} />
  <Redirect to="/hello-nextjs" /> // or <Route path="/" exact render={() => <Redirect to="/hello-nextjs" />} />
</Switch>

解決方法は?

注意事項

まず、クライアントサイドリダイレクト(React内)、サーバーサイドリダイレクト(301 HTTPレスポンス)、サーバーサイドリダイレクト+認証(301 HTTPレスポンスだが認証チェックのロジックもある)のいずれが必要かを判断する必要があります。 .

これが、私が書ける最も完全な答えです。しかし、ほとんどのシナリオでは、このどれもが必要ではありません。Reactアプリで行うようなリダイレクトを行うだけです。 クライアントサイドのリダイレクションを優先してください。 を使うだけです。 useEffect + router.push で、終わりです。

サーバーサイドのリダイレクトは、特にプライベートなページを安全にしたい場合などに魅力的ですが、本当に必要なのかどうかを見極める必要があります。通常、必要ありません。認証トークンやリフレッシュトークンを管理するような、予想外の複雑さを引き起こすからです。その代わり、例えばゲートウェイサーバーやリバースプロキシ、あるいはその種のチェックを処理するためのアップフロントサーバーをアーキテクチャに追加するとよいでしょう。

Next.jsはReactアプリであり、SSRのようなNext.jsの高度な機能を使うにはコストがかかりますが、それはあなたの文脈で正当化されるべきものであることを心に留めておいてください。

Next 9.4 の回答

こんにちは、すべてのシナリオで動作するコンポーネントの例です。

バルカンネクストスターターwithPrivate access

使用例はこちら

答えは膨大なので、もし私が何らかの形でSOのルールを破ったら申し訳ありませんが、私は180行のコード片を貼り付けたくありません。SSRと静的エクスポートの両方をサポートしたい場合、Nextでリダイレクトを処理する簡単なパターンはありません。

以下のシナリオは、それぞれ特定のパターンが必要です。

  • サーバーサイドレンダリング:許可されればページをレンダリングし、許可されなければHTTPリダイレクトを行う。
  • 静的レンダリング (サーバーサイド): 何もレンダリングしませんが、ページをビルドに含めます。
  • クライアント側レンダリング、静的エクスポート後:クライアント側でユーザーが認証済みかどうかをチェックし、リダイレクトするかどうかを決定します。このチェック中またはリダイレクト中は、何も表示しません(またはローダーを表示します)。
  • next/routerを使用したクライアントリダイレクト後のクライアントサイドレンダリング:同じ動作です。
  • SSR後のクライアント側レンダリング: 最初のレンダリングで、getInitialPropsによって渡されたpropsを使用して、ユーザーが許可されているかどうかを直接判断しています。ほんの少し速くなり、空白のフラッシュを避けることができます。

この記事の執筆時点(Next 9.4)では、このような場合は getInitialProps ではなく getServerSideProps を行うことができなくなります。 next export .

次回の9.5アップデートは

コメントで @Arthur が述べているように、9.5 では、以下のようなセットアップも可能です。 next.config.jsでリダイレクトを行う。 . この機能の限界は私にはまだわかりませんが、例えばページを移動する必要がある場合や、限られた期間だけアクセスを許可する場合など、グローバルなリダイレクションになるようです。 つまり、リクエストコンテキストにアクセスできないので、例えば認証を扱うためのものではありません。繰り返しになりますが、確認が必要です。

次の10件の新着ドキュメント更新

この解決策は、認証に依存するリダイレクトに特化したものです。

認証パターンがドキュメント化されました

から認証されるのは好きではありません。 getServerSideProps というのも、リフレッシュトークンの処理など、高度なパターンを設定するのはかなり遅すぎると私は考えているからです。しかし、これが正式な解決策なのです。

また、次のようなアプローチもあります。 このチケットで Vercelのダッシュボードの動作に基づき、認証されていないコンテンツのフラッシュを防ぐことができます(執筆時)。

次の10.2ヘッダーとクッキーに基づく書き換えのアップデート

次の10.2で紹介します。 リライト ヘッダーとクッキーに基づくものです。 これは、認証クッキーやヘッダーの存在に基づいて、サーバーサイドでリダイレクトするための素晴らしい方法です。

ただし、留意してほしいのは、これは セキュア リダイレクトを行います。 ユーザーは偽のトークンでリクエストヘッダを変更することができます。トークンの有効性を実際にチェックし、ヘッダーを正しく設定するために、ゲートウェイ、リバースプロキシ、またはアップフロントサーバーが依然として必要です。

編集:URLは変更されないことに注意してください。リライトは、URLを変更することなく、アプリケーションの既存のページを指す=> それによって、quot;仮想URLを持つことができます。

使用例:あるページがあるとします。 src/contact.tsx が翻訳され、国際化リダイレクトが設定されています。ページ名そのもの ("contact") を翻訳するには、次のように書き換えます。 /de/kontact から /de/contact .

次の12件の更新

現在 ミドルウェア は、サーバーサイドのリダイレクトを完全に制御することができます。

しかし、ほとんどの場合、クライアント側のリダイレクトとチェックで十分であることを再度心に留めておいてください。

古い回答(動作するが、静的レンダリングが乱雑になる)

準公式な例

with-cookie-auth でリダイレクトする例です。 getInitialProps . 有効なパターンかどうかはまだわかりませんが、コードはこんな感じです。

Profile.getInitialProps = async ctx => {
  const { token } = nextCookie(ctx)
  const apiUrl = getHost(ctx.req) + '/api/profile'

  const redirectOnError = () =>
    typeof window !== 'undefined'
      ? Router.push('/login')
      : ctx.res.writeHead(302, { Location: '/login' }).end()

  try {
    const response = await fetch(apiUrl, {
      credentials: 'include',
      headers: {
        Authorization: JSON.stringify({ token }),
      },
    })

    if (response.ok) {
      const js = await response.json()
      console.log('js', js)
      return js
    } else {
      // https://github.com/developit/unfetch#caveats
      return await redirectOnError()
    }
  } catch (error) {
    // Implementation or Network error
    return redirectOnError()
  }
}

サーバーサイドとクライアントサイドの両方を処理します。そのため fetch の呼び出しは、実際に認証トークンを取得するものであり、これを別の関数にカプセル化したいと思うかもしれません。

代わりにアドバイスしたいこと

 1. サーバーサイドレンダリング時のリダイレクト(SSR時のフラッシュは避ける)

最も一般的なケースです。初回ロード時のページフラッシュを避けるために、この時点でリダイレクトさせたいのです。

MyApp.getInitialProps = async appContext => {
    const currentUser = await getCurrentUser(); // define this beforehand
    const appProps = await App.getInitialProps(appContext);
    // check that we are in SSR mode (NOT static and NOT client-side)
    if (typeof window === "undefined" && appContext.ctx.res.writeHead) {
      if (!currentUser && !isPublicRoute(appContext.router.pathname)) {
          appContext.ctx.res.writeHead(302, { Location: "/account/login" });
          appContext.ctx.res.end();
      }
    }
    return { ...appProps, currentUser };
  };

 2. componentDidMountでリダイレクト(静的モードなどSSRが無効な場合に有効)

これは、クライアントサイドレンダリングのフォールバックです。

  componentDidMount() {
    const { currentUser, router } = this.props;
    if (!currentUser && !isPublicRoute(router.pathname)) {
      Router.push("/account/login");
    }
  }

静的ビルド中にリダイレクトできないので、この点を追加して静的モードで初期ページを点滅させることは避けられませんでしたが、通常のアプローチよりはマシなようです。進展があれば編集してみようと思います。

完全な例はこちら

関連する問題で、悲しいことにクライアントのみの回答で終わっているもの

リダイレクトに関する新たな課題を公開しました。