图书馆管理项目下载计数报错: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
相关产品推荐
相关产品推荐

