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

Next.js App Router中删除API的albumName查询参数未定义问题

删除专辑API中albumName未定义的问题排查与解决

问题描述

我正在开发一个Delete API,用于从MongoDB Atlas的用户文档中删除专辑,但始终遇到albumName属性未定义的错误,找不到原因。用户可以在页面选择要删除的专辑触发操作,数据库中用户的albums是专辑对象数组,每个专辑包含albumName和outfits(数组)两个属性。

Delete API代码

import { connect } from "@/dbConfig/dbConfig";
import User from "@/models/userModel";
import { NextRequest, NextResponse } from "next/server";
import jwt from "jsonwebtoken";

connect();

export async function DELETE(request) {
  const token = request.cookies.get("token")?.value || '';
  const decodedToken = jwt.decode(token);
  const email = decodedToken ? decodedToken.email : '';
  
  const albumName = request.query.albumName; // Extract albumName from query params

  try {
    const userEmail = email.toLowerCase();

    if (!userEmail || !albumName) {
      return NextResponse.json({ error: "Email or albumName not provided" }, { status: 400 });
    }

    const user = await User.findOneAndUpdate(
      { email: userEmail },
      { $pull: { albums: { albumName: albumName } } },
      { new: true }
    );

    if (!user) {
      return NextResponse.json({ error: "User not found" }, { status: 404 });
    }

    if (!user.albums.find(album => album.albumName === albumName)) {
      return NextResponse.json({ error: "Album not found in user's albums" }, { status: 404 });
    }

    return NextResponse.json({ success: true, message: "Album deleted successfully" });
  } catch (error) {
    return NextResponse.json({ error: error.message }, { status: 500 });
  }
}

Albums 页面代码

'use client';

import { useEffect, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faTrashCan } from '@fortawesome/free-solid-svg-icons'
import Link from 'next/link';
import axios from 'axios';

