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
相关产品推荐
相关产品推荐

