英文:
CORS error with 2nd gen Cloud Functions for Firebase
问题
I'm providing the translation for the code portions you mentioned:
我正在尝试使用第二代 Cloud Functions for Firebase。我已经使用 `onDocumentCreated()` 触发了 `makeUppercase()`。现在我正在尝试让 Angular 应用程序调用一个可调用的云函数。
*index.ts*
```js
import * as logger from "firebase-functions/logger";
import { onCall } from "firebase-functions/v2/https";
import { defineSecret } from "firebase-functions/params";
const apiKey = defineSecret("12345abcde");
import { initializeApp } from "firebase-admin/app";
initializeApp();
export const CallUppercaseMe2ndGen = onCall({ secrets: [apiKey] }, (request) => {
if (request.data != undefined) {
if (request.auth != undefined) {
const text = request.data.text;
const uid = request.auth.uid;
logger.log("Uppercasing", uid, text);
const uppercase = text.toUpperCase();
return uppercase;
} else {
return null;
}
} else {
return null;
}
});
angular.component.ts
async callMe() {
const upperCaseMe = httpsCallable(this.functions, 'CallUppercaseMe2ndGen');
// const upperCaseMe = httpsCallableFromURL(this.functions, 'http://localhost:5001/my-project/us-central1/CallUppercaseMe2ndGen');
// const upperCaseMe = httpsCallableFromURL(this.functions, 'https://us-central1-my-project.cloudfunctions.net/CallUppercaseMe2ndGen');
upperCaseMe({
text: 'hello world',
})
.then((result) => {
console.log(result)
})
.catch((error) => {
console.error(error);
});
}
这会引发 CORS 错误:
来自来源 'http://localhost:4200' 对 'https://us-central1/my-project.cloudfunctions.net/CallUppercaseMe2ndGen' 的 fetch 已被 CORS 策略阻止:响应预检请求不通过访问控制检查:请求的资源上不存在 'Access-Control-Allow-Origin' 头。如果不透明响应满足您的需求,请将请求的模式设置为 'no-cors' 以禁用 CORS 获取资源。
我知道一些解决云函数中的 CORS 错误的方法。
-
使用 IAM 服务帐户。这是我在第一代云函数中的首选方法。在第二代云函数中,我放入了我的 API 密钥。我怀疑问题在于我需要放入更多内容才能将云函数连接到 IAM 服务帐户。我该如何操作?
-
在
firebase.json
中设置 "Access-Control-Allow-Origin"。这在已部署的云函数中有效,但在模拟器中无效。 -
在
onRequest
中导入cors
。这适用于onRequest
,而不适用于onCall
。我不使用 HTTP 请求来调用云函数。 -
使用
httpsCallableFromURL
而不是httpsCallable
。Doug Stevenson 告诉我不要使用这个解决方案,但有时它可以修复 CORS 错误。 -
在第二代云函数中,您可以在
onRequest
中配置 CORS。我使用的是onCall
,而不是onRequest
。我尝试将参数{secrets: [apiKey]}
替换为{cors: true}
,但没有帮助。
如有需要,我将展示这五种解决方案的代码。
I've provided the translated code portions. If you need assistance with any of the five solutions mentioned, please let me know.
<details>
<summary>英文:</summary>
I'm trying the [2nd generation][1] Cloud Functions for Firebase. I have `makeUppercase()` firing with `onDocumentCreated()`. Now I'm trying to get an Angular app to call a callable cloud function.
*index.ts*
```js
import * as logger from "firebase-functions/logger";
import { onCall } from "firebase-functions/v2/https";
import { defineSecret } from "firebase-functions/params";
const apiKey = defineSecret("12345abcde");
import { initializeApp } from "firebase-admin/app";
initializeApp();
export const CallUppercaseMe2ndGen = onCall({ secrets: [apiKey] }, (request) => {
if (request.data != undefined) {
if (request.auth != undefined) {
const text = request.data.text;
const uid = request.auth.uid;
logger.log("Uppercasing", uid, text);
const uppercase = text.toUpperCase();
return uppercase;
} else {
return null;
}
} else {
return null;
}
});
angular.component.ts
async callMe() {
const upperCaseMe = httpsCallable(this.functions, 'CallUppercaseMe2ndGen');
// const upperCaseMe = httpsCallableFromURL(this.functions, 'http://localhost:5001/my-project/us-central1/CallUppercaseMe2ndGen');
// const upperCaseMe = httpsCallableFromURL(this.functions, 'https://us-central1-my-project.cloudfunctions.net/CallUppercaseMe2ndGen');
upperCaseMe({
text: 'hello world',
})
.then((result) => {
console.log(result)
})
.catch((error) => {
console.error(error);
});
}
This throws a CORS error:
Access to fetch at 'https://us-central1/my-project.cloudfunctions.net/CallUppercaseMe2ndGen' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I know a few ways to fix a CORS error in a cloud function.
-
Use an IAM service account. This is my preference in 1st generation cloud functions. In the 2nd generation cloud function I put in my API key. I suspect the problem is that I need to put in more than the API key to connect a cloud function to an IAM service account. How do I do this?
-
Set "Access-Control-Allow-Origin" in
firebase.json
. This works in a deployed cloud function but not in the emulator. -
Import
cors
withonRequest
. This works withonRequest
, not withonCall
. I don't use HTTP requests to call cloud functions. -
Use
httpsCallableFromURL
instead ofhttpsCallable
. Doug Stevenson has discouraged me from this solution but it sometimes fixes the CORS error. -
New in the 2nd generation cloud functions, you can configure CORS in
onRequest
. I'm usingonCall
, notonRequest
. I tried replacing the parameter{secrets: [apiKey]}
with{cors: true}
but that didn't help.
On request I'll show code for each of these five solutions.
答案1
得分: 3
I didn't say that the CORS errors were thrown when I was using the emulator, not the cloud. Everything is working now. I did two things:
-
I deployed my functions to the Firebase cloud. The first deployment ended with an error message saying that Firebase had to configure stuff for 2nd gen cloud functions. The next deployment went through and my functions worked in the cloud.
-
I added this code at the top of the Angular handler function:
connectFunctionsEmulator(this.functions, "127.0.0.1", 5001);
This code makes the function run in the emulator. Commenting it out makes the function run in the cloud.
The CORS errors were thrown because my Angular app was trying to call the functions in the cloud, but the functions hadn't been deployed to the cloud. I hadn't put in connectFunctionsEmulator()
so the functions weren't running in the emulator.
In other words, the CORS error were a red herring that sent me down a rabbit hole. If the error had said, "No such function found in the Firebase cloud and no emulator is connected," I would've figured out the error quickly.
I tried the same function with { secrets: [apiKey]
, with { cors: true }
, and with neither. All three worked.
英文:
I didn't say that the CORS errors were thrown when I was using the emulator, not the cloud. Everything is working now. I did two things:
-
I deployed my functions to the Firebase cloud. The first deployment ended with an error message saying that Firebase had to configure stuff for 2nd gen cloud functions. The next deployment went through and my functions worked in the cloud.
-
I added this code at the top of the Angular handler function:
connectFunctionsEmulator(this.functions, "127.0.0.1", 5001);
This code makes the function run in the emulator. Commenting it out makes the function run in the cloud.
The CORS errors were thrown because my Angular app was trying to call the functions in the cloud, but the functions hadn't been deployed to the cloud. I hadn't put in connectFunctionsEmulator()
so the functions weren't running in the emulator.
In other words, the CORS error were a red herring that sent me down a rabbit hole. If the error had said, "No such function found in the Firebase cloud and no emulator is connected," I would've figured out the error quickly.
I tried the same function with { secrets: [apiKey]
, with { cors: true }
, and with neither. All three worked.
答案2
得分: 1
-
import { setGlobalOptions } from 'firebase-functions/v2' // 将所有函数定位到最接近用户的位置 setGlobalOptions({ region: "us-central1" })
-
创建一个新命名的函数,它只是您先前函数的副本,但包括
{cors: true}
。export const myFunctionCopy = onCall({ cors: true }, async (request) => { ... }
-
相应地将您的前端代码指向这个新命名的函数。
-
部署
我怀疑Google后端存在某种缓存/传播问题。重新命名函数(而不仅仅是更新代码)可以避免这种情况。
英文:
Had the same problem. I'm not sure which one of these actions (or all of them) solved the problem, but here's what I did.
-
import { setGlobalOptions } from 'firebase-functions/v2' // locate all functions closest to users setGlobalOptions({ region: "us-central1" })
-
Create a newly named function that's just a copy of your previous function, but includes
{cors: true}
export const myFunctionCopy = onCall({ cors: true }, async (request) => { ... }
-
Point your front-end code to this newly named function accordingly.
-
Deploy
I suspect there's some sort of cache / propagation issue going on with Google's backend. Renaming the function (as opposed to just updating the code) avoids this.
答案3
得分: 0
这个问题通过访问谷歌控制台中的云函数,并且如果是 v2 函数的话,需要 Cloud Run Invoker,"allUsers" 或 "allAuthenticatedUsers" 来解决。我曾以为如果是 "Functions Invoker" 就可以,但他们已经将其迁移到了 Cloud Run。
英文:
This was fixed for me by going to the cloud function in google console, and if its a v2 function, it needs Cloud Run Invoker, "allUsers" or "allAuthenticatedUsers". I had thought it would be okay if it was a "Functions Invoker", but they have migrated it to Cloud Run.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论