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

图书馆管理项目下载计数报错:Cannot read properties of undefined (reading 'customerId')

问题:点击下载按钮增加次数时出现 Cannot read properties of undefined (reading 'customerId') 错误

需求为每次点击下载按钮时将用户下载次数加1,订阅客户每月限5次下载,普通客户无法下载,但触发下载操作时出现上述错误。


相关代码

后端逻辑(customer.js)

exports.numberDownload = async (req, res)=>{
  try {
    const customer =  await Customer.findByIdAndUpdate(req.user.customerId)
    if(customer.countDownload<5){
      await Customer.findByIdAndUpdate(req.user.customerId, {$inc:{countDownload: +1}}, {new: true})
    res.send({message: 'downloaded succefully'})
    }
    else {
      res.status(400).send({message: "You can't download only five per month"})
    }
  } catch (error) {
    res.status(500).send({
      message: error.message || 'some error occured'
    }); 
  }
}

前端页面(book-details.html)

<div class="container mt-5">
  <div class="card mb-3 col-sm-10">
    <div class="row">
      <div class="col-md-4">
        <img [src]="book.photo" alt="book" class="img-fluid rounded-start" />
      </div>
      <div class="col-sm-8">
        <div class="card-body">
          <h3 class="card-title">{{book.title | titlecase}}</h3>
          <p class="card-text">{{book.author | titlecase}}</p>
          <p class="card-text">
            {{book.description | slice:0:100}}
            <span *ngIf="book.description.length > 100">...</span>
          </p>
           <button class="btn btn-primary" type="button" (click)="download()" [disabled]="((countDownload >= 5) || (typeCustomer == 'Normal')) ? true : false">
            <a href="{{book.contentLink}}" target="_blank" download="{{book.title}}">Download</a>
           </button> 
        </div>
      </div>
    </div>
  </div>
</div>

前端服务(service.ts)

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class BookClientService {
  baseURL='http://localhost:3000/api/v1/';
  constructor(private http: HttpClient) { }

  getBooks(){
    return this.http.get(`${this.baseURL}BooksCustomer`);
  }

  getBookById(idBook: any){
    return this.http.get(`${this.baseURL}BookCustomer/${idBook}`);
   }

   countDownload(customerId: any, data: any){
    return this.http.put(`${this.baseURL}download/${customerId}`, data);
   }
}

前端组件(component.ts)

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { BookClientService } from 'src/app/client/list-books/bookClient.service';
import jwt_decode from 'jwt-decode';
import { CustomerService } from 'src/app/views/customers/services/customer.service';

@Component({
  selector: 'app-book-details',
  templateUrl: './book-details.component.html',
  styleUrls: ['./book-details.component.scss']
})
export class BookDetailsComponent implements OnInit {
  book: any;
  idBook: any;
  idCustomer: any;
  countDownload: any;
  typeCustomer: any;
  constructor(private bookClientService: BookClientService, private customerService: CustomerService,private route: ActivatedRoute) { }

  ngOnInit(): void {
    this.idBook = this.route.snapshot.params['id'];
    this.bookClientService.getBookById(this.idBook).subscribe(response => this.book = response);
    
    const token = localStorage.getItem('token') || '';
    let decodedToken: any = jwt_decode(token);
    this.countDownload = decodedToken.countDownload;
    this.typeCustomer = decodedToken.type;
    this.idCustomer = decodedToken.customerId;
  }

  download(){
 this.bookClientService.countDownload(this.idCustomer, this.idBook).subscribe(data=>console.log(data))
 }
}

错误原因及修复方案

1. 后端核心问题:req.user 未定义

错误提示直接说明 req.user 是 undefined,导致无法读取 customerId。

  • 检查该接口路由是否配置了身份验证中间件(如JWT验证),确保请求到达 numberDownload 方法前,req.user 已被正确注入用户信息。
  • 若路由未添加验证逻辑,需补上,避免未登录用户直接调用接口。

2. 后端代码优化:合并冗余操作

当前代码先查询用户再更新,存在冗余且可能引发并发问题,可合并为单次原子操作:

exports.numberDownload = async (req, res)=>{
  try {
    // 原子操作:仅当次数小于5时才更新
    const updatedCustomer = await Customer.findOneAndUpdate(
      { 
        _id: req.user?.customerId, // 用可选链避免未定义报错
        countDownload: { $lt: 5 } 
      },
      { $inc: { countDownload: 1 } },
      { new: true }
    );

    if(updatedCustomer){
      res.send({message: '下载成功'});
    } else {
      res.status(400).send({message: '每月最多下载5次'});
    }
  } catch (error) {
    res.status(500).send({
      message: error.message || '发生未知错误'
    }); 
  }
}

3. 前端交互修复:避免a标签默认跳转干扰

按钮内嵌套a标签会同时触发跳转和接口请求,可能导致请求未完成页面就跳转。修改方案:

  • 移除HTML中的a标签,通过代码触发下载:
download(){
  this.bookClientService.countDownload(this.idCustomer, this.idBook).subscribe({
    next: (data) => {
      console.log(data);
      // 接口调用成功后触发下载
      const link = document.createElement('a');
      link.href = this.book.contentLink;
      link.download = this.book.title;
      link.target = '_blank';
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    },
    error: (err) => {
      console.error('下载次数更新失败', err);
    }
  })
}
  • 对应修改HTML按钮:
<button class="btn btn-primary" type="button" (click)="download()" [disabled]="((countDownload >= 5) || (typeCustomer == 'Normal'))">
  Download
</button>

4. 前端token容错处理

若localStorage中无token,解码会出错,需添加容错逻辑:

ngOnInit(): void {
  // ...其他代码
  const token = localStorage.getItem('token');
  if(token){
    let decodedToken: any = jwt_decode(token);
    this.countDownload = decodedToken.countDownload;
    this.typeCustomer = decodedToken.type;
    this.idCustomer = decodedToken.customerId;
  } else {
    // 无token时默认设为普通用户,禁用下载按钮
    this.typeCustomer = 'Normal';
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 17:20:33