NextJS中基于Medium API配置动态路由遇阻,求正确配置方案
解决Next.js动态路由[...slug].js配置问题
问题背景
我在Next.js应用中配置动态路由时遇到了问题。我用非官方Medium API展示Medium上的文章,首页已经能展示文章卡片列表,现在想让每张卡片通过Medium的URL路径跳转到应用内对应的页面。
API返回的Medium文章URL是转义后的完整地址,示例:"url":"https:\/\/medium.com\/@myprofile\/some-post-title-81afcccca8a7"。我截取了域名后的路径部分,用来作为Next应用的动态路由。我在post目录下创建了[...slug].js页面,但点击首页卡片跳转到mywebsite.com/post/some-post-title-81afcccca8a7时出现错误,不知道怎么正确配置这个页面。
首页获取文章的getStaticProps代码如下(API要求带密钥和主机头,返回数据需要解析):
const res = await fetch(url, options).catch((e) => { console.error('Error fetching data', e) return { props: { error: true }, // 作为props传给页面组件 } }) const data = await res.json() var article_ids = data.associated_articles var articleDataArr = [] for (const article_id of article_ids) { let article = await fetch( `https://medium2.p.rapidapi.com/article/${article_id}`, options, ) const articleData = await article.text() articleDataArr.push(articleData) } return { props: { articleDataArr, }
解决方案
要让[...slug].js正常工作,你需要完成静态路径生成和根据slug获取对应文章数据两个核心步骤,同时还要处理slug与文章ID的映射关系:
1. 首页数据处理:保存slug与文章ID的映射
首先修改首页的getStaticProps,提取每个文章的slug并和对应的ID绑定,这样动态页面才能通过slug找到对应的文章ID:
const res = await fetch(url, options).catch((e) => { console.error('Error fetching data', e) return { props: { error: true } } }) const data = await res.json() const article_ids = data.associated_articles // 存储slug和文章ID的映射,同时保留文章基础数据 const articleDataArr = [] const slugToIdMap = {} for (const article_id of article_ids) { let articleRes = await fetch( `https://medium2.p.rapidapi.com/article/${article_id}`, options, ) // 改用json()解析数据,方便后续处理 const articleData = await articleRes.json() // 处理转义URL,提取slug const decodedUrl = decodeURIComponent(articleData.url) const path = new URL(decodedUrl).pathname // 截取@profile后的路径作为slug,可根据需求调整规则 const slug = path.split('/').filter(Boolean).slice(1).join('/') articleDataArr.push({ ...articleData, slug }) slugToIdMap[slug] = article_id } return { props: { articleDataArr, slugToIdMap, }, revalidate: 86400, // 可选:开启增量静态再生,每天更新一次 }
同时,首页的文章卡片跳转链接要改为:
// 假设每个article是articleDataArr里的项 <Link href={`/post/${article.slug}`} passHref> <a>文章卡片</a> </Link>
2. 配置[...slug].js的getStaticPaths
这个函数需要告诉Next.js预渲染哪些动态路径:
export async function getStaticPaths() { const options = { method: 'GET', headers: { 'X-RapidAPI-Key': '你的API密钥', 'X-RapidAPI-Host': 'medium2.p.rapidapi.com' } } const res = await fetch('你的文章列表API地址', options) const data = await res.json() const article_ids = data.associated_articles const paths = [] for (const article_id of article_ids) { const articleRes = await fetch(`https://medium2.p.rapidapi.com/article/${article_id}`, options) const articleData = await articleRes.json() const decodedUrl = decodeURIComponent(articleData.url) const path = new URL(decodedUrl).pathname const slug = path.split('/').filter(Boolean).slice(1).join('/') paths.push({ params: { slug: [slug] } }) // [...slug]对应数组格式的参数 } return { paths, fallback: 'blocking', // 可选:新文章自动生成静态页面,避免404 } }
3. 配置[...slug].js的getStaticProps
根据传入的slug参数,找到对应的文章ID并请求详情:
export async function getStaticProps({ params }) { const { slug } = params const targetSlug = slug[0] // [...slug]会把路径转为数组,取第一个元素即可 const options = { method: 'GET', headers: { 'X-RapidAPI-Key': '你的API密钥', 'X-RapidAPI-Host': 'medium2.p.rapidapi.com' } } // 获取文章列表,匹配slug对应的ID const res = await fetch('你的文章列表API地址', options) const data = await res.json() const article_ids = data.associated_articles let articleData = null for (const article_id of article_ids) { const articleRes = await fetch(`https://medium2.p.rapidapi.com/article/${article_id}`, options) const tempArticle = await articleRes.json() const decodedUrl = decodeURIComponent(tempArticle.url) const path = new URL(decodedUrl).pathname const tempSlug = path.split('/').filter(Boolean).slice(1).join('/') if (tempSlug === targetSlug) { articleData = tempArticle break } } if (!articleData) { return { notFound: true } // 找不到文章返回404 } return { props: { articleData }, revalidate: 86400, // 可选:增量更新页面 } }
4. [...slug].js的页面组件
编写页面组件渲染文章内容:
export default function PostPage({ articleData }) { if (!articleData) { return <div>文章不存在</div> } return ( <div className="post-container"> <h1>{articleData.title}</h1> {/* 渲染文章内容,注意处理API返回的HTML格式 */} <div dangerouslySetInnerHTML={{ __html: articleData.content }} /> </div> ) }
关键注意点
- 确保API请求头包含正确的密钥和主机信息,避免请求失败。
- 若文章数量较多,建议将slug与ID的映射缓存到本地文件或数据库,减少重复API请求。
fallback: 'blocking'可让新发布的文章自动生成静态页面,无需重新部署应用。- 必须先对API返回的转义URL解码,再进行路径截取操作。
内容的提问来源于stack exchange,提问作者i-am-niall
相关产品推荐
相关产品推荐

