firebase cloud函数在request上使用不正确返回错误的cors

发布于 2025-02-10 13:04:12 字数 7727 浏览 0 评论 0原文

编辑:对于任何访问此问题的人,请阅读OnRequest和OnCall功能之间的区别。了解如何在客户端代码中调用它们中的每一个。它应该有助于了解这里的问题。

我正在使用Firebase进行后端建立一个个人项目,并为前端做出反应。我正在尝试从云功能正确返回错误。客户端显示的错误不是从服务器发送的错误。我将证明以下问题。

我希望客户端显示一个错误,上面写着“另一个帐户已经在使用电子邮件地址”。从下面的屏幕截图中可以看到,这就是作为响应对象发送的内容。但是,每当我在客户端执行console.log(err.message)时,就会说“无效的argument”。 (请参阅下面的屏幕截图中的控制台日志)。

请求信息

这是代码。

云功能 - index.ts

export const registerUser = https.onRequest((req, resp) => {
cors(req, resp, async () => {
    const data = req.body.data;
    const userData: IUserData = {
      firstName: data.firstName,
      lastName: data.lastName,
      statusId: 1,
      customerId: ''
    }
    
    try {
      // Create auth user in firebase
      const newAuthUser = await admin.auth().createUser({
        email: data.email,
        emailVerified: false,
        password: data.password,
        displayName: data.firstName + ' ' + data.lastName,
        disabled: false
      })
      // Create a stripe customer
      const uid = newAuthUser.uid;
      const stripeCustomer = await createStripeCustomer(data, uid);
      userData.customerId = stripeCustomer.id;
      // Create user doc in firestore
      const storeUserRef = app.firestore().doc('/users/' + uid);
      const storeUser = await storeUserRef.set(userData);
      resp.status(200).json({
        user: storeUser,
        message: "User successfully registered"
      }).send();
      return;
    } catch (err: any) {
      console.log(err);
      resp.status(400).send(err);
      return;
    }
  })
})

react -signuppage.tsx

function SignUpPage() {
  const firebaseAuth = getAuth(FIREBASE_APP);
  const functions = getFunctions(FIREBASE_APP);
  const [auth, loading] = useAuthState(firebaseAuth);
  const [regError, setRegError] = useState("")
  const nav = useNavigate();
  const { register, handleSubmit, formState: { errors }, } = useForm();
  const [registering, setRegistering] = useState(false);
  const registerUser = httpsCallable(functions, 'registerUser');
  connectFunctionsEmulator(functions, "localhost", 5001);
  
  // Check if a user login exists
  useEffect(() => {
    // If the auth has finished loading and the user exists, redirect to the
    // user account page
    if (auth && !loading) { nav('/account') }
  }, [auth, loading, nav])

  /**
   * Form on submit
   * @param data 
   */
  const onSubmit = (data: any) => {
    setRegistering(true);
    setRegError("");
    const userData: IRegisterPayload = {
      firstName: data.firstName,
      lastName: data.lastName,
      email: data.email,
      password: data.password
    }
    registerUser(userData).then(async(res) => {
      // return await signInWithEmailAndPassword(
      //   firebaseAuth, userData.email, userData.password
      // );
      console.log('REGISTER USER SUCCESS: ', res)
    })
    .then(res => {
      console.log(res);
      nav("/account");
    })
    .catch((err) => {
      console.log('Console log err.message:', err.message);
      setRegError(err.message);
    }).finally(() => {
      setRegistering(false);
    })
  };

  return(
    <SiteLayout>
      <div className="px-3 py-5 mx-auto signup-wrapper w-100 container d-flex flex-column flex-grow-1">
        <div className="row">
          <div className="col-12 col-lg-6 col-xl-5 pe-lg-5">
            <h2 className="h2 mb-4 text-center">Sign up</h2>
            <p className="text-center">Already a member? <NavLink to={`/login`}>Login</NavLink></p>
            <form onSubmit={handleSubmit(onSubmit)}
              className="mb-2"
            >
              <div className="mb-3">
                <label>First Name</label>
                <input type={`text`}
                  className={`form-control${errors.firstName ? ' is-invalid': ''}`}
                  placeholder="Enter your first name"
                  disabled={registering}
                  {...register('firstName', { required: true})}
                />
                {errors.firstName && <small className="text-danger d-block mt-1">First Name is required</small>}
              </div>
              <div className="mb-3">
                <label>Last Name</label>
                <input type={`text`} 
                  className={`form-control${errors.lastName ? ' is-invalid': ''}`}
                  placeholder="Enter your last name"
                  disabled={registering}
                  {...register('lastName', { required: true})}
                />
                {errors.lastName && <small className="text-danger d-block mt-1">Last Name is required</small>}
              </div>
              <div className="mb-3">
                <label>Email</label>
                <input type={`email`} 
                  className={`form-control${errors.email ? ' is-invalid': ''}`}
                  placeholder="Enter your email address"
                  disabled={registering}
                  {...register('email', { required: true})}
                />
                {errors.email && <small className="text-danger d-block mt-1">Email is required</small>}
              </div>
              <div className="mb-4">
                <label>Password</label>
                <input type={`password`} 
                  className={`form-control${errors.password ? ' is-invalid': ''}`}
                  placeholder="Enter your password"
                  disabled={registering}
                  {...register('password',{ 
                    required: "You must specify a password",
                    validate: (val: string) => {
                      // Check the entered value meets the regex requirement
                      const isStrong = PASSWORD_REGEX.test(val);
                      return (val && isStrong) || "Password does not meet criteria";
                    }
                  })}
                />
                {errors.password && <p className="small text-danger d-block my-1">{errors.password.message}</p>}
                <small className="text-muted">Passwords must be a minimum of 8 characters and include:</small>
                <ul className="small text-muted">
                  <li>At least 1 uppercase letter</li>
                  <li>At least 1 lowercase letter</li>
                  <li>At least 1 special character</li>
                  <li>At least 1 number</li>
                </ul>
              </div>
              <Button isLoading={registering}
                isSubmit={true}
                loadingLabel="Registering..."
                className="w-100"
              >
                Sign up
              </Button>
              {regError &&
                <Message severity="error" text={regError} className="my-3 w-100" />
              }
            </form>
            <p className="small text-muted">
            By signing up, you confirm that you accept the Terms of Service and Privacy Policy. This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
            </p>            
          </div>
        </div>
      </div>
    </SiteLayout>
  );
}
export default SignUpPage;

