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

Node.js开发GET /albums与POST /purchases接口遇到问题求助

Node.js API 开发问题:GET /albums 和 POST /purchases 接口故障排查

我正在学习Node.js,作为练习需要开发两个接口:

  • GET /albums:获取数据库中所有专辑的列表
  • POST /purchases:创建一条购买记录

我的尝试代码

const mongoose = require('mongoose');
const express = require('express');
const app = express();
const bodyParser = require('body-parser');

// Imports
const Album = require("./models/album");
const Purchase = require("./models/purchase");

// TODO code the API

// Connect to DB
mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true});
var conn = mongoose.connection;
conn.on('connected', function() {
  console.log('database is connected successfully');
});
conn.on('disconnected',function(){
  console.log('database is disconnected successfully');
})
conn.on('error', console.error.bind(console, 'connection error:'));

// Routes
app.get('/albums', function(req, res, next) {
  Album.find({}, (err, albums) => {
    if (!err) {
      res.set({
        'Content-Type': 'application/json',
        'Status': 200,
      })
      return res.end(JSON.stringify(albums));
    } else {
      console.log('Failed to retrieve the Course List: ' + err);
    }
  });
});

// POST method route
app.post('/purchases', (req, res) => {
  const purchase = new Purchase({
    user: req.body.user,
    album: req.body.album
  })
  purchase.save(function (err, post) {
    if (err) {
      return err
    }
    res.json(201, purchase);
  })
})

module.exports = app;

接口开发要求

GET /albums 请求要求

由于这是一个JSON API,需返回JSON格式数据和200状态码(销毁方法除外,需返回204状态码表示无内容)。GET、POST和PUT方法需在data对象中返回Album的title、performer和cost三个字段。响应格式示例如下:
预期格式:

response.body.data = {
  _id: "the id of the album",
  title: "Appetite for Destruction",
  performer: "Guns N' Roses",
  cost: 20
};

POST /purchases 请求要求

POST /purchases接口需接收请求体中的user和album属性,然后在新创建的购买记录中存储这两条记录的引用。响应需包含购买记录以及关联的user和album的完整数据字段。

数据模型Schema

Album Schema

const albumSchema = mongoose.Schema({
  performer: String,
  title: String,
  cost: Number
});

Purchase Schema

const purchaseSchema = mongoose.Schema({
  user: {type: mongoose.Schema.Types.ObjectId, ref: "User"},
  album: {type: mongoose.Schema.Types.ObjectId, ref: "Album"}
})

测试用例

程序需要通过以下两个接口测试用例:

describe("GET /albums", () => {
  it("should return an array of all models", async () => {
    const album = new Album(albumData).save();
    const res = await chai
      .request(app)
      .get("/albums")
    ;
    expect(res.status).to.equal(200);
    expect(res).to.be.json;
    expect(res.body.data).to.be.a("array");
    expect(res.body.data.length).to.equal(1);
    expect(res.body.data[0].title).to.equal(albumData.title);
    expect(res.body.data[0].performer).to.equal(albumData.performer);
    expect(res.body.data[0].cost).to.equal(albumData.cost);
  }).timeout(2000);
});

describe("POST /purchases", () => {
  it("should create a new purchase and return its relations", async () => {
    const otherAlbumData = {
      title: "Sample",
      performer: "Unknown",
      cost: 2,
    };
    const album = await new Album(otherAlbumData).save();
    const user = await new User({name: "James"}).save();
    const res = await chai
      .request(app)
      .post("/purchases")
      .send({user, album})
    ;
    expect(res.status).to.equal(200);
    expect(res).to.be.json;
    expect(res.body.data).to.haveOwnProperty("user");
    expect(res.body.data.user).to.haveOwnProperty("name");
    expect(res.body.data).to.haveOwnProperty("album");
    expect(res.body.data.album).to.haveOwnProperty("title");
    expect(res.body.data.user.name).to.equal(user.name);
    expect(res.body.data.album.title).to.equal(album.title);
  }).timeout(2000);
});

遇到的问题

  1. GET /albums接口无法正确获取数据,测试报错:expected undefined to be an array
  2. POST /purchases接口抛出500错误:Cannot read property 'user' of undefined,但按需求该接口应能从请求体中获取user和album属性。

解决方案

别担心,这两个都是新手开发API时的常见问题,咱们一步步来修复:

1. 修复 POST /purchases 的 Cannot read property 'user' of undefined 错误

这个问题的核心是你没有配置body-parser解析JSON请求体——虽然你引入了body-parser,但没告诉Express要用它处理JSON格式的请求数据。在代码里的// TODO code the API 下方添加这一行:

// 解析JSON格式的请求体
app.use(bodyParser.json());

添加后,Express就能正确解析POST请求中的JSON数据,req.body 就不会再是undefined了。

2. 修复 GET /albums 的测试错误 expected undefined to be an array

你的接口现在直接返回了专辑数组,但测试期望的是把数组包裹在data对象里,同时还有几个小细节需要调整:

  • 不需要手动设置Content-Type和Status,Express的res.json()会自动处理这些响应头
  • 错误发生时要给客户端返回错误响应,不能只在控制台打印日志

修改后的GET /albums代码:

app.get('/albums', async (req, res) => {
  try {
    // 只返回需要的字段,符合接口要求
    const albums = await Album.find({}, '_id title performer cost');
    // 把数组包裹在data对象中返回,匹配测试预期格式
    res.status(200).json({ data: albums });
  } catch (err) {
    console.error('Failed to retrieve albums:', err);
    // 给客户端返回错误状态码和信息
    res.status(500).json({ error: 'Failed to retrieve albums' });
  }
});

我把回调改成了async/await写法,代码更易读;同时用find的第二个参数指定返回字段,确保只返回需求中要求的内容;最后把数组放在data属性里返回,完全匹配测试的预期格式。

3. 完善 POST /purchases 接口,使其通过测试

你的POST接口目前有几个问题:

  • 保存后没有**填充(populate)**关联的user和album数据,测试期望返回完整的关联对象而非ID
  • res.json(201, purchase) 是Express旧版本写法,现在应该用res.status(201).json(...)
  • 错误情况下没有返回响应,会导致客户端超时
  • 需要把返回的购买记录包裹在data对象里

修改后的POST /purchases代码:

app.post('/purchases', async (req, res) => {
  try {
    // 测试用例传入的是完整对象,我们需要提取_id存入数据库
    const purchase = new Purchase({
      user: req.body.user._id,
      album: req.body.album._id
    });
    
    // 保存后填充关联的user和album数据
    const savedPurchase = await purchase.save();
    const populatedPurchase = await Purchase.findById(savedPurchase._id)
      .populate('user', 'name') // 只返回user的name字段
      .populate('album', 'title performer cost'); // 只返回album需要的字段
    
    // 把结果包裹在data对象中返回,匹配测试预期
    res.status(201).json({ data: populatedPurchase });
  } catch (err) {
    console.error('Failed to create purchase:', err);
    res.status(500).json({ error: 'Failed to create purchase' });
  }
});

这里注意测试用例是把完整的user和album对象传过来的,所以我们需要取它们的_id存入Purchase;然后用populate方法把关联的用户和专辑数据填充进来,最后同样把结果放在data对象里返回。

最后几个小建议

  • 尽量用async/await代替回调函数,代码更清晰易维护
  • 所有接口都要处理错误,给客户端返回合适的错误状态码和信息
  • 测试用例里的new Album(albumData).save()最好加上await,避免出现数据库还没保存完就发请求的情况

这样修改后,你的两个接口应该就能顺利通过测试了!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 00:23:11