如何在 MERN 应用程序中实现登录功能?

问题描述

我正在我的本地主机上开发一个(简单易用的)笔记 Web 应用程序(前端的端口 3000,后端的端口 5000)。我在实现用户登录功能时遇到了问题。我会尝试在这里提炼问题。

我的主要 App.jsx 组件有一个 LoginScreen.jsx 组件作为子组件,它在这里

import { useState,useEffect } from 'react';
import { Link } from 'react-router-dom';
import AuthService from '../services/auth.js';
import './LoginScreen.css';

const LoginScreen = ({ history }) => {
  const [email,setEmail] = useState('');
  const [password,setPassword] = useState('');
  const [error,setError] = useState('');

  useEffect(() => {
    if (localStorage.getItem('authToken')) history.push('/');
  },[history]);

  const loginHandler = async e => {
    e.preventDefault();

    try {
      const data = { email,password };

      AuthService.loginUser(data).then(response => {
        localStorage.setItem('authToken',response.data.token);
      });

      history.push('/');
    } catch (error) {
      setError(error.response.data.error);

      setTimeout(() => {
        setError('');
      },5000);
    }
  };

  return (
    <div className="login-screen">
      <form
        className="login-screen__form"
        onSubmit={loginHandler}
        autoComplete="off"
      >
        <h3 className="login-screen__title">Login</h3>
        {error && <span className="error-message">{error}</span>}

        <div className="form-group">
          <label htmlFor="email">Email:</label>
          <input
            type="email"
            required
            id="email"
            placeholder="enter email"
            value={email}
            onChange={e => setEmail(e.target.value)}
            tabIndex={1}
          />
        </div>

        <div className="form-group">
          <label htmlFor="password">
            Password:{' '}
            <Link
              to="/forgotpassword"
              className="login-screen__forgotpassword"
              tabIndex={4}
            >
              Forgot Password?
            </Link>
          </label>
          <input
            type="password"
            required
            id="password"
            placeholder="enter password"
            value={password}
            onChange={e => setPassword(e.target.value)}
            tabIndex={2}
          />
        </div>

        <button type="submit" className="btn btn-primary" tabIndex={3}>
          Login
        </button>
        <span className="login-screen__subtext">
          Don't have an account? <Link to="/register">Register</Link>
        </span>
      </form>
    </div>
  );
};

export default LoginScreen;

用户点击“登录”时,应该会触发 AuthService,如下所示:

import http from '../routing/http-auth.js';

class AuthService {
  loginUser = data => http.post('/login',data);

  registerUser = data => http.post('/register',data);

  userForgotPassword = data => http.post('/forgotpassword',data);

  userPasswordReset = data => http.put('/passwordreset/:resetToken',data);
}

export default new AuthService();

上面文件中的http是一个axios实例,如下图。

import axios from 'axios';

export default axios.create({
  baseURL: 'http://localhost:5000/api/v1/auth',headers: {
    'Content-type': 'application/json'
  }
});

因此,请求被路由到后端,它在这里

import express from 'express';
import AuthController from './auth.controller.js';

const router = express.Router();

router.route('/register').post(AuthController.register);
router.route('/login').post(AuthController.login);
router.route('/forgotpassword').post(AuthController.forgotPassword);
router.route('/passwordreset/:resetToken').put(AuthController.passwordReset);

export default router;

然后在这里

static login = async (req,res,next) => {
    const email = req.body.email;
    const password = req.body.password;

    if (!email || !password) {
      return next(
        new ErrorResponse('please provide an email and password',400)
      );
    }

    try {
      const user = await User.findOne({ email }).select('+password');

      if (!user) return next(new ErrorResponse('invalid credentials',401));

      const isMatch = await user.matchPasswords(password);

      if (!isMatch) return next(new ErrorResponse('invalid credentials',401));

      sendToken(user,200,res);
    } catch (error) {
      next(error);
    }
  };

这一切都适用于一个虚拟应用程序,但现在,它在我的笔记应用程序中不起作用。我收到错误

Uncaught (in promise) TypeError: Cannot read property 'data' of undefined
    at loginHandler (LoginScreen.jsx:27)

我怎样才能做到这一点?

解决方法

您的 TypeError: Cannot read property 'data' of undefined 表示您的代码试图从未定义(空,缺失)对象读取 .data 属性。

在第 27 行,您的代码执行 setError(error.response.data.error); 因此您的错误消息意味着 error 存在但 error.response 不存在。

console.log(error) 放在第 27 行之前(或设置断点),这样您就可以确保理解您捕获的 error 对象。

如果错误消息显示 TypeError: Cannot read property 'data' of an undefined object named error.response,那就太好了。但事实并非如此,因此您必须自己进行推断。