【发布时间】:2021-06-17 10:41:26
【问题描述】:
我正在开发一个 React 应用程序。无论是 Angular/React/Vue 项目,我每次都面临的问题是 AJAX 请求期间的错误处理(可以使用 Axios 或任何其他 AJAX 库进行)
我在这里找到的大多数答案都只是在try catch 或Promise.reject() 方法中执行console.log(error)。现实世界的应用程序不是这样工作的。
我想解决所有可能的错误,并根据错误的类型向用户显示适当的消息。例如,错误可能是:
- 来自 API 的验证错误。
- 网络失败。
- 一般错误,例如电子邮件/密码不正确。
- 在 GET 请求期间需要来自 API 的一些数据但由于错误而失败的表单。现在我会留下一个无法继续前进的死形态。
- 任何其他可能引发错误的原因。
我怎样才能知道所有可能的错误情况?重要的是,生成它们以查看我的应用程序如何响应它们。
由于我也在开发 API,请告诉我应该如何将错误发送回前端,以便它可以适当地处理它们。
目前,我的实现是(这不是很好的 IMO):
import React from "react";
import Link from "../../components/AppLink/AppLink";
import AppTextField from "../../components/FormComponents/AppTextField";
import styles from "../../styles/Auth.module.css";
import { Box, Button, Grid, Hidden, InputAdornment, Typography, Collapse } from "@material-ui/core";
import { Alert, AlertTitle } from '@material-ui/lab';
import EmailSvgIcon from "../../components/Icons/EmailSvgIcon";
import LockSvgIcon from "../../components/Icons/LockSvgIcon";
import LockOpenIcon from '@material-ui/icons/LockOpen';
import { ROUTE_FORGOT_PASSWORD, ROUTE_REGISTER } from "../../utils/RoutesList";
import AuthBanner from "../../components/AuthBanner/AuthBanner";
import useIsMobile from "../../hooks/useIsMobile";
import * as yup from 'yup';
import axios from '../../axiosConfig';
import CircularProgress from '@material-ui/core/CircularProgress';
import { useState } from "react";
import { AlertProps } from "@material-ui/lab";
import { mapApiErrorsToHookForm } from "../../utils/Form";
import { AxiosError } from "axios";
import { useForm, FormProvider } from "react-hook-form";
import { yupResolver } from '@hookform/resolvers/yup';
interface FormValues
{
email: string;
password: string;
}
interface AlertState
{
severity : AlertProps["severity"];
title : string;
message : string;
show : boolean;
}
const validationSchema = yup.object().shape({
email: yup.string()
.required("Please enter your email")
.email("Please enter a valid email address"),
password: yup.string().required("Please enter your password"),
});
const LoginPage = () =>
{
const formMethods = useForm <FormValues>({
resolver: yupResolver(validationSchema)
});
const { isSubmitting } = formMethods.formState;
const isMobile = useIsMobile();
const [alert, setAlert] = useState <AlertState>({
severity : undefined,
title : "",
message : "",
show : false,
});
const onSubmit = async (formValues: any) =>
{
try
{
const response = await axios.post("auth/user/login", formValues);
}
catch (error)
{
const exception: AxiosError = error;
let title = "Error", message: string;
if (exception.response && exception.response.status === 400)
{
message = exception.response.data.message;
switch (exception.response.data.errorType)
{
case "VALIDATION_ERROR":
title = "Incorrect input";
let errors = mapApiErrorsToHookForm(exception.response.data.errors)
for (let field in errors)
{
formMethods.setError(field as keyof FormValues, {
type: "validation",
message: errors[field]
});
}
break;
case "ACCOUNT_NOT_FOUND":
title = "Account not found";
break;
}
}
else
{
message = exception.message;
}
setAlert({
title,
message,
severity: "error",
show: true
});
}
}
return (
<>
<Grid
item
container
xs={12}
sm={4}
direction="column"
justify="space-between"
className={styles.bannerGrid}>
<AuthBanner
image="/images/auth/login_banner.jpg"
title="Tip"
description="The search for housing on this site is 10 times faster"
/>
</Grid>
<Grid item container sm={8} className={styles.contentWrapper}>
<Box display="flex" flexDirection="column" flex={1} paddingX={2} paddingY={2}>
<Hidden only="xs">
<Box textAlign="right">
<Link
underline="none"
href={ROUTE_REGISTER}
color="textPrimary"
className={styles.topLink}>
Register
</Link>
</Box>
</Hidden>
<Box
display="flex"
flexDirection="column"
justifyContent="center"
flex={1}
maxWidth={500}>
<Box marginBottom={3}>
<Box marginBottom={1.5}>
<Typography variant="h4" className={styles.formTitle}>Login</Typography>
</Box>
<Typography variant="body1" color="textSecondary">Welcome back!</Typography>
</Box>
<FormProvider {...formMethods}>
<form onSubmit={formMethods.handleSubmit(onSubmit)}>
<Collapse in={alert.show && !isSubmitting}>
<Box marginBottom={3}>
<Alert
severity={alert.severity}
onClose={() => setAlert({...alert, show: false})}>
<AlertTitle>{alert.title}</AlertTitle>
{alert.message}
</Alert>
</Box>
</Collapse>
<Box marginBottom={2}>
<AppTextField
name="email"
type="email"
label="Email"
variant="outlined"
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<EmailSvgIcon fontSize="small" />
</InputAdornment>
),
}}
/>
</Box>
<Box marginBottom={2}>
<AppTextField
name="password"
type="password"
label="Password"
variant="outlined"
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<LockSvgIcon fontSize="small" />
</InputAdornment>
),
}}
/>
</Box>
<Grid container spacing={2}>
<Grid
item xs={12}
sm={6}>
<Button
type="submit"
variant="contained"
color="primary"
size="large"
endIcon={
isSubmitting
? <CircularProgress color="secondary" size={18} />
: <LockOpenIcon />
}
fullWidth={isMobile}
disabled={isSubmitting}>
Login
</Button>
</Grid>
<Grid
item
container
xs={12}
sm={6}
alignItems="center"
justify={isMobile ? "center" : "flex-end"}>
<Link
href={ROUTE_FORGOT_PASSWORD}
color="primary"
className={styles.formLink}>
Forgot Password?
</Link>
</Grid>
</Grid>
</form>
</FormProvider>
</Box>
</Box>
</Grid>
</>
);
}
export default LoginPage;
对于这个特定的表单,我从 API 发送 errorType 并基于它设置相应的警报标题和消息,但我需要为每个表单都这样做吗?
我知道拦截器,但它无法在每个错误字段下显示验证消息,因为 formMethods 无法在其上下文中显示。
请通过示例提供详细的答案,以便我对此有很好的理解。另外,不要忘记解释如何处理所有可能的错误情况并处理它们。
提前致谢:)
【问题讨论】: