从SSR组件重定向时传递URL路径(Next.js 13.4/Next-Auth)

huangapple go评论105阅读模式
英文:

Pass a url path when redirecting from a SSR component (Next.js 13.4/Next-Auth)

问题

I've protected certain routes to check if the user is signed in.

我已保护了某些路由以检查用户是否已登录。

I'm using the getServerSession from "next-auth" and redirect from "next/navigation" (the only one available for server component I believe).

我正在使用来自"next-auth"的getServerSession以及来自"next/navigation"的redirect(我相信这是服务器组件中唯一可用的)。

This works fine.

这个工作正常。

But I want to pass the firstly requested URL when redirected, so that when the user signs in, he is redirected to the URL he was trying to get to the first time.

但我想在重定向时传递首次请求的URL,以便用户登录后,他被重定向到他第一次尝试访问的URL。

I believe it would give something like

我认为它会像这样:

  const session = (await getServerSession(authOptions)) as Session;
  if (!session) {
    redirect(`/auth/signin?callbackUrl=${urlPath}`);
  }
  const session = (await getServerSession(authOptions)) as Session;
  if (!session) {
    redirect(`/auth/signin?callbackUrl=${urlPath}`);
  }

Here is the entire code for generating the page:

以下是生成页面的完整代码:

import { authOptions } from '@/lib/helpers/authOptions';
import { redirect } from 'next/navigation';

interface LayoutProps {
  children: React.ReactNode;
}

export default async function Layout({ children}: LayoutProps) {
  const session = (await getServerSession(authOptions)) as Session;
  
  const urlPath = // ??

  if (!session) {
    redirect(`/auth/signin?callbackUrl=${urlPath}`);
  }

  return <>{children}</>;
}
import { authOptions } from '@/lib/helpers/authOptions';
import { redirect } from 'next/navigation';

interface LayoutProps {
  children: React.ReactNode;
}

export default async function Layout({ children}: LayoutProps) {
  const session = (await getServerSession(authOptions)) as Session;
  
  const urlPath = // ??

  if (!session) {
    redirect(`/auth/signin?callbackUrl=${urlPath}`);
  }

  return <>{children}</>;
}

I'm only passing the children as prop, but I don't see the URL path in this.

我只传递children作为属性,但我在其中没有看到URL路径。

I guess I could add to the expected props:

我猜我可以添加到预期的属性中:

  children: React.ReactNode;
  url?: string;
}
export default async function Layout({ children}: LayoutProps) {
  const urlPath = url ? url : '/';
  // Rest of the code
  children: React.ReactNode;
  url?: string;
}
export default async function Layout({ children}: LayoutProps) {
  const urlPath = url ? url : '/';
  // 其余的代码

But I don't know how to pass this URL prop to the layout because I don't know when it is called.

但我不知道如何将此URL属性传递给布局,因为我不知道它何时被调用。

I'd prefer a util server-side function like

我更喜欢像这样的服务器端实用函数:

  const url = req.url;
  return url;
}
  const url = req.url;
  return url;
}

But again, I don't know how to call it and pass it the correct req.

但再次说一遍,我不知道如何调用它并传递正确的req。

There should be a way to obtain the requested URL in the Node.js part of Next, no?!

在Next的Node.js部分应该有一种获取请求的URL的方法,不是吗?

英文:

I've protected certain routes to check if the user is signed in.

I'm using the getServerSession from "next-auth" and redirect from "next/navigation" (the only one available for server component I believe).

const session = (await getServerSession(authOptions)) as Session;
  if (!session) {
    redirect(`/auth/signin`);
  }

This works fine.
But I want to pass the firstly requested url when redirected, so that when the user signin, he is redirected to the url he was trying to get to the first time.

I believe it would give something like

  const urlPath = // I don't know how to obtain the urlPath !
  const session = (await getServerSession(authOptions)) as Session;
  if (!session) {
    redirect(`/auth/signin?callbackUrl=${urlPath}`);
  }

Here is the entire code for generating the page:

import { getServerSession, Session } from 'next-auth';
import { authOptions } from '@/lib/helpers/authOptions';
import { redirect } from 'next/navigation';