export default function AlbumsPage() {
  const [albumName, setAlbumName] = useState('');
  const [albums, setAlbums] = useState([]);
  const [showModal, setShowModal] = useState(false);
  const [deleteAlbumName, setDeleteAlbum] = useState(''); 
  const [errorMessage, setErrorMessage] = useState('');

  const fetchAlbums = async () => {
    try {
      const response = await axios.get('/api/users/getUserAlbums');
      setAlbums(response.data.albums);
    } catch (error) {
      console.log(error);
    }
  };

  useEffect(() => {
    fetchAlbums();
  }, []);

  const handleCreateAlbum = () => {
    setShowModal(true);
    setErrorMessage('');
  };

  const handleModalInputChange = (e) => {
    setAlbumName(e.target.value);
    setErrorMessage('');
  };

  const handleModalClose = () => {
    setShowModal(false);
    setAlbumName('');
  };

  const handleModalSubmit = async () => {
    try {
      const response = await axios.post('/api/users/createAlbums', { albumName });
      console.log(albumName);

      setAlbums(prevAlbums => [...prevAlbums, { albumName }]);
      setShowModal(false);
      setAlbumName('');
    } catch (error) {
      if (error.response) {
        if (error.response.status === 400) {
          setErrorMessage('Album already exists');
        } else {
          setErrorMessage('Failed to create album');
        }
      } else {
        setErrorMessage('Network error. Please try again.');
      }
    }
  };

  const handleDeleteAlbum = async (albumName) => {
    setDeleteAlbum(albumName); 
    setShowModal(true); 
  };

  const confirmDeleteAlbum = async () => {
    if (deleteAlbumName) { 
      console.log('Deleting album:', deleteAlbumName); // Log the album name
      try {
        const response = await axios.delete(`api/users/deleteAlbums?albumName=${deleteAlbumName}`);
        setShowModal(false);
        setDeleteAlbum('');
        fetchAlbums(); 
      } catch (error) {
        console.log(error);
        console.log('Failed to delete album');
      }
    }
  };
  
  

  useEffect(() => {
    const handleKeyPress = (event) => {
      if (event.key === 'Enter') {
        event.preventDefault();
        handleModalSubmit();
      }
    };
    document.addEventListener('keydown', handleKeyPress);
    return () => {
      document.removeEventListener('keydown', handleKeyPress);
    };
  }, [albumName]);

  return (
    <main>
      {showModal && (
        <div className="fixed top-0 left-0 w-full h-full flex items-center justify-center bg-gray-500 bg-opacity-50 z-50"></div>
      )}
  
      {showModal && (
        <div className="fixed top-0 left-0 w-full h-full flex items-center justify-center z-50">
          <div className="bg-white p-10 rounded-md shadow-lg">
            {deleteAlbumName ? ( 
              <>
                <h2 className="text-lg font-semibold mb-2">Are you sure you want to delete this album?</h2>
                <div className="flex items-center justify-center">
                  <button className="bg-black text-white px-4 py-2 rounded-md mr-2 font-semibold" onClick={confirmDeleteAlbum}>Yes</button>
                  <button className="bg-gray-300 text-gray-800 px-4 py-2 rounded-md font-semibold" onClick={handleModalClose}>No</button>
                </div>
              </>
            ) : (
              <>
                <h2 className="text-lg font-semibold mb-2">Enter Album Name</h2>
                {errorMessage && <p className='text-sm text-red-500 mb-2'>{errorMessage}</p>}
                <input
                  type="text"
                  className="border border-gray-300 rounded-md p-2 mb-2 outline-none"
                  value={albumName}
                  onChange={handleModalInputChange}
                  placeholder='Album Name'
                />
                <div className="flex items-center justify-center">
                  <button className="bg-black text-white px-4 py-2 rounded-md mr-2 font-semibold" onClick={handleModalSubmit}>Create</button>
                  <button className="bg-gray-300 text-gray-800 px-4 py-2 rounded-md font-semibold" onClick={handleModalClose}>Cancel</button>
                </div>
              </>
            )}
          </div>
        </div>
      )}
  
      <div className="flex flex-col items-center justify-center bg-gray-300 shadow-lg p-4">
        <h1 className="font-bold text-3xl mb-4">Albums</h1>
        <button className="bg-black text-white px-4 py-2 rounded-xl mb-4 font-semibold" onClick={handleCreateAlbum}>
          Create Album
        </button>
      </div>
  
      <div className="grid grid-cols-3 gap-4 m-5">
        {albums.map((album, index) => (
          album && album.albumName && ( 
            <div key={index} className="bg-white rounded-lg shadow-md p-4 relative text-center">
              <Link href={`/albums/${album.albumName}`} className="text-lg font-semibold mb-2 hover:opacity-70">{album.albumName}</Link>
              <button
                className="absolute bottom-0 right-0 bg-transparent text-white px-4 py-2 rounded-md font-semibold"
                onClick={() => handleDeleteAlbum(album.albumName)}
                title='Delete Album'
              >
                <FontAwesomeIcon icon={faTrashCan} className='text-red-500' />
              </button>
            </div>
          )
        ))}
      </div>
    </main>
  );
}

问题分析

  1. API端查询参数获取方式错误:在Next.js App Router的DELETE路由函数中,不能直接通过request.query获取URL查询参数,正确的方式是使用request.nextUrl.searchParams。原代码中request.query.albumName会导致albumName始终为undefined。

  2. 前端请求URL路径错误:confirmDeleteAlbum中的axios请求URL缺少开头的/,会导致请求路径被解析为相对路径,可能无法正确到达API端点,进而导致查询参数传递失败。

修复方案

1. API端修复

修改API中获取albumName的代码,替换为正确的查询参数获取方式:

// 替换原有的const albumName = request.query.albumName;
const albumName = request.nextUrl.searchParams.get('albumName') || '';

如果专辑名称包含特殊字符,建议添加解码处理:

const albumName = decodeURIComponent(request.nextUrl.searchParams.get('albumName') || '');

2. 前端修复

修改confirmDeleteAlbum中的请求URL,添加开头的/,确保是绝对路径:

// 替换原有的axios.delete(`api/users/deleteAlbums?albumName=${deleteAlbumName}`);
const encodedAlbumName = encodeURIComponent(deleteAlbumName);
const response = await axios.delete(`/api/users/deleteAlbums?albumName=${encodedAlbumName}`);

使用encodeURIComponent编码专辑名称,避免特殊字符导致的参数传递错误。

额外优化(可选)

在API的校验逻辑中,可以补充对albumName的空值判断,确保逻辑严谨:

if (!userEmail || !albumName.trim()) {
  return NextResponse.json({ error: "Email or albumName not provided" }, { status: 400 });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 01:20:55