【Nextjs】为什么server action中在try/catch内写redirect操作会跳转失败?

问题描述

比如这个功能,我要的效果是,创建新的文档后,跳转到新的文档编辑页:

下面代码redirect 功能可以正常执行,没有问题

jsx 复制代码
// src/app/.../action.ts
'use server'
import { redirect } from 'next/navigation'

export async function create() {
  // 新建文档
  const newDoc = await db.doc.create({
    data: {
      title: '新建文档 ',
      content: '',
    },
  })
  console.log('新建文档的信息', newDoc)
  redirect(`/work/${newDoc.uid}`)
}

但是我们可能总会担心代码报错抛出异常,所以我们就会想要包一个 try/catch 以防万一

可是为什么加了 try...catch 之后,跳转操作就就失败呢?

jsx 复制代码
'use server'

import { db } from '@/db/db'
import { redirect } from 'next/navigation'

export async function create() {
  try {
    const newDoc = await db.doc.create({
      data: {
        title: '新建文档 '
        content: '',
      },
    })
    console.log('新建文档', newDoc)
    redirect(`/work/${newDoc.uid}`)
  } catch (error) {
    console.error('新建文档失败', error)
  }
}

探究过程

nestjs 官方 issue:github.com/vercel/next...

📄 从官方文档对 redirect 操作的描述可以看到:

❗️ 这是Nextjs官方有意而为之的redirect能够进行跳转操作的底层原理是:

执行到redirect() 这行代码之后,将会抛出一个特别的异常(NEXT_REDIRECT 异常),抛出后 Next 框架会捕获到它,然后执行对应的跳转逻辑,很奇特的执行方式,Next实现 redirect 的方式就是这么骚。

那么知道redirect的跳转原理之后,就不难发现了。

我们使用 try/catch 之后,会将 try{} 中抛出的异常捕获到 catch{} 中,但是我们此时如果只是进行了console.log(err) 操作,那其实就是把这个异常给扼杀在catch{} 块里面了,他没有被真正的抛出到框架中。

如果你仍然头铁,就是要把 redirect 写在 try/catch 里面,想跳转功能正常工作的话,你就必须把redirect引发的异常抛出去,最简单的写法:

jsx 复制代码
export async function create() {
  try {
    // 新建文档
    // ...
    redirect(`/work/${newDoc.uid}`)
  } catch (error) {
    console.error('新建文档失败', error)
    throw error  // ⚠️ 只需要加这行,将异常抛出去
  }
}

你会发现,只需要加上一行throw error跳转操作又回来了!

❓ 但是这就产生了一个新的问题:这岂不是把真正的异常都抛出去了吗?我们写 try/catch 的目的就是为了捕获住代码引发的错误,不让整个程序崩溃,这么直接throw error就白搞了。

说的对,所以我们可以使用 nextjs 库中的一个函数(isRedirectError),将 redirect 的异常过滤出来,如果是它,就放行,直接从 catch{} 抛出去,如果不是,就拦的死死的

这是我在 issue 里面的看到的,有人这么使用,也有其他人这么做成功了。

但是可惜的是,我复现失败了,按照文件路径,我找到了 node\_modules 里面,确实有这个文件,也搜到了这个方法,但是我想使用这个方法,ts 一直给我报错:找不到模块"nest/dist/client/components/redirect"或其相应的类型声明。

无法使用,算了。

我们再转换思路,把其他的逻辑就放在 try/catch 里面,redirect 就放在最后不就可以了吗?:

jsx 复制代码
'use server'
import { redirect } from 'next/navigation'

export async function create() {
  let newDoc = {} as { uid: string }
  try {
    newDoc = await db.doc.create({
      data: {
        title: '新建文档 ',
        content: '',
      },
    })
    console.log('新建文档', newDoc)
  } catch (error) {
    console.error('新建文档失败', error)
  }
  redirect(`/work/${newDoc.uid}`)
}

这样也是可行的,反正 redirect 也会抛出异常,也就是说代码执行到它这里,后面的代码就不会再执行,因此它作为最后一句即可。只不过你得把你的变量提到 try 上面,不然的话由于 let/const作用域问题 try{}外面会访问不到

所以你会发现,如果你想把redirect 塞到try/catch里面,会很蛋疼,只能说官方其实也不建议你这么做 ,直接写,别用 try/catch 就好了。

相关推荐
anOnion8 分钟前
构建无障碍组件之Alert Dialog Pattern
前端·html·交互设计
choke23316 分钟前
[特殊字符] Python 文件与路径操作
java·前端·javascript
云飞云共享云桌面18 分钟前
高性能图形工作站的资源如何共享给10个SolidWorks研发设计用
linux·运维·服务器·前端·网络·数据库·人工智能
Deng94520131430 分钟前
Vue + Flask 前后端分离项目实战:从零搭建一个完整博客系统
前端·vue.js·flask
威迪斯特33 分钟前
Flask:轻量级Web框架的技术本质与工程实践
前端·数据库·后端·python·flask·开发框架·核心架构
wuhen_n1 小时前
JavaScript内置数据结构
开发语言·前端·javascript·数据结构
大鱼前端1 小时前
为什么我说CSS-in-JS是前端“最佳”的糟粕设计?
前端
不爱吃糖的程序媛1 小时前
Capacitor:跨平台Web原生应用开发利器,现已全面适配鸿蒙
前端·华为·harmonyos
AC赳赳老秦1 小时前
2026国产算力新周期:DeepSeek实战适配英伟达H200,引领大模型训练效率跃升
大数据·前端·人工智能·算法·tidb·memcache·deepseek
CHU7290351 小时前
淘宝扭蛋机抽盒小程序前端功能解析:解锁趣味抽盒新体验
前端·小程序