【问题标题】:REACT-REDUX-EXPRESS, unable to update user fields, 404 ERRORREACT-REDUX-EXPRESS,无法更新用户字段,404 错误
【发布时间】:2021-09-02 06:35:21
【问题描述】:

我正在尝试更新用户信息(姓名、电子邮件、密码),但出现两个错误。

  1. 如果我尝试输入和更新姓名或电子邮件(数据未更新),我会收到 404 错误:

请求:

回应:

  1. 但是,如果我尝试输入和更新密码,它会被更新(因为我必须在再次登录时输入新密码),但它会在提交按钮后立即显示这些错误:

我尝试通过将数据直接放入 thunder 客户端 来更新用户数据,并且它正在更新:

这是我的源代码:

前端

ProfileScreen.js

import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import ErrorMessage from "../../components/ErrorMessage/ErrorMessage";
import Message from "../../components/Message/Message";
import Loader from "../../components/Loader/Loader";
import {
  getUserDetails,
  updateUserProfile,
} from "../../redux/actions/userActions";
import "./ProfileScreen.scss";
import { USER_UPDATE_PROFILE_RESET } from "../../redux/constants/userConstants";

const ProfileScreen = ({ location, history }) => {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [message, setMessage] = useState(null);

  const regex =
    /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/;

  const dispatch = useDispatch();

  const userDetails = useSelector((state) => state.userDetails);
  const { loading, error, user } = userDetails;

  const userLogin = useSelector((state) => state.userLogin);
  const { userInfo } = userLogin;

  const userUpdateProfile = useSelector((state) => state.userUpdateProfile);
  const { success } = userUpdateProfile;

  useEffect(() => {
    if (!userInfo) {
      history.push("/login");
    } else {
      if (!user.name || !user || success) {
        dispatch({ type: USER_UPDATE_PROFILE_RESET });
        dispatch(getUserDetails("profile"));
      } else {
        setName(user.name);
        setEmail(user.email);
      }
    }
  }, [history, userInfo, dispatch, user, success]);

  const passwordHandler = (e) => {
    e.preventDefault();

    !regex.test(password)
      ? setMessage(
          "Password must contain atleast 8 characters & one alphabet, number & special character"
        )
      : password !== confirmPassword
      ? setMessage("Passwords do not match!")
      : dispatch(updateUserProfile({ id: user._id, password }));
  };

  const enameHandler = (e) => {
    e.preventDefault();
    dispatch(updateUserProfile({ id: user._id, name, email, password }));
  };

  return (
    <>
      <div className="profile-container">
        <div className="profile">
          {message && <ErrorMessage error={message} />}
          {error && <ErrorMessage error={error} />}
          {success && <Message success={"Profile Updated"} />}
          <div className="profile-form">
            <h2>User Profile</h2>
            {loading ? (
              <Loader />
            ) : (
              <div>
                <form onSubmit={enameHandler}>
                  <div className="profile-form-items">
                    <h3> Update Name or Email</h3>
                    <input
                      className="profile-input"
                      type="name"
                      placeholder="New Name"
                      value={name}
                      onChange={(e) => setName(e.target.value)}
                    />
                    <input
                      className="profile-input"
                      type="email"
                      placeholder="New Email address"
                      value={email}
                      onChange={(e) => setEmail(e.target.value)}
                    />

                    <button type="submit" value="submit">
                      Update
                    </button>
                  </div>
                </form>
                <form onSubmit={passwordHandler}>
                  <div className="profile-form-items">
                    <h3>Update Password</h3>
                    <input
                      className="profile-input"
                      type="password"
                      placeholder="New Password"
                      value={password}
                      onChange={(e) => setPassword(e.target.value)}
                    />
                    <input
                      className="profile-input"
                      type="password"
                      placeholder="Confirm New Password"
                      value={confirmPassword}
                      onChange={(e) => setConfirmPassword(e.target.value)}
                    />
                    <button type="submit" value="submit">
                      Update
                    </button>
                  </div>
                </form>
              </div>
            )}
          </div>
        </div>
    </>
  );
};

export default ProfileScreen;

userAction.js

