You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13 中使用 res.redirect() 无法跳转的问题排查

问题:Next.js中调用后端跳转接口无法生效

我正在实现设置昵称和头像的功能,操作成功后调用后端接口请求授权并跳转至首页。解决CORS错误后,页面仍然无法跳转,怀疑是Next.js的SPA特性导致刷新式跳转不生效,想知道具体原因及解决办法。

相关代码

Next.js /init/page.tsx 代码

const submitAvatar = async () => {
        const formData = new FormData(); 
        if (!avatarFile) {
            alert('You are using default avatar');
        }
        else {
            formData.append('avatar', avatarFile);
        }

        try {
            const response = await fetch('http://localhost:3001/user/photo', {
                method: 'POST',
                headers: {
                    'Authorization': `Bearer ${value}`,
                },
                body: formData,
            });
            try {
                if (response.ok) {
                    await fetch('http://localhost:3001/auth/init', { //request redirect here
                        method: 'GET',
                        headers: {
                            'Authorization': `Bearer ${value}`,
                        },
                    })
                }
            } catch (error) {
                console.error('redirect', error);
            }
        } catch (error) {
            console.error(error);
        }
    };

后端接口代码

@Get('init')
@UseGuards(InitAuthGuard)
async getInit(
    @GetUser() user: UserEntity,
    @Res() res: Response
){
    const {Token} = await this.authService.existingSign(user.uid);
    
    res.cookie('Token', Token);
    res.redirect('http://localhost:3000');
}

Next.js CORS配置(next.config.js)

/** @type {import('next').NextConfig} */

const nextConfig = {
    async headers() {
        return [
            {
                source: '/(.*)', // Match all paths
                headers: [
                    {
                        key: 'Access-Control-Allow-Origin',
                        value: '*', // Adjust this based on your needs
                    },
                    {
                        key: 'Access-Control-Allow-Credentials',
                        value: 'true',
                    },
                    {
                        key: 'Access-Control-Allow-Headers',
                        value: 'Authorization', // Include any other headers you need
                    },
                ],
            },
        ];
    }
}

module.exports = nextConfig

原因分析

核心问题确实和Next.js的SPA特性相关:

  • 前端用fetch发起异步请求时,浏览器会在后台处理请求响应,不会自动执行后端返回的redirect指令——redirect是针对浏览器直接访问的场景设计的,不适用于AJAX/fetch这类异步请求。
  • 跨域场景下,你的fetch请求未配置credentials: 'include',导致后端设置的Cookie无法被浏览器正确保存,即使跳转也无法携带授权信息。

解决办法

方案一:前端处理跳转(推荐)

后端仅完成Cookie设置和授权逻辑,跳转操作由前端主动触发,符合SPA的交互逻辑:

  1. 修改后端接口:
@Get('init')
@UseGuards(InitAuthGuard)
async getInit(
    @GetUser() user: UserEntity,
    @Res() res: Response
){
    const {Token} = await this.authService.existingSign(user.uid);
    
    res.cookie('Token', Token, {
        domain: 'localhost',
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'lax'
    });
    res.status(200).json({success: true});
}
  1. 修改前端submitAvatar方法:
  • 添加credentials: 'include'确保跨域Cookie正常保存
  • 使用Next.js的useRouter实现无刷新跳转:
import { useRouter } from 'next/navigation';

const router = useRouter();

const submitAvatar = async () => {
        const formData = new FormData(); 
        if (!avatarFile) {
            alert('You are using default avatar');
        }
        else {
            formData.append('avatar', avatarFile);
        }

        try {
            const response = await fetch('http://localhost:3001/user/photo', {
                method: 'POST',
                headers: {
                    'Authorization': `Bearer ${value}`,
                },
                body: formData,
                credentials: 'include'
            });
            if (response.ok) {
                const initResponse = await fetch('http://localhost:3001/auth/init', {
                    method: 'GET',
                    headers: {
                        'Authorization': `Bearer ${value}`,
                    },
                    credentials: 'include'
                });
                if (initResponse.ok) {
                    router.push('/');
                }
            }
        } catch (error) {
            console.error(error);
        }
    };

方案二:直接让浏览器访问后端接口(不推荐)

如果必须保留后端的redirect逻辑,可以放弃fetch,直接让浏览器发起页面跳转请求:

const submitAvatar = async () => {
        // ... 原有头像上传逻辑
        try {
            const response = await fetch('http://localhost:3001/user/photo', {
                method: 'POST',
                headers: {
                    'Authorization': `Bearer ${value}`,
                },
                body: formData,
                credentials: 'include'
            });
            if (response.ok) {
                window.location.href = `http://localhost:3001/auth/init?token=${value}`;
            }
        } catch (error) {
            console.error(error);
        }
    };

这种方式会强制刷新页面,破坏SPA的流畅体验,仅作为临时替代方案。

内容的提问来源于stack exchange,提问作者z2ouu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:05:37