MEAN Stack新手求助:Postman登录报错、注册提示用户已存在
MEAN Stack登录/注册接口问题排查与修复
我是MEAN Stack开发新手,遇到两个接口问题:
- 调用
/api/users/login时返回**"Username or password is invalid!"**错误 - 调用
/api/users/register时返回**"user is already exist"**提示
截图说明
- 注册请求:POST请求到
/api/users/register,返回400状态码,提示用户已存在 - 登录请求:POST请求到
/api/users/login,返回400状态码,提示用户名或密码无效
问题根源与修复方案
1. 注册时用户已存在的问题
注册接口会将邮箱转为小写存储(email: email.toLowerCase()),但查询用户时直接使用原始邮箱await UserModel.findOne({email})。如果用户用大写邮箱(如TEST@EXAMPLE.COM)注册过,后续用相同大写邮箱注册时,数据库里存储的是小写版本,查询时会匹配到已存在的用户,导致报错。
修复:查询用户时也将邮箱转为小写:
// user.router.ts 注册接口中修改查询逻辑 const user = await UserModel.findOne({email: email.toLowerCase()});
2. 登录失败的问题
主要原因有两个:
- 邮箱大小写不匹配:数据库存储的是小写邮箱,登录时传入大写邮箱会查询不到用户
- 种子数据密码未加密:
sample_users中的密码如果是明文,执行/seed接口时会直接存入数据库,后续用bcrypt比对时会失败
修复步骤:
- 登录接口中同样转为小写查询邮箱:
// user.router.ts 登录接口中修改查询逻辑 const user = await UserModel.findOne({email: email.toLowerCase()});
- 修复种子数据加密逻辑:
// user.router.ts 的seed接口中修改 const encryptedUsers = sample_users.map(async user => ({ ...user, password: await bcrypt.hash(user.password, 10) })); await UserModel.create(await Promise.all(encryptedUsers));
- 检查Postman请求参数:确保
email和password参数名拼写正确,无额外空格或格式错误
3. 其他检查点
- 确认数据库连接正常,用户数据已正确写入
- 检查
process.env.JWT_SECRET是否配置(当前错误与JWT无关,但需确保后续功能正常) - 查看后端控制台日志:代码中已添加
console.log,可确认接收到的邮箱、密码,以及查询到的用户密码是否符合预期
完整修改后代码
user.router.ts
import {Router} from 'express'; import { sample_users } from '../data'; import jwt from 'jsonwebtoken'; import asyncHandler from 'express-async-handler'; import { User, UserModel } from '../models/user.model'; import { HTTP_BAD_REQUEST } from '../constants/http_status'; import bcrypt from 'bcryptjs'; const router = Router(); router.get("/seed", asyncHandler( async (req, res) => { const usersCount = await UserModel.countDocuments(); if(usersCount> 0){ res.send("Seed is already done!"); return; } // 对种子数据密码加密后存入 const encryptedUsers = sample_users.map(async user => ({ ...user, password: await bcrypt.hash(user.password, 10) })); await UserModel.create(await Promise.all(encryptedUsers)); res.send("Seed Is Done!"); } )) router.post("/login", asyncHandler( async (req, res) => { const {email, password} = req.body; console.log(email + " userEmail"); console.log(password + " userPassword" ); // 转为小写查询用户 const user = await UserModel.findOne({email: email.toLowerCase()}); console.log(user?.password); if(user && (await bcrypt.compare(password,user.password))) { res.send(generateTokenReponse(user)); } else{ res.status(HTTP_BAD_REQUEST).send("Username or password is invalid!"); } } )) router.post('/register', asyncHandler( async (req, res) => { const {name, email, password, address} = req.body; // 转为小写查询用户是否存在 const user = await UserModel.findOne({email: email.toLowerCase()}); if(user){ res.status(HTTP_BAD_REQUEST) .send('User is already exist, please login!'); return; } const encryptedPassword = await bcrypt.hash(password, 10); const newUser:User = { id:'', name, email: email.toLowerCase(), password: encryptedPassword, address, isAdmin: false } const dbUser = await UserModel.create(newUser); res.send(generateTokenReponse(dbUser)); } )) const generateTokenReponse = (user : User) => { const token = jwt.sign({ id: user.id, email:user.email, isAdmin: user.isAdmin },process.env.JWT_SECRET!,{ expiresIn:"30d" }); return { id: user.id, email: user.email, name: user.name, address: user.address, isAdmin: user.isAdmin, token: token }; } export default router;
user.service.ts
import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { ToastrService } from 'ngx-toastr'; import { BehaviorSubject, Observable, tap } from 'rxjs'; import { USER_LOGIN_URL, USER_REGISTER_URL } from '../shared/constants/urls'; import { IUserLogin } from '../shared/interfaces/IUserLogin'; import { IUserRegister } from '../shared/interfaces/IUserRegister'; import { User } from '../shared/models/User'; const USER_KEY = 'User'; @Injectable({ providedIn: 'root' }) export class UserService { private userSubject = new BehaviorSubject<User>(this.getUserFromLocalStorage()); public userObservable:Observable<User>; constructor(private http:HttpClient, private toastrService:ToastrService) { this.userObservable = this.userSubject.asObservable(); } public get currentUser():User{ return this.userSubject.value; } login(userLogin:IUserLogin):Observable<User>{ return this.http.post<User>(USER_LOGIN_URL, userLogin).pipe( tap({ next: (user) =>{ this.setUserToLocalStorage(user); this.userSubject.next(user); this.toastrService.success( `Welcome to Foodmine ${user.name}!`, 'Login Successful' ) }, error: (errorResponse) => { this.toastrService.error(errorResponse.error, 'Login Failed'); } }) ); } register(userRegiser:IUserRegister): Observable<User>{ return this.http.post<User>(USER_REGISTER_URL, userRegiser).pipe( tap({ next: (user) => { this.setUserToLocalStorage(user); this.userSubject.next(user); this.toastrService.success( `Welcome to the Foodmine ${user.name}`, 'Register Successful' ) }, error: (errorResponse) => { this.toastrService.error(errorResponse.error, 'Register Failed') } }) ) } logout(){ this.userSubject.next(new User()); localStorage.removeItem(USER_KEY); window.location.reload(); } private setUserToLocalStorage(user:User){ localStorage.setItem(USER_KEY, JSON.stringify(user)); } private getUserFromLocalStorage():User{ const userJson = localStorage.getItem(USER_KEY); if(userJson) return JSON.parse(userJson) as User; return new User(); } }
login.component.ts
import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { UserService } from 'src/app/services/user.service'; @Component({ selector: 'app-login-page', templateUrl: './login-page.component.html', styleUrls: ['./login-page.component.css'] }) export class LoginPageComponent implements OnInit { loginForm!:FormGroup; isSubmitted = false; returnUrl = ''; constructor(private formBuilder: FormBuilder , private userService:UserService, private activatedRoute:ActivatedRoute, private router:Router) { } ngOnInit(): void { this.loginForm = this.formBuilder.group({ email:['', [Validators.required,Validators.email]], password:['', Validators.required] }); this.returnUrl = this.activatedRoute.snapshot.queryParams.returnUrl; } get fc(){ return this.loginForm.controls; } submit(){ this.isSubmitted = true; if(this.loginForm.invalid) return; this.userService.login({email:this.fc.email.value, password: this.fc.password.value}).subscribe(() => { this.router.navigateByUrl(this.returnUrl); }); } }
auth.guard.ts
import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { Observable } from 'rxjs'; import { UserService } from 'src/app/services/user.service'; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { constructor(private userService:UserService, private router:Router){} canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { if(this.userService.currentUser.token) return true; this.router.navigate(['/login'], {queryParams:{returnUrl: state.url}}) return false; } }
内容的提问来源于stack exchange,提问作者DevNadim
相关产品推荐
相关产品推荐