import axios from "axios";
import {
  USER_DETAILS_FAIL,
  USER_DETAILS_REQUEST,
  USER_DETAILS_SUCCESS,
  USER_LOGIN_FAIL,
  USER_LOGIN_REQUEST,
  USER_LOGIN_SUCCESS,
  USER_LOGOUT,
  USER_REGISTER_FAIL,
  USER_REGISTER_REQUEST,
  USER_REGISTER_SUCCESS,
  USER_UPDATE_PROFILE_FAIL,
  USER_UPDATE_PROFILE_REQUEST,
  USER_UPDATE_PROFILE_SUCCESS,
} from "../constants/userConstants";

export const login = (email, password) => async (dispatch) => {
  try {
    dispatch({
      type: USER_LOGIN_REQUEST,
    });

    const config = {
      headers: {
        "Content-Type": "application/json",
      },
    };

    const { data } = await axios.post(
      "/api/users/login",
      { email, password },
      config
    );

    dispatch({
      type: USER_LOGIN_SUCCESS,
      payload: data,
    });

    localStorage.setItem("userInfo", JSON.stringify(data));
  } catch (error) {
    dispatch({
      type: USER_LOGIN_FAIL,
      payload:
        error.response && error.response.data.message
          ? error.response.data.message
          : error.message,
    });
  }
};

export const logout = () => (dispatch) => {
  localStorage.removeItem("userInfo");
  dispatch({
    type: USER_LOGOUT,
  });
};

export const register = (name, email, password) => async (dispatch) => {
  try {
    dispatch({
      type: USER_REGISTER_REQUEST,
    });

    const config = {
      headers: {
        "Content-Type": "application/json",
      },
    };

    const { data } = await axios.post(
      "/api/users",
      { name, email, password },
      config
    );

    dispatch({
      type: USER_REGISTER_SUCCESS,
      payload: data,
    });

    dispatch({
      type: USER_LOGIN_SUCCESS,
      payload: data,
    });

    localStorage.setItem("userInfo", JSON.stringify(data));
  } catch (error) {
    dispatch({
      type: USER_REGISTER_FAIL,
      payload:
        error.response && error.response.data.message
          ? error.response.data.message
          : error.message,
    });
  }
};

export const getUserDetails = (id) => async (dispatch, getState) => {
  try {
    dispatch({
      type: USER_DETAILS_REQUEST,
    });

    const {
      userLogin: { userInfo },
    } = getState();

    const config = {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${userInfo.token}`,
      },
    };

    const { data } = await axios.get(`/api/users/${id}`, config);

    dispatch({
      type: USER_DETAILS_SUCCESS,
      payload: data,
    });
  } catch (error) {
    dispatch({
      type: USER_DETAILS_FAIL,
      payload:
        error.response && error.response.data.message
          ? error.response.data.message
          : error.message,
    });
  }
};

export const updateUserProfile = (user) => async (dispatch, getState) => {
  try {
    dispatch({
      type: USER_UPDATE_PROFILE_REQUEST,
    });

    const {
      userLogin: { userInfo },
    } = getState();

    const config = {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${userInfo.token}`,
      },
    };
    console.log("UPDATE Action called");

    const { data } = await axios.put(`/api/users/profile`, user, config);

    dispatch({
      type: USER_UPDATE_PROFILE_SUCCESS,
      payload: data,
    });
  } catch (error) {
    dispatch({
      type: USER_UPDATE_PROFILE_FAIL,
      payload:
        error.response && error.response.data.message
          ? error.response.data.message
          : error.message,
    });
  }
};

userReducer.js

import {
  USER_UPDATE_PROFILE_FAIL,
  USER_UPDATE_PROFILE_REQUEST,
  USER_UPDATE_PROFILE_SUCCESS,
} from "../constants/userConstants";

export const userUpdateProfileReducer = (state = {}, action) => {
  switch (action.type) {
    case USER_UPDATE_PROFILE_REQUEST:
      return { loading: true };
    case USER_UPDATE_PROFILE_SUCCESS:
      return { loading: false, success: true, userInfo: action.payload };
    case USER_UPDATE_PROFILE_FAIL:
      return { loading: false, error: action.payload };
    default:
      return state;
  }
};

store.js

import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";

// reducers
 import {
  userLoginReducer,
  userRegisterReducer,
  userDetailsReducer,
  userUpdateProfileReducer,
} from "./reducers/userReducers";

const reducer = combineReducers({
  userLogin: userLoginReducer,
  userRegister: userRegisterReducer,
  userDetails: userDetailsReducer,
  userUpdateProfile: userUpdateProfileReducer,
});

