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

Next.js14+TypeScript+MongoDB按_id操作用户接口404问题排查

问题

正在修复三个无法正常工作的用户接口:按_id查询、更新、删除用户。调用API时浏览器和Postman均返回「页面未找到」。http://localhost:3000/api/users可正常获取所有用户,但调用http://localhost:3000/api/users/{_id}(替换{_id}为实际用户ID)时报错。


用户控制器代码

// 按_id查询用户
export async function getUserById(req: NextApiRequest, res: NextApiResponse) {
  try {
    console.log('访问的路由:', req.url);

    const { id } = req.query;

    // 从UserModel按_id查询用户
    const user = await connectAndDisconnect(() => UserModel.findById(id as string));

    if (!user) {
      console.log('未找到用户:', id);
      return res.status(404).json({ error: '用户未找到' });
    }

    return res.status(200).json(user);
  } 
  catch (error) {
    console.error('查询用户ID出错:', req.query.id, error);
    return res.status(500).json({ error: '服务器内部错误' });
  }
}

// 按_id更新用户
export async function updateUserById(request: NextRequest) {
  const userId = request.nextUrl.searchParams.get('_id');
  const { body } = request;

  if (!userId) {
    return new NextResponse('无效的用户ID', { status: 400 });
  }

  const updatedUser = await asyncHandler(
    () =>
      connectAndDisconnect(() =>
        UserModel.findByIdAndUpdate(userId, body as Partial<IUser>, { new: true })
      ),
    '更新用户ID出错'
  );

  if (!updatedUser) {
    return new NextResponse('用户未找到', { status: 404 });
  }

  const jsonUpdatedUser = JSON.stringify(updatedUser);

  return new NextResponse(jsonUpdatedUser, {
    headers: { 'Content-Type': 'application/json' },
  });
}

// 按_id删除用户
export async function deleteUserById(request: NextRequest) {
  const userId = request.nextUrl.searchParams.get('_id');

  if (!userId) {
    return new NextResponse('无效的用户ID', { status: 400 });
  }

  const deletedUser = await asyncHandler(
    () => connectAndDisconnect(() => UserModel.findByIdAndDelete(userId)),
    '删除用户ID出错'
  );

  if (!deletedUser) {
    return new NextResponse('用户未找到', { status: 404 });
  }

  const jsonDeletedUser = JSON.stringify(deletedUser);

  return new NextResponse(jsonDeletedUser, {
    headers: { 'Content-Type': 'application/json' },
  });
}

路由文件(api/users.ts)代码

// api/users.ts
import { NextRequest, NextResponse } from 'next/server';
import { createUser, getAllUsers, getUserById, updateUserById, deleteUserById } from 
'@/app/controllers/user';
import handleRequest from '@/app/middleware/handlerequest';

export async function GET(request: NextRequest) {
  return handleRequest(request, {
    // 获取所有用户正常工作
    '/api/users': getAllUsers,
    '/api/users/:id': getUserById,
  });
}

export async function POST(request: NextRequest) {
  return handleRequest(request, {
    // 创建新用户正常工作
    '/api/users': createUser,
  });
}

 export async function PUT(request: NextRequest) {
   return handleRequest(request, {
     '/api/users/:id': updateUserById,
   });
 }

 export async function DELETE(request: NextRequest) {
   return handleRequest(request, {
     '/api/users/:id': deleteUserById,
   });
 }

问题分析与修复方案

1. Next.js路由结构不匹配

Next.js App Router中,动态路由需要创建[id]格式的文件/文件夹。当前路由文件api/users.ts仅能处理/api/users请求,无法匹配/api/users/{id}路径。

修复:
在app/api/users目录下创建[id]/route.ts文件,专门处理单个用户的GET、PUT、DELETE请求。

2. 请求参数获取方式错误

  • getUserById使用了Pages Router的NextApiRequest类型,但路由文件用的是App Router的NextRequest,类型不兼容;且App Router中动态参数需通过params获取,而非req.query。
  • updateUserById和deleteUserById错误地从URL查询参数searchParams获取_id,实际请求的是路径参数,应从路由params中提取。

3. 修复后的代码示例

单个用户路由文件(app/api/users/[id]/route.ts)

import { NextRequest, NextResponse } from 'next/server';
import { connectAndDisconnect } from '@/utils/db';
import { UserModel } from '@/models/User';
import asyncHandler from '@/utils/asyncHandler';

// 获取单个用户
export async function GET(_request: NextRequest, { params }: { params: { id: string } }) {
  try {
    const user = await connectAndDisconnect(() => UserModel.findById(params.id));
    if (!user) {
      return NextResponse.json({ error: '用户未找到' }, { status: 404 });
    }
    return NextResponse.json(user, { status: 200 });
  } catch (error) {
    console.error('查询用户出错:', error);
    return NextResponse.json({ error: '服务器内部错误' }, { status: 500 });
  }
}

// 更新用户
export async function PUT(request: NextRequest, { params }: { params: { id: string } }) {
  const userId = params.id;
  if (!userId) {
    return new NextResponse('无效的用户ID', { status: 400 });
  }
  const body = await request.json();
  const updatedUser = await asyncHandler(
    () => connectAndDisconnect(() => UserModel.findByIdAndUpdate(userId, body, { new: true })),
    '更新用户出错'
  );
  if (!updatedUser) {
    return new NextResponse('用户未找到', { status: 404 });
  }
  return NextResponse.json(updatedUser);
}

// 删除用户
export async function DELETE(_request: NextRequest, { params }: { params: { id: string } }) {
  const userId = params.id;
  if (!userId) {
    return new NextResponse('无效的用户ID', { status: 400 });
  }
  const deletedUser = await asyncHandler(
    () => connectAndDisconnect(() => UserModel.findByIdAndDelete(userId)),
    '删除用户出错'
  );
  if (!deletedUser) {
    return new NextResponse('用户未找到', { status: 404 });
  }
  return NextResponse.json(deletedUser);
}

修改原api/users.ts路由文件

保留获取所有用户和创建用户的逻辑:

// api/users.ts
import { NextRequest, NextResponse } from 'next/server';
import { createUser, getAllUsers } from '@/app/controllers/user';

export async function GET(request: NextRequest) {
  return getAllUsers(request);
}

export async function POST(request: NextRequest) {
  return createUser(request);
}

4. 统一控制器函数类型

将原getUserById的参数类型改为App Router的NextRequest和params,保持与其他函数一致,避免类型冲突。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 17:56:06