有人可以告诉我如何显示我想要的错误消息吗?

EDIT: For anyone visiting this issue - read up on the difference between onRequest, and onCall functions. Understand how to call each of them in your client side code. It should help to understand what the problem was here.

I am building a personal project with firebase for the backend, and React for the frontend. I am trying to return errors correctly from cloud functions. The error shown by the client side isn't what is sent from the server. I will demonstrate the issue below.

I want the client side to display an error that says "The email address is already in use by another account". As you can see from the screenshot below, that is what is sent as the response object. However, whenever I do a console.log(err.message) in the client side, it says "invalid-argument". (See the console log in the screenshot below).

Request information

This is the code.

Cloud functions - index.ts

export const registerUser = https.onRequest((req, resp) => {
cors(req, resp, async () => {
    const data = req.body.data;
    const userData: IUserData = {
      firstName: data.firstName,
      lastName: data.lastName,
      statusId: 1,
      customerId: ''
    }
    
    try {
      // Create auth user in firebase
      const newAuthUser = await admin.auth().createUser({
        email: data.email,
        emailVerified: false,
        password: data.password,
        displayName: data.firstName + ' ' + data.lastName,
        disabled: false
      })
      // Create a stripe customer
      const uid = newAuthUser.uid;
      const stripeCustomer = await createStripeCustomer(data, uid);
      userData.customerId = stripeCustomer.id;
      // Create user doc in firestore
      const storeUserRef = app.firestore().doc('/users/' + uid);
      const storeUser = await storeUserRef.set(userData);
      resp.status(200).json({
        user: storeUser,
        message: "User successfully registered"
      }).send();
      return;
    } catch (err: any) {
      console.log(err);
      resp.status(400).send(err);
      return;
    }
  })
})

React - SignupPage.tsx