const userInfoFromStorage = localStorage.getItem("userInfo")
  ? JSON.parse(localStorage.getItem("userInfo"))
  : null;

const initialState = {
  userLogin: { userInfo: userInfoFromStorage },
};
const middleware = [thunk];

const store = createStore(
  reducer,
  initialState,
  composeWithDevTools(applyMiddleware(...middleware))
);

export default store;

后端

userRoutes.js

const express = require("express");
const {
  authUser,
  getUserProfile,
  registerUser,
  updateUserProfile,
} = require("../controllers/userController");
const protect = require("../middleware/authMiddleware");
const router = express.Router();

router.route("/").post(registerUser);
router.post("/login", authUser);
router
  .route("/profile")
  .get(protect, getUserProfile)
  .put(protect, updateUserProfile);

module.exports = router;

userController.js

//  @description: Update user profile
//  @route:       PUT /api/users/profile
//  @access:      Private
exports.updateUserProfile = async (req, res, next) => {
  try {
    const user = await User.findById(req.user._id);

    if (user) {
      user.name = req.body.name || user.name;
      user.email = req.body.email || user.email;

      if (req.body.password) {
        user.password = req.body.password;
      }

      const updatedUser = await user.save();

      res.json({
        _id: updatedUser._id,
        name: updatedUser.name,
        email: updatedUser.email,
        isAdmin: updatedUser.isAdmin,
        token: generateToken(updatedUser._id),
      });
    }
  } catch (error) {
    error = new Error("User not found");
    error.status = 404;
    next(error);
  }
};

authMiddleware.js

const jwt = require("jsonwebtoken");
const User = require("../models/userModel");

const protect = async (req, res, next) => {
  let token;
  if (
    req.headers.authorization &&
    req.headers.authorization.startsWith("Bearer")
  ) {
    try {
      token = req.headers.authorization.split(" ")[1];

      const decoded = jwt.verify(token, process.env.JWT_SECRET);

      req.user = await User.findById(decoded.id).select("-password");
      next();
    } catch (error) {
      error = new Error("Not Authorized!!");
      error.status = 401;
      next(error);
    }
  }

  if (!token) {
    const error = new Error("Not Authorized!!, No Token!!");
    error.status = 401;
    next(error);
  }
};

module.exports = protect;

【问题讨论】:

    标签: javascript reactjs express redux


    【解决方案1】:

    错误(很可能)在

        const user = await User.findById(req.user._id);
    

    检查您是否正确获取“req.user._id”。如果获取 req.user._id 有任何错误,还要检查您的 authMiddlwere。

    【讨论】:

    • 感谢您的回复。作为回报,我得到了正确的用户 ID。事实上,我已经通过thunder client 检查了我的后端,一切正常(我可以更新姓名、电子邮件和密码)。我认为,主要问题在于 react-redux 代码。
    • 不,我的意思是该行正在生成错误。这条线与正确获取 req.user._id 有关。如果你的后端工作正常,那么在== Authorization:Bearer ${userInfo.token},==中检查token是否正确发送,即尝试控制台记录userInfo.token
    • 好的,所以我在控制台日志userInfo.token 之后得到了eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwYzZkZjIyZjI1ZDM4MWU3OGFiNWYzMiIsImlhdCI6MTYyMzk5MzI0NSwiZXhwIjoxNjI2NTg1MjQ1fQ.5nnCj1GCqR_anD7UQKaJTbxZUDDBaVEfE4JcHaw_z_g,并且存储在我的本地存储中的令牌是eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwYzZkZjIyZjI1ZDM4MWU3OGFiNWYzMiIsImlhdCI6MTYyMzk5MzI0NSwiZXhwIjoxNjI2NTg1MjQ1fQ.5nnCj1GCqR_anD7UQKaJTbxZUDDBaVEfE4JcHaw_z_g 我比较了这里的文本Text Compare! 和它的相同。我尝试清除存储并再次登录,但结果相同。
    • 你能分享你的 authMiddleware 吗?
    猜你喜欢
    • 2020-08-09
    • 2020-04-14
    • 1970-01-01
    • 2021-05-06
    • 1970-01-01
    • 2015-06-12
    • 2016-07-14
    • 2020-10-07
    • 2019-03-17
    相关资源
    最近更新 更多