我正在开发一个项目,基本上当我注册时,它应该在提供的电子邮件上发送一个 otp,但我却收到了上述错误。我之前在邮递员上做过测试,当时它工作正常,但现在当我连接后端和前端并尝试发送 otp 时,它在控制台中抛出错误
SENDOTP API 错误............
AxiosError {message: '请求失败,状态代码 401', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {...}, request: XMLHttpRequest, ...}
代码:“ERR_BAD_REQUEST”
config:{过渡:{…},适配器:数组(2),transformRequest:数组(1),transformResponse:数组(1),超时:0,…}
消息:“请求失败,状态代码为 401”
名称:“AxiosError”
请求:XMLHttpRequest {onreadystatechange:null,readyState:4,超时:0,withCredentials:false,上传:XMLHttpRequestUpload,...}
响应:{数据:{…},状态:401,statusText:“未经授权”,标头:AxiosHeaders,配置:{…},…}
stack:“AxiosError:请求失败,状态代码401 解决时(http://localhost:3000/static/js/bundle.js:144559:12) 在 XMLHttpRequest.onloadend (http://localhost:3000/static/js/bundle.js:143244:66)"
[[原型]]:错误
现在,当我尝试注册时,它会出错,但我希望它在我的邮件中发送 otp
我的 API 连接器文件
import axios from "axios"
export const axiosInstance = axios.create({});
export const apiConnector = (method, url, bodyData, headers, params) => {
return axiosInstance({
method:`${method}`,
url:`${url}`,
data: bodyData ? bodyData : null,
headers: headers ? headers: null,
params: params ? params : null,
});
}
my apis file
const BASE_URL = "https://localhost:4000/api/v1"
export const categories = {
CATEGORIES_API: BASE_URL + "/course/showAllCategories"
}
// AUTH ENDPOINTS
export const endpoints = {
SENDOTP_API: BASE_URL + "/auth/sendotp",
SIGNUP_API: BASE_URL + "/auth/signup",
LOGIN_API: BASE_URL + "/auth/login",
RESETPASSTOKEN_API: BASE_URL + "/auth/reset-password-token",
RESETPASSWORD_API: BASE_URL + "/auth/reset-password",
}
my authAPI file
import { toast } from "react-hot-toast"
import { setLoading, setToken } from "../../slices/authSlice"
//import { resetCart } from "../../slices/cartSlice"
//import { setUser } from "../../slices/profileSlice"
import { apiConnector } from "../apiconnector"
import { endpoints } from "../apis"
const {
SENDOTP_API,
SIGNUP_API,
LOGIN_API,
RESETPASSTOKEN_API,
RESETPASSWORD_API,
} = endpoints
export function sendOtp(email, navigate) {
return async (dispatch) => {
const toastId = toast.loading("Loading...")
dispatch(setLoading(true))
try {
const response = await apiConnector("POST", SENDOTP_API, {
email,
checkUserPresent: true,
})
console.log("SENDOTP API RESPONSE............", response)
console.log(response.data.success)
if (!response.data.success) {
throw new Error(response.data.message)
}
toast.success("OTP Sent Successfully")
navigate("/verify-email")
} catch (error) {
console.log("SENDOTP API ERROR............", error)
toast.error("Could Not Send OTP")
}
dispatch(setLoading(false))
toast.dismiss(toastId)
}
}
export function signUp(
accountType,
firstName,
lastName,
email,
password,
confirmPassword,
otp,
navigate
) {
return async (dispatch) => {
const toastId = toast.loading("Loading...")
dispatch(setLoading(true))
try {
const response = await apiConnector("POST", SIGNUP_API, {
accountType,
firstName,
lastName,
email,
password,
confirmPassword,
otp,
})
console.log("SIGNUP API RESPONSE............", response)
if (!response.data.success) {
throw new Error(response.data.message)
}
toast.success("Signup Successful")
navigate("/login")
} catch (error) {
console.log("SIGNUP API ERROR............", error)
toast.error("Signup Failed")
navigate("/signup")
}
dispatch(setLoading(false))
toast.dismiss(toastId)
}
}
export function login(email, password, navigate) {
return async (dispatch) => {
const toastId = toast.loading("Loading...")
dispatch(setLoading(true))
try {
const response = await apiConnector("POST", LOGIN_API, {
email,
password,
})
console.log("LOGIN API RESPONSE............", response)
if (!response.data.success) {
throw new Error(response.data.message)
}
toast.success("Login Successful")
dispatch(setToken(response.data.token))
const userImage = response.data?.user?.image
? response.data.user.image
: `https://api.dicebear.com/5.x/initials/svg?seed=${response.data.user.firstName} ${response.data.user.lastName}`
//dispatch(setUser({ ...response.data.user, image: userImage }))
localStorage.setItem("token", JSON.stringify(response.data.token))
localStorage.setItem("user", JSON.stringify(response.data.user))
navigate("/dashboard/my-profile")
} catch (error) {
console.log("LOGIN API ERROR............", error)
toast.error("Login Failed")
}
dispatch(setLoading(false))
toast.dismiss(toastId)
}
}
export function logout(navigate) {
return (dispatch) => {
dispatch(setToken(null))
//dispatch(setUser(null))
//dispatch(resetCart())
localStorage.removeItem("token")
localStorage.removeItem("user")
toast.success("Logged Out")
navigate("/")
}
}
export function getPasswordResetToken(email , setEmailSent) {
return async(dispatch) => {
dispatch(setLoading(true));
try{
const response = await apiConnector("POST", RESETPASSTOKEN_API, {email,})
console.log("RESET PASSWORD TOKEN RESPONSE....", response);
if(!response.data.success) {
throw new Error(response.data.message);
}
toast.success("Reset Email Sent");
setEmailSent(true);
}
catch(error) {
console.log("RESET PASSWORD TOKEN Error", error);
toast.error("Failed to send email for resetting password");
}
dispatch(setLoading(false));
}
}
export function resetPassword(password, confirmPassword, token) {
return async(dispatch) => {
dispatch(setLoading(true));
try{
const response = await apiConnector("POST", RESETPASSWORD_API, {password, confirmPassword, token});
console.log("RESET Password RESPONSE ... ", response);
if(!response.data.success) {
throw new Error(response.data.message);
}
toast.success("Password has been reset successfully");
}
catch(error) {
console.log("RESET PASSWORD TOKEN Error", error);
toast.error("Unable to reset password");
}
dispatch(setLoading(false));
}
}
my signup form
import { useState } from "react"
import { toast } from "react-hot-toast"
import { AiOutlineEye, AiOutlineEyeInvisible } from "react-icons/ai"
import { useDispatch } from "react-redux"
import { useNavigate } from "react-router-dom"
import { sendOtp } from "../../../services/operations/authAPI"
import { setSignupData } from "../../../slices/authSlice"
//import { ACCOUNT_TYPE } from "../../../utils/constants"
//import Tab from "../../common/Tab"
function SignupForm() {
const navigate = useNavigate()
const dispatch = useDispatch()
// student or instructor
//const [accountType, setAccountType] = useState(ACCOUNT_TYPE.STUDENT)
const [formData, setFormData] = useState({
firstName: "",
lastName: "",
email: "",
password: "",
confirmPassword: "",
})
const [showPassword, setShowPassword] = useState(false)
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
const { firstName, lastName, email, password, confirmPassword } = formData
// Handle input fields, when some value changes
const handleOnChange = (e) => {
setFormData((prevData) => ({
...prevData,
[e.target.name]: e.target.value,
}))
}
// Handle Form Submission
const handleOnSubmit = (e) => {
e.preventDefault()
if (password !== confirmPassword) {
toast.error("Passwords Do Not Match")
return
}
const signupData = {
...formData,
// accountType,
}
// Setting signup data to state
// To be used after otp verification
dispatch(setSignupData(signupData))
// Send OTP to user for verification
dispatch(sendOtp(formData.email, navigate))
// Reset
setFormData({
firstName: "",
lastName: "",
email: "",
password: "",
confirmPassword: "",
})
//setAccountType(ACCOUNT_TYPE.STUDENT)
}
// data to pass to Tab component
const tabData = [
{
id: 1,
tabName: "Student",
//type: ACCOUNT_TYPE.STUDENT,
},
{
id: 2,
tabName: "Instructor",
//type: ACCOUNT_TYPE.INSTRUCTOR,
},
]
return (
<div>
{/* Tab */}
{/* <Tab tabData={tabData} field={accountType} setField={setAccountType} /> */}
{/* Form */}
<form onSubmit={handleOnSubmit} className="flex w-full flex-col gap-y-4">
<div className="flex gap-x-4">
<label>
<p className="mb-1 text-[0.875rem] leading-[1.375rem] text-richblack-5">
First Name <sup className="text-pink-200">*</sup>
</p>
<input
required
type="text"
name="firstName"
value={firstName}
onChange={handleOnChange}
placeholder="Enter first name"
style={{
boxShadow: "inset 0px -1px 0px rgba(255, 255, 255, 0.18)",
}}
className="w-full rounded-[0.5rem] bg-richblack-800 p-[12px] text-richblack-5"
/>
</label>
<label>
<p className="mb-1 text-[0.875rem] leading-[1.375rem] text-richblack-5">
Last Name <sup className="text-pink-200">*</sup>
</p>
<input
required
type="text"
name="lastName"
value={lastName}
onChange={handleOnChange}
placeholder="Enter last name"
style={{
boxShadow: "inset 0px -1px 0px rgba(255, 255, 255, 0.18)",
}}
className="w-full rounded-[0.5rem] bg-richblack-800 p-[12px] text-richblack-5"
/>
</label>
</div>
<label className="w-full">
<p className="mb-1 text-[0.875rem] leading-[1.375rem] text-richblack-5">
Email Address <sup className="text-pink-200">*</sup>
</p>
<input
required
type="text"
name="email"
value={email}
onChange={handleOnChange}
placeholder="Enter email address"
style={{
boxShadow: "inset 0px -1px 0px rgba(255, 255, 255, 0.18)",
}}
className="w-full rounded-[0.5rem] bg-richblack-800 p-[12px] text-richblack-5"
/>
</label>
<div className="flex gap-x-4">
<label className="relative">
<p className="mb-1 text-[0.875rem] leading-[1.375rem] text-richblack-5">
Create Password <sup className="text-pink-200">*</sup>
</p>
<input
required
type={showPassword ? "text" : "password"}
name="password"
value={password}
onChange={handleOnChange}
placeholder="Enter Password"
style={{
boxShadow: "inset 0px -1px 0px rgba(255, 255, 255, 0.18)",
}}
className="w-full rounded-[0.5rem] bg-richblack-800 p-[12px] pr-10 text-richblack-5"
/>
<span
onClick={() => setShowPassword((prev) => !prev)}
className="absolute right-3 top-[38px] z-[10] cursor-pointer"
>
{showPassword ? (
<AiOutlineEyeInvisible fontSize={24} fill="#AFB2BF" />
) : (
<AiOutlineEye fontSize={24} fill="#AFB2BF" />
)}
</span>
</label>
<label className="relative">
<p className="mb-1 text-[0.875rem] leading-[1.375rem] text-richblack-5">
Confirm Password <sup className="text-pink-200">*</sup>
</p>
<input
required
type={showConfirmPassword ? "text" : "password"}
name="confirmPassword"
value={confirmPassword}
onChange={handleOnChange}
placeholder="Confirm Password"
style={{
boxShadow: "inset 0px -1px 0px rgba(255, 255, 255, 0.18)",
}}
className="w-full rounded-[0.5rem] bg-richblack-800 p-[12px] pr-10 text-richblack-5"
/>
<span
onClick={() => setShowConfirmPassword((prev) => !prev)}
className="absolute right-3 top-[38px] z-[10] cursor-pointer"
>
{showConfirmPassword ? (
<AiOutlineEyeInvisible fontSize={24} fill="#AFB2BF" />
) : (
<AiOutlineEye fontSize={24} fill="#AFB2BF" />
)}
</span>
</label>
</div>
<button
type="submit"
className="mt-6 rounded-[8px] bg-yellow-50 py-[8px] px-[12px] font-medium text-richblack-900"
>
Create Account
</button>
</form>
</div>
)
}
export default SignupForm
sendotp contoller which works perfectly fine on postman
exports.sendOTP = async (req, res) => {
try {
const {email} = req.body;
const checkUserPresent = await User.findOne({email});
if (checkUserPresent) {
return res.status(401).json({
success: false,
message: "User already exist",
});
}
//generate otp
var otp = OTPgenerator.generate(6, {
upperCaseAlphabets: false,
lowerCaseAlphabets: false,
specialChars: false,
});
console.log("OTP generated", otp);
let result = await OTP.findOne({ otp: otp });
while (result) {
otp = OTPgenerator.generate(6, {
upperCaseAlphabets: false,
lowerCaseAlphabets: false,
specialChars: false,
});
const result = await OTP.findOne({ otp: otp });
}
const otpPayload = { email, otp };
//create an entry in db
const otpBody = await OTP.create(otpPayload);
console.log(otpBody);
res.status(200).json({
success: true,
message: "OTP sent successfully",
});
} catch (e) {
console.log(e);
res.status(500).json({
success: false,
message: e.message,
});
}
};
您的 env 文件必须丢失,该文件将包含基本 Url 确保创建一个 .env 文件并存储所需的后端 URL