当用户试图登录未注册用户时,我试图显示错误消息,但我无法从后端(nodejs, express, MongoDB)发送的前端(react)中获得错误消息。
我正在使用redux来处理react应用程序的状态。
具体来说,我需要改变登录函数
import React, { useState } from 'react';
import { Box, Button, TextField, useMediaQuery, Typography, useTheme } from '@mui/material';
import { EditOutlined } from '@mui/icons-material';
import { Formik } from 'formik';
import * as yup from "yup";
import { useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { setLogin } from 'state/authReducer';
import Dropzone from 'react-dropzone';
import FlexBetween from 'components/FlexBetween';
const registerSchema = yup.object().shape({
name: yup.string().required("Обязательное поле"),
email: yup.string().email("Почта введена неправильно").required("Обязательное поле"),
password: yup.string().required("Обязательное поле"),
occupation: yup.string().required("Укажите вашу должность"),
picture: yup.string().required("Выберите изображение для вашего профиля")
})
const loginSchema = yup.object().shape({
email: yup.string().email("Почта введена неправильно").required("Обязательное поле"),
password: yup.string().required("Обязательное поле"),
})
const initialValuesRegister = {
name: "",
email: "",
password: "",
occupation: ""
}
const initialValuesLogin = {
email: "",
password: "",
}
const Form = () => {
const [pageType, setPageType] = useState("login");
const { palette } = useTheme();
const dispatch = useDispatch();
const navigate = useNavigate();
const isNonMobile = useMediaQuery("(min-width: 600px)");
const isLogin = pageType === "login";
const isRegister = pageType === "register";
const register = async (values, onSubmitProps) => {
const formData = new FormData();
for (let value in values) {
formData.append(value, values[value])
}
formData.append("picturePath", values.picture.name);
const savedUserResponse = await fetch(
"http://localhost:5001/auth/register",
{
method: "POST",
body: formData,
}
);
const savedUser = await savedUserResponse.json();
onSubmitProps.resetForm();
if (savedUser) {
setPageType("login");
}
}
const login = async (values, onSubmitProps) => {
const loggedInResponse = await fetch(
"http://localhost:5001/auth/login",
{
method: "POST",
headers: { "Content-Type": "application/json"},
body: JSON.stringify(values),
}
);
const loggedIn = await loggedInResponse.json();
onSubmitProps.resetForm();
if (loggedIn) {
dispatch(
setLogin({
user: loggedIn,
token: loggedIn.token,
})
);
navigate("/dashboard");
}
};
const handleFormSubmit = async(values, onSubmitProps) => {
if (isLogin) {
await login(values, onSubmitProps);
}
if (isRegister) {
await register(values, onSubmitProps);
}
};
return (
<Formik
onSubmit={handleFormSubmit}
initialValues={isLogin ? initialValuesLogin : initialValuesRegister}
validationSchema={isLogin ? loginSchema : registerSchema}
>
{({
values,
errors,
touched,
handleBlur,
handleChange,
handleSubmit,
setFieldValue,
resetForm,
}) => (
<form onSubmit={handleSubmit}>
<Box
display="grid"
gap="10px"
gridTemplateColumns="repeat(4, minmax(0, 1fr))"
sx={{
"& > div": {
gridColumn: isNonMobile ? undefined : "span 4"
},
}}
>
{isRegister && (
<>
<TextField
label="ФИО"
onBlur={handleBlur}
onChange={handleChange}
value={values.name}
name="name"
error={Boolean(touched.name) && Boolean(errors.name)}
helperText={touched.name && errors.name}
sx={{ gridColumn: "span 4" }}
/>
<TextField
label="Должность"
onBlur={handleBlur}
onChange={handleChange}
value={values.occupation}
name="occupation"
error={Boolean(touched.occupation) && Boolean(errors.occupation)}
helperText={touched.occupation && errors.occupation}
sx={{ gridColumn: "span 4" }}
/>
<Box
gridColumn="span 4"
border={`1px solid ${palette.divider}`}
borderRadius="5px"
p="1rem"
>
<Dropzone
acceptedFiles=".jpg,.jpeg,.png"
multiple={false}
onDrop={(acceptedFiles) => {
setFieldValue("picture", acceptedFiles[0])
}}
>
{({getRootProps, getInputProps}) => (
<Box
{...getRootProps()}
border={`2px dashed ${palette.primary.main}`}
p="1rem"
sx={{ "&:hover": { cursor: "pointer" }}}
>
<input {...getInputProps()} />
{!values.picture ? (
<p>Add Picture Here</p>
) : (
<FlexBetween>
<Typography>{values.picture.name}</Typography>
<EditOutlined />
</FlexBetween>
)}
</Box>
)}
</Dropzone>
</Box>
</>
)}
<TextField
autoComplete='new-password'
placeholder="Логин"
onBlur={handleBlur}
onChange={handleChange}
value={values.email}
name="email"
error={Boolean(touched.email) && Boolean(errors.email)}
helperText={touched.email && errors.email}
sx={{ gridColumn: "span 4" }}
/>
<TextField
autoComplete='new-password'
placeholder="Пароль"
type="password"
onBlur={handleBlur}
onChange={handleChange}
value={values.password}
name="password"
error={Boolean(touched.password) && Boolean(errors.password)}
helperText={touched.password && errors.password}
sx={{ gridColumn: "span 4" }}
/>
</Box>
{/* КНОПКИ */}
<Box>
<Button
fullWidth
type="submit"
sx={{
m: "1rem 0",
p: "1rem",
backgroundColor: palette.text.primary,
color: palette.background.alt,
"&:hover": {backgroundColor: palette.text.primary, boxShadow: "0 4px 7px rgba(0, 0, 0, 0.15), 0 100px 80px rgba(255, 255, 255, 0.02), 0 42px 33px rgba(255, 255, 255, 0.024), 0 22px 18px rgba(255, 255, 255, 0.028), 0 12px 10px rgba(255, 255, 255, 0.034), 0 7px 5px rgba(255, 255, 255, 0.04), 0 3px 2px rgba(255, 255, 255, 0.07)"}
}}
>
{isLogin ? "ВОЙТИ" : "ЗАРЕГЕСТРИРОВАТЬСЯ"}
</Button>
<Typography
onClick={() => {
setPageType(isLogin ? "register" : "login");
resetForm();
}}
sx={{
textDecoration: "underline",
color: palette.text.disabled,
"&:hover": {
cursor: "pointer",
color: palette.primary.main
},
}}
>
{isLogin
? "Нет аккаунта? Создайте его здесь."
: "Уже есть аккаунт? Войдите здесь."}
</Typography>
</Box>
</form>
)}
</Formik>
)
}
export default Form;
我authReducer:
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
user: null,
token: null,
mode: "dark",
userId: "63701cc1f03239c72c000180",
}
export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
setLogin: (state, action) => {
state.user = action.payload.user;
state.token = action.payload.token;
},
setLogout: (state) => {
state.user = null;
state.token = null;
},
setMode: (state) => {
state.mode = state.mode === 'light' ? "dark" : 'light';
},
}
})
export const { setLogin, setLogout, setClients, setMode } = authSlice.actions;
export default authSlice.reducer;
后台登录码:
export const login = async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email: email });
if (!user) {
return res.status(400).json({ msg: "User does not exist! "});
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ msg: "Invalid credentials! "});
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
delete user.password;
res.status(200).json({ token, user });
} catch (e) {
res.status(500).json({ error: e.message });
}
}
不幸的是,fetch在4xx响应时不会抛出错误,因此您需要构建自己的处理程序。
fetch返回的promise返回到一个带有" ok "属性的Response对象。fetch返回后,检查loggedInResponse。Ok为真,否则作为错误处理。