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

Posted

技术标签:

【中文标题】如何在 MERN 应用中实现登录功能?【英文标题】:How do I implement login functionality in a MERN app? 【发布时间】:2021-10-01 18:42:05 【问题描述】:

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

我的主要 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)

我怎样才能做到这一点?

【问题讨论】:

邮递员可以登录吗?您可以添加 sendToken 方法的代码吗?你也能展示你如何设置服务器(索引、应用程序或 server.js)吗? 邮递员也失败了...错误:请提供电子邮件和密码。 在第 25 行输入 console.log(error) - 就在 setError(error.response.data.error) 上方,我收到错误:TypeError: Cannot read property 'push' of undefined at loginHandler ( LoginScreen.jsx:26) 要去尝试一些改变...谢谢大家! Umar 然后首先尝试解决快递方面的问题。您是否添加了 express json 中间件以便它可以从请求正文中读取? 【参考方案1】:

您的TypeError: Cannot read property 'data' of undefined 表示您的代码试图从未定义(null,缺失)对象中读取.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,那就太好了。但事实并非如此,因此您必须自己做出推断。

【讨论】:

以上是关于如何在 MERN 应用中实现登录功能?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Xamarin Forms App 中实现 SSO?

如何在 servlet jsp 中实现生产就绪登录注销功能

如何在 Wildfly Web 应用中实现 LDAP 登录

我们如何在现有应用程序中实现邀请注册?

如何在基于 JWT 的单点登录身份验证架构中实现注销?

在 ASP.NET MVC 中实现“记住我”功能