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

Next.js登录后用NextResponse.redirect重定向报JSON.parse错误

登录验证通过后使用NextResponse.redirect()出现JSON解析错误的问题

问题描述

我正在学习NextJS,使用新版App Router构建简易食谱应用,此前所有功能均正常,直到实现登录功能时出现问题:当登录验证通过(使用Prisma和MongoDB,仅检查数据库中是否存在同名用户),尝试用NextResponse.redirect()重定向到首页时,反复出现错误:

Uncaught (in promise) SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

若不使用NextResponse.redirect(),仅返回提示“登录成功”的NextResponse.json(),则功能正常。想请教为何登录成功后无法重定向到首页。

相关代码

src/app/api/user/login/route.ts

import { NextResponse } from "next/server";
import prisma from "../../../../utils/prisma";
import bcrypt from "bcryptjs";

export async function POST(request: Request) {
  const body = await request.json();
  const { username, password } = body;
  const user = await prisma.user.findUnique({
    where: {
      username,
    },
  });
  if (!user) {
    return NextResponse.json(
      { error: "User " + username + " not found" },
      { status: 404 }
    );
  }
  const match = await bcrypt.compare(password, user.password);
  if (!match) {
    return NextResponse.json({ error: "Password incorrect" }, { status: 303 });
  }
  NextResponse.redirect("http://localhost:3000");
}

src/app/auth/page.ts

import React, { useState } from "react";

export default function LoginRegister() {
  return (
    <div className="flex justify-center gap-40 text-2xl mt-40">
      <Login />
      <Register />
    </div>
  );
}

const Login = () => {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");

  async function loginHandler() {
    const data = { username: username, password: password };
    await fetch("http://localhost:3000/api/user/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    })
      .then((res) => res.json())
      .then((res) => console.log(res));
  }

  return (
    <form className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
      <div className="mb-4">
        <label
          className="block text-gray-900 font-bold mb-2"
          htmlFor="username"
        >
          Username
        </label>
        <input
          className="shadow border rounded w-full py-2 px-3 text-gray-900  focus:outline-none focus:shadow-outline"
          id="username"
          type="text"
          placeholder="Username"
          onChange={(e) => {
            setUsername(e.target.value);
          }}
          required
        />
      </div>
      <div className="mb-6">
        <label
          className="block text-gray-900 font-bold mb-2"
          htmlFor="password"
        >
          Password
        </label>
        <input
          className="shadow rounded w-full py-2 px-3 text-gray-900 mb-3 focus:outline-none focus:shadow-outline"
          id="password"
          type="password"
          placeholder="******************"
          onChange={(e) => {
            setPassword(e.target.value);
          }}
          required
        />
      </div>
      <div className="flex items-center justify-between">
        <button
          className="bg-blue-500 hover:bg-blue-900 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
          type="button"
          onClick={loginHandler}
        >
          Login
        </button>
      </div>
    </form>
  );
};

问题原因

  1. 前端解析逻辑冲突:前端fetch请求后调用了res.json(),但NextResponse.redirect()返回的是303重定向响应,响应体为空,并非JSON格式。前端尝试解析空内容为JSON,必然抛出语法错误。
  2. API路由未正确返回响应:路由最后一行的NextResponse.redirect()没有加return,导致函数默认返回200空响应,同样会触发前端的JSON解析错误。
  3. 状态码使用不规范:密码错误时返回303状态码不符合HTTP规范,应该用401(未授权),避免和重定向状态码混淆。

解决方案

方案一:前端处理重定向(推荐)

API返回登录成功的JSON响应,前端收到后手动跳转,逻辑更清晰可控:

  1. 修改route.ts的验证通过及密码错误部分:
// 替换原有的NextResponse.redirect
return NextResponse.json({ success: true }, { status: 200 });

// 修正密码错误的状态码
if (!match) {
  return NextResponse.json({ error: "Password incorrect" }, { status: 401 });
}
  1. 修改前端loginHandler:
async function loginHandler() {
  const data = { username: username, password: password };
  await fetch("http://localhost:3000/api/user/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(data),
  })
    .then((res) => res.json())
    .then((res) => {
      if (res.success) {
        window.location.href = "http://localhost:3000"; // 手动跳转首页
      } else {
        alert(res.error); // 展示错误提示
      }
    })
    .catch(err => console.error("请求错误:", err));
}

方案二:API直接重定向(适合非AJAX场景)

若坚持用API重定向,需确保前端不解析空响应,同时修正路由的return问题:

  1. 修改route.ts:
// 必须加return,用request.url构建目标URL更灵活,指定303状态码
return NextResponse.redirect(new URL("/", request.url), { status: 303 });

// 修正密码错误状态码
if (!match) {
  return NextResponse.json({ error: "Password incorrect" }, { status: 401 });
}
  1. 修改前端loginHandler,跳过JSON解析:
async function loginHandler() {
  const data = { username: username, password: password };
  await fetch("http://localhost:3000/api/user/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(data),
  })
    .then((res) => {
      if (res.status === 303) {
        window.location.href = "http://localhost:3000";
      } else {
        return res.json().then(err => alert(err.error));
      }
    })
    .catch(err => console.error("请求错误:", err));
}

内容的提问来源于stack exchange,提问作者Marc Müller

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:22:49