function SignUpPage() {
  const firebaseAuth = getAuth(FIREBASE_APP);
  const functions = getFunctions(FIREBASE_APP);
  const [auth, loading] = useAuthState(firebaseAuth);
  const [regError, setRegError] = useState("")
  const nav = useNavigate();
  const { register, handleSubmit, formState: { errors }, } = useForm();
  const [registering, setRegistering] = useState(false);
  const registerUser = httpsCallable(functions, 'registerUser');
  connectFunctionsEmulator(functions, "localhost", 5001);
  
  // Check if a user login exists
  useEffect(() => {
    // If the auth has finished loading and the user exists, redirect to the
    // user account page
    if (auth && !loading) { nav('/account') }
  }, [auth, loading, nav])

  /**
   * Form on submit
   * @param data 
   */
  const onSubmit = (data: any) => {
    setRegistering(true);
    setRegError("");
    const userData: IRegisterPayload = {
      firstName: data.firstName,
      lastName: data.lastName,
      email: data.email,
      password: data.password
    }
    registerUser(userData).then(async(res) => {
      // return await signInWithEmailAndPassword(
      //   firebaseAuth, userData.email, userData.password
      // );
      console.log('REGISTER USER SUCCESS: ', res)
    })
    .then(res => {
      console.log(res);
      nav("/account");
    })
    .catch((err) => {
      console.log('Console log err.message:', err.message);
      setRegError(err.message);
    }).finally(() => {
      setRegistering(false);
    })
  };

  return(
    <SiteLayout>
      <div className="px-3 py-5 mx-auto signup-wrapper w-100 container d-flex flex-column flex-grow-1">
        <div className="row">
          <div className="col-12 col-lg-6 col-xl-5 pe-lg-5">
            <h2 className="h2 mb-4 text-center">Sign up</h2>
            <p className="text-center">Already a member? <NavLink to={`/login`}>Login</NavLink></p>
            <form onSubmit={handleSubmit(onSubmit)}
              className="mb-2"
            >
              <div className="mb-3">
                <label>First Name</label>
                <input type={`text`}
                  className={`form-control${errors.firstName ? ' is-invalid': ''}`}
                  placeholder="Enter your first name"
                  disabled={registering}
                  {...register('firstName', { required: true})}
                />
                {errors.firstName && <small className="text-danger d-block mt-1">First Name is required</small>}
              </div>
              <div className="mb-3">
                <label>Last Name</label>
                <input type={`text`} 
                  className={`form-control${errors.lastName ? ' is-invalid': ''}`}
                  placeholder="Enter your last name"
                  disabled={registering}
                  {...register('lastName', { required: true})}
                />
                {errors.lastName && <small className="text-danger d-block mt-1">Last Name is required</small>}
              </div>
              <div className="mb-3">
                <label>Email</label>
                <input type={`email`} 
                  className={`form-control${errors.email ? ' is-invalid': ''}`}
                  placeholder="Enter your email address"
                  disabled={registering}
                  {...register('email', { required: true})}
                />
                {errors.email && <small className="text-danger d-block mt-1">Email is required</small>}
              </div>
              <div className="mb-4">
                <label>Password</label>
                <input type={`password`} 
                  className={`form-control${errors.password ? ' is-invalid': ''}`}
                  placeholder="Enter your password"
                  disabled={registering}
                  {...register('password',{ 
                    required: "You must specify a password",
                    validate: (val: string) => {
                      // Check the entered value meets the regex requirement
                      const isStrong = PASSWORD_REGEX.test(val);
                      return (val && isStrong) || "Password does not meet criteria";
                    }
                  })}
                />
                {errors.password && <p className="small text-danger d-block my-1">{errors.password.message}</p>}
                <small className="text-muted">Passwords must be a minimum of 8 characters and include:</small>
                <ul className="small text-muted">
                  <li>At least 1 uppercase letter</li>
                  <li>At least 1 lowercase letter</li>
                  <li>At least 1 special character</li>
                  <li>At least 1 number</li>
                </ul>
              </div>
              <Button isLoading={registering}
                isSubmit={true}
                loadingLabel="Registering..."
                className="w-100"
              >
                Sign up
              </Button>
              {regError &&
                <Message severity="error" text={regError} className="my-3 w-100" />
              }
            </form>
            <p className="small text-muted">
            By signing up, you confirm that you accept the Terms of Service and Privacy Policy. This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
            </p>            
          </div>
        </div>
      </div>
    </SiteLayout>
  );
}
export default SignUpPage;

Can someone please tell me how to display the error message that I want?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

套路撩心 2025-02-17 13:04:12

在您的客户端应用程序中,您说的是,regusteruser是“可呼叫”类型函数,但它并未在您的后端部署。

// here you are saying that the backend is a "callable" type function
const registerUser = httpsCallable(functions, 'registerUser');

但是,您的后端是使用普通的http onRequest 类型功能。

// here are you deploying a normal HTTP function (not "callable")
export const registerUser = https.onRequest((req, resp) => {

这将行不通 - 您无法将HTTP类型函数称为可可。

如果您想要后端中的可呼叫类型功能,请按照文档中的说明使用 /a>类型功能。从该链接的文档中,请注意:

请务必记住,HTTPS可呼叫功能相似,但与HTTP功能不完全相同。要使用HTTPS可呼叫功能,您必须将客户端SDK与functions.https后端API一起使用(或实现协议)。


In your client app, you are saying that regusterUser is a "callable" type function, but it is not deployed that way in your backend.

// here you are saying that the backend is a "callable" type function
const registerUser = httpsCallable(functions, 'registerUser');

However, your backend is using an normal HTTP onRequest type function instead.

// here are you deploying a normal HTTP function (not "callable")
export const registerUser = https.onRequest((req, resp) => {

This won't work - you can't call HTTP type functions as a callable.

If you want a callable type function in your backend, follow the instructions in the documentation to use an onCall type function instead. From that linked documentation, note:

It's important to keep in mind that HTTPS callable functions are similar but not identical to HTTP functions. To use HTTPS callable functions you must use the client SDK for your platform together with the functions.https backend API (or implement the protocol).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文