interface LayoutProps {
  children: React.ReactNode;
}


export default async function Layout({ children}: LayoutProps) {
  const session = (await getServerSession(authOptions)) as Session;
  
  const urlPath = // ??

  if (!session) {
    redirect(`/auth/signin?callbackUrl=${urlPath}`);
  }

  return <>{children}</>;
}

I'm only passing the children as prop, but I don't see the url path in this.
I guess I could add to the expected props:

interface LayoutProps {
  children: React.ReactNode;
  url?: string;
}
export default async function Layout({ children}: LayoutProps) {
  const urlPath = url ? url : '/';
// Rest of code

but I don't know how to pass this url prop to the layout, because I don't know when it is called.

I'd prefer a util server-side function like

export async function getUrlPath(req) {
  const url = req.url;
  return url;
}

But again, I don't know how to call it and passing it the correct req.
I've tried with a NextResponse (req, res) but the req is always empty...

There should be a way to obtain the requested url in the node.js part of Next, no?!

答案1

得分: 0

在Next.js中,您可以在服务器端代码中使用req对象来获取提到的URL。但是,由于您的Design组件不是标准的Next.js编程接口路由,您无法直接访问req对象。为了实现您的目标,您可以使用自定义服务器中间件。

要将URL路径传递给Format组件,您可以创建一个自定义服务器中间件,从传入请求中提取URL路径,并将其作为prop添加到Design组件中。

以下是如何实现的方式:
创建一个实用函数来获取URL路径:

import { IncomingMessage } from 'http';

export function getUrlPath(req: IncomingMessage): string {
  const url = req.url;
  return url || '/';
}

创建一个自定义服务器中间件,将URL路径连接到req对象中:

// 在pages/api/custom-middleware.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { getUrlPath } from 'utils/getUrlPath';

export default function customMiddleware(
  req: NextApiRequest,
  res: NextApiResponse,
  next: () => void
) {
  // 将URL路径附加到请求对象
  req.urlPath = getUrlPath(req);

  next();
}

在您的next.config.js文件中注册自定义中间件:

// next.config.js
module.exports = {
  async rewrites() {
    return [
      // 添加其他重写规则(如果有的话)
      {
        source: '/:path*',
        destination: '/:path*',
      },
    ];
  },
  async redirects() {
    return [
      // 添加其他重定向规则(如果有的话)
      {
        source: '/auth/signin',
        destination: '/auth/signin',
        permanent: true,
      },
    ];
  },
  async middleware() {
    const customMiddleware = require('./pages/api/custom-middleware').default;
    return customMiddleware;
  },
};

修改您的Design组件以获取urlPath prop:

// components/Layout.tsx
import { getServerSession, Session } from 'next-auth';
import { authOptions } from '@/lib/helpers/authOptions';
import { redirect } from 'next/navigation';
import { NextApiRequest } from 'next';

interface LayoutProps {
  children: React.ReactNode;
  urlPath: string; // 添加urlPath prop
}

export default function Layout({ children, urlPath }: LayoutProps) {
  const session = (await getServerSession(authOptions, req as NextApiRequest)) as Session;

  if (!session) {
    redirect(`/auth/signin?callbackUrl=${urlPath}`);
  }

  return <>{children}</>;
}

现在,当您使用Format组件时,请确保从自定义中间件传递urlPath prop:

// pages/some-page.tsx
import Layout from 'components/Layout';

export default function SomePage({ urlPath }) {
  return (
    <Layout urlPath={urlPath}>
      {/* 您的页面内容 */}
    </Layout>
  );
}

export async function getServerSideProps({ req }) {
  // req对象包含来自自定义中间件的urlPath
  return {
    props: {
      urlPath: req.urlPath,
    },
  };
}

现在,您的Layout组件将接收包含原始URL路径的urlPath prop,并且您可以在身份验证后使用它进行重定向。

希望对您有所帮助!

英文:

In Next.js, you can get to the mentioned URL in the server-side code utilizing the req object from the approaching HTTP demand. Be that as it may, since your Design part is certainly not a standard Next.js Programming interface course, you will not have direct admittance to the req object. To accomplish your objective, you can utilize a custom server middleware.

To pass the URL way to the Format part, you can make a custom server middleware that removes the URL way from the approaching solicitation and adds it as a prop to the Design part.

This is the way you can make it happen:
Create a utility function to get the URL path:

import { IncomingMessage } from &#39;http&#39;;

export function getUrlPath(req: IncomingMessage): string {
  const url = req.url;
  return url || &#39;/&#39;;
}

Make a custom server middleware to join the URL way to the req object:

// in the pages/api/custom-middleware.ts
import { NextApiRequest, NextApiResponse } from &#39;next&#39;;
import { getUrlPath } from &#39;utils/getUrlPath&#39;;

export default function customMiddleware(
  req: NextApiRequest,
  res: NextApiResponse,
  next: () =&gt; void
) {
  // Attach the URL path to the request object
  req.urlPath = getUrlPath(req);

  next();
}

Register the custom middleware in your next.config.js document:

// next.config.js
module.exports = {
  async rewrites() {
    return [
      // Add any other rewrites if you have
      {
        source: &#39;/:path*&#39;,
        destination: &#39;/:path*&#39;,
      },
    ];
  },
  async redirects() {
    return [
      // Add any other redirects if you have
      {
        source: &#39;/auth/signin&#39;,
        destination: &#39;/auth/signin&#39;,
        permanent: true,
      },
    ];
  },
  async middleware() {
    const customMiddleware = require(&#39;./pages/api/custom-middleware&#39;).default;
    return customMiddleware;
  },
};

Alter your Design part to get the urlPath prop:

// components/Layout.tsx
import { getServerSession, Session } from &#39;next-auth&#39;;
import { authOptions } from &#39;@/lib/helpers/authOptions&#39;;
import { redirect } from &#39;next/navigation&#39;;
import { NextApiRequest } from &#39;next&#39;;

interface LayoutProps {
  children: React.ReactNode;
  urlPath: string; // Add the urlPath prop
}

export default function Layout({ children, urlPath }: LayoutProps) {
  const session = (await getServerSession(authOptions, req as NextApiRequest)) as Session;

  if (!session) {
    redirect(`/auth/signin?callbackUrl=${urlPath}`);
  }

  return &lt;&gt;{children}&lt;/&gt;;
}

Presently, when you utilize the Format part, make a point to pass the urlPath prop from the custom middleware:

// pages/some-page.tsx
import Layout from &#39;components/Layout&#39;;

export default function SomePage({ urlPath }) {
  return (
    &lt;Layout urlPath={urlPath}&gt;
      {/* Your page content */}
    &lt;/Layout&gt;
  );
}

export async function getServerSideProps({ req }) {
  // The req object contains the urlPath from the custom middleware
  return {
    props: {
      urlPath: req.urlPath,
    },
  };
}

Now, your Layout component will receive the urlPath prop containing the original URL path, and you can use it for redirection after authentication.

I hope it helps!

答案2

得分: 0

关于Next.js 13.4版本和应用程序切换变体以及涉及服务器活动的服务器端理由,您仍然可以像以前描述的那样将所提到的URL作为回调URL传递。我们将改变方法以使其与服务器活动一起工作。

以下是修改后的解决方案:

创建一个自定义函数以获取所提到的URL:

// utils/getUrlPath.ts
import { IncomingMessage } from 'http';

export function getUrlPath(req: IncomingMessage): string {
  const url = req.url;
  return url || '/';
}

更改您的高级布局以使用服务器活动传递回调URL:

// guest_route/layout.tsx
import { serverAction, getServerSession, Session } from 'next-auth';
import { authOptions } from '@/lib/helpers/authOptions';
import { redirect } from 'next/navigation';
import { getUrlPath } from 'utils/getUrlPath';

export default function LayoutTopLevel({ children }: { children: React.ReactNode }) {
  return serverAction(async ({ req }) => {
    const session = await getServerSession(authOptions, req);
    if (!session) {
      const urlPath = getUrlPath(req);
      redirect(`/auth/signin?callbackUrl=${encodeURIComponent(urlPath)}`);
      return null; // Return null to avoid rendering the children when not authenticated
    }

    return <>{children}</>;
  });
}

在您的受保护页面中使用嵌套布局:

// logged_route/layout.tsx
import LayoutTopLevel from '@/guest_route/layout';

export default function LayoutNested({ children }: { children: React.ReactNode }) {
  // You can add additional checks or customizations specific to this layout if needed.
  // Since you are protecting logged-in routes, you can directly use LayoutTopLevel here.
  return <LayoutTopLevel>{children}</LayoutTopLevel>;
}

您的受保护页面可以保持不变,继续使用嵌套布局:

// logged_route/page.tsx
import LayoutNested from '@/logged_route/layout';

export default function ProtectedPage() {
  return (
    <LayoutNested>
      {/* Your page content */}
    </LayoutNested>
  );
}

通过这种方法,高级布局(guest_route/layout.tsx)使用服务器活动来检查会话状态。如果用户未经过验证,它将立即重定向到带有所提到的URL作为回调URL的登录页面。如果用户已验证,它将继续提供嵌套布局和页面部分以按预期渲染。

请注意,getServerSideProps的概念在应用程序切换版本中未实现,因此使用服务器活动可以作为处理服务器端逻辑的更灵活的替代方法。

英文:

With regards to Next.js 13.4 with the application switch variant and involving server activities for server-side rationale, you can in any case accomplish passing the mentioned URL as a callback URL likewise as portrayed previously. We'll change the way to deal with make it work with server activities.

Here is the altered arrangement:

Make a custom capability to get the mentioned URL:

// utils/getUrlPath.ts
import { IncomingMessage } from &#39;http&#39;;

export function getUrlPath(req: IncomingMessage): string {
  const url = req.url;
  return url || &#39;/&#39;;
}

Change your high level format to pass the callback URL utilizing server activities:

// guest_route/layout.tsx
import { serverAction, getServerSession, Session } from &#39;next-auth&#39;;
import { authOptions } from &#39;@/lib/helpers/authOptions&#39;;
import { redirect } from &#39;next/navigation&#39;;
import { getUrlPath } from &#39;utils/getUrlPath&#39;;

export default function LayoutTopLevel({ children }: { children: React.ReactNode }) {
  return serverAction(async ({ req }) =&gt; {
    const session = await getServerSession(authOptions, req);
    if (!session) {
      const urlPath = getUrlPath(req);
      redirect(`/auth/signin?callbackUrl=${encodeURIComponent(urlPath)}`);
      return null; // Return null to avoid rendering the children when not authenticated
    }

    return &lt;&gt;{children}&lt;/&gt;;
  });
}

Utilize the settled format in your safeguarded pages:

// logged_route/layout.tsx
import LayoutTopLevel from &#39;@/guest_route/layout&#39;;

export default function LayoutNested({ children }: { children: React.ReactNode }) {
  // You can add additional checks or customizations specific to this layout if needed.
  // Since you are protecting logged-in routes, you can directly use LayoutTopLevel here.
  return &lt;LayoutTopLevel&gt;{children}&lt;/LayoutTopLevel&gt;;
}

Your safeguarded page can continue as before, utilizing the settled format:

// logged_route/page.tsx
import LayoutNested from &#39;@/logged_route/layout&#39;;

export default function ProtectedPage() {
  return (
    &lt;LayoutNested&gt;
      {/* Your page content */}
    &lt;/LayoutNested&gt;
  );
}

With this methodology, the high level design (guest_route/layout.tsx) utilizes a server activity to check for the meeting status. In the event that the client isn't verified, it promptly diverts to the signin page with the mentioned URL as the callback URL. In the event that the client is validated, it continues with delivering the settled formats and page parts to no one's surprise.

Kindly note that the idea of getServerSideProps isn't implemented in the application switch rendition, so the utilization of server activities can be an option in contrast to dealing with server-side rationale in a more adaptable way.

huangapple
  • 本文由 发表于 2023年8月4日 20:42:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76836031.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定