Next.js:如何用正则将动态Post URL加入protectedRoutes数组?
Next.js 动态路由权限控制解决方案
我在开发带认证功能的Next.js项目,目前将需要保护的URL以字符串形式存入protectedRoutes数组,未登录用户访问这些路由会被重定向。现在需要把所有/post/[postId]格式的动态帖子页面加入保护,但直接添加正则表达式无效,也不想遍历Firebase中的所有帖子ID逐个添加(避免数组过长影响搜索效率)。
尝试过的无效代码
const postURL = new RegExp("/post/") const protectedRoutes = ["/home", "/profile", postURL];
希望避免的写法
const postID = getPostID() // 从Firebase获取帖子ID的函数 postID.forEach((id) => { protectedRoutes.push(`/post/${id}`); });
现有项目代码
_app.js
import PrivateRoute from "@/components/PrivateRoute"; import { AuthProvider } from "@/contexts/AuthContext"; import "@/styles/globals.css"; export default function App({ Component, pageProps }) { const protectedRoutes = ["/home", "/profile"]; const publicRoutes = [ "/", "/sign-in", "/sign-in/phone", "/sign-up", "/reset-password", ]; return ( <AuthProvider> <PrivateRoute protectedRoutes={protectedRoutes} publicRoutes={publicRoutes} > <Component {...pageProps} /> </PrivateRoute> </AuthProvider> ); }
PrivateRoute.jsx
import { useEffect } from "react"; import { useRouter } from "next/router"; import { useAuth } from "@/contexts/AuthContext"; import FullPageLoader from "./FullPageLoader"; export default function PrivateRoute({ protectedRoutes, publicRoutes, children, }) { const router = useRouter(); const { currentUser, loading } = useAuth(); const pathIsProtected = protectedRoutes.indexOf(router.pathname) !== -1; const pathIsPublic = publicRoutes.indexOf(router.pathname) !== -1; useEffect(() => { if (!loading && !currentUser && pathIsProtected) { router.push("/sign-in"); } if (!loading && currentUser && pathIsPublic) { router.push("/home"); } }, [currentUser, loading, pathIsProtected, pathIsPublic]); if ((loading || !currentUser) && pathIsProtected) { return <FullPageLoader />; } if ((loading || currentUser) && pathIsPublic) { return <FullPageLoader />; } return children; }
解决方案
方案一:修改全局权限判断逻辑,支持正则匹配
核心思路是让protectedRoutes数组同时支持字符串和正则表达式,然后修改判断逻辑,分别处理两种类型的路由规则。
- 更新_app.js中的路由规则
在protectedRoutes中加入匹配所有帖子路径的正则:
const protectedRoutes = ["/home", "/profile", /^\/post\/.+$/];
正则/^\/post\/.+$/表示匹配以/post/开头、后面包含任意字符的路径,覆盖所有/post/[postId]格式的动态路由。
- 修改PrivateRoute中的判断逻辑
替换原来的indexOf判断,改用some方法遍历规则,区分字符串和正则的匹配方式:
// 替换原有的pathIsProtected判断 const pathIsProtected = protectedRoutes.some(route => { if (typeof route === 'string') { return router.pathname === route; } else if (route instanceof RegExp) { return route.test(router.pathname); } return false; }); // 若publicRoutes未来也需要支持动态规则,可同步修改pathIsPublic const pathIsPublic = publicRoutes.some(route => { if (typeof route === 'string') { return router.pathname === route; } else if (route instanceof RegExp) { return route.test(router.pathname); } return false; });
这样既不用维护海量的帖子ID路由,又能高效匹配所有动态帖子路径,代码改动小且逻辑统一。
方案二:页面级单独验证(可选补充)
如果需要对单个帖子页面做更精细的权限控制(比如部分帖子允许未登录查看),可以直接在/post/[postId].js页面中添加认证逻辑:
import { useEffect } from 'react'; import { useRouter } from 'next/router'; import { useAuth } from '@/contexts/AuthContext'; import FullPageLoader from '../components/FullPageLoader'; export default function PostPage({ postData }) { const router = useRouter(); const { currentUser, loading } = useAuth(); useEffect(() => { if (!loading && !currentUser) { router.push('/sign-in'); } }, [currentUser, loading]); if (loading || !currentUser) { return <FullPageLoader />; } return <div>{postData.content}</div>; } // 若使用SSR,可在getServerSideProps中提前验证(推荐) export async function getServerSideProps(context) { // 从请求中获取认证状态(比如cookie中的session) const session = await getSession(context); if (!session) { return { redirect: { destination: '/sign-in', permanent: false, }, }; } // 获取对应帖子数据 const postData = await fetchPost(context.params.postId); return { props: { postData }, }; }
这种方式适合特殊场景下的权限定制,全局控制还是推荐方案一。
内容的提问来源于stack exchange,提问作者Lauris Kwan
相关产品推荐
相关产品推荐

