从Angular?调用Firebase可调用函数时出现CORS错误

efzxgjgh  于 2022-12-14  发布在  Angular
关注(0)|答案(1)|浏览(154)

我的代码可以编译,但是当我调用函数时,我得到了一个CORS错误。阅读this answer时,似乎Angular正在向Firebase云函数发送一个获取请求,但是Firebase云函数拒绝了该请求,并且有某种方法可以告诉Firebase云函数接受来自Angular的请求(http://localhost:4200/)?
我看到了storage.rules,但没有看到functions.rules文件。在我的Firebase控制台中,我看到了Firestore和存储的规则,但没有看到函数的规则。
我将hosting添加到firebase.json

"hosting": {
    "rewrites": [
      {
        "source": "/callMe",
        "function": "callMe"
      },
    ]
  }

我尝试了httpsCallableFromURL而不是httpsCallable

const addMessage = httpsCallableFromURL(this.functions, 'http://localhost:5001/triggerable-functions-project/us-central1/addMessage');

以下是从应用程序调用Firebase云函数的文档。
以下是错误消息。

Access to fetch at 'https://us-central1-triggerable-functions-project.cloudfunctions.net/addMessage' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
POST https://us-central1-triggerable-functions-project.cloudfunctions.net/addMessage net::ERR_FAILED
...hundreds of frames...
ERROR Error: Uncaught (in promise): FirebaseError: internal
FirebaseError: internal
    at resolvePromise (zone.js:1214:31)
    at resolvePromise (zone.js:1168:17)
    at zone.js:1281:17
    at _ZoneDelegate.invokeTask (zone.js:409:31)
    at core.mjs:25298:55
    at AsyncStackTaggingZoneSpec.onInvokeTask (core.mjs:25298:36)
    at _ZoneDelegate.invokeTask (zone.js:408:60)
    at Object.onInvokeTask (core.mjs:25606:33)
    at _ZoneDelegate.invokeTask (zone.js:408:60)
    at Zone.runTask (zone.js:178:47)

我看了看AngularFire,但放弃了。代码似乎是针对AngularFire 6和Firebase 8的,文档最后一次更新是2021年8月21日。AngularFire 7使用Firebase 9,这是实质性的不同。
下面是我的代码:

  • app.component.ts* 在第30行抛出错误。
import { Component } from '@angular/core';
import { getFunctions, httpsCallable, httpsCallableFromURL } from "firebase/functions";
import { initializeApp } from 'firebase/app';
import { getFirestore, setDoc, doc } from "firebase/firestore";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  firebaseConfig = {
    apiKey: "...",
    authDomain: "triggerable-functions-project.firebaseapp.com",
    projectId: "triggerable-functions-project",
    storageBucket: "triggerable-functions-project.appspot.com",
    messagingSenderId: "...",
    appId: "..."
  };

  firebaseApp = initializeApp(this.firebaseConfig);
  db = getFirestore(this.firebaseApp);

  messageText: string | null = null;
  functions = getFunctions(this.firebaseApp);

  callMe(messageText: string | null) {
    console.log("Calling Cloud Function: " + messageText);
    const addMessage = httpsCallable(this.functions, 'addMessage');
    // const addMessage = httpsCallableFromURL(this.functions, 'http://localhost:5001/triggerable-functions-project/us-central1/addMessage');

    addMessage({ text: messageText }) // throws error here
      .then((result) => {
        console.log(result.data)
      });
  };
}
  • app.component.html* 创建一个表单,您可以键入消息,然后单击Submit
<form (ngSubmit)="callMe(messageText)">
    <input type="text" [(ngModel)]="messageText" name="message" placeholder="message" required>
    <button type="submit" value="Submit">Submit</button>
</form>
  • index.js* 是云函数:
import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getFirestore, connectFirestoreEmulator, setDoc, doc } from "firebase/firestore";

const firebaseConfig = {
    apiKey: "...",
    authDomain: "triggerable-functions-project.firebaseapp.com",
    projectId: "triggerable-functions-project",
    storageBucket: "triggerable-functions-project.appspot.com",
    messagingSenderId: "...",
    appId: "..."
};

const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);
connectFirestoreEmulator(db, 'localhost', 8080);

export const addMessage = functions.https.onCall((data, context) => {
  try {
    const original = snap.data().original;
    const uppercase = original.toUpperCase();
    return snap.ref.set({ uppercase }, { merge: true });
  } catch (error) {
    console.error(error);
  }
});
  • 应用程序模块.ts*
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { environment } from '../environments/environment';

// Angular
import { FormsModule } from '@angular/forms';

// AngularFire
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
import { provideFirestore, getFirestore, connectFirestoreEmulator } from '@angular/fire/firestore';
import { provideFunctions,getFunctions, connectFunctionsEmulator, httpsCallable, httpsCallableData, httpsCallableFromURL } from '@angular/fire/functions';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
    provideFirestore(() => {
      const firestore = getFirestore();
      if (!environment.production) {
        connectFirestoreEmulator(firestore, 'localhost', 8080);
      }
      return firestore;
    }),
    provideFunctions(() => {
      const functions = getFunctions();
      if (!environment.production) {
        connectFunctionsEmulator(functions, 'localhost', 5001);
      }
      return functions;
    }),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
eoxn13cs

eoxn13cs1#

我把httpsCallable改成了httpsCallableFromURL。然后GitHub Copilot写了剩下的行。Copilot太棒了!

callMe(messageText: string | null) {
    const addMessage = httpsCallableFromURL(this.functions, 'http://localhost:5001/triggerable-functions-project/us-central1/addMessage');
    addMessage({ text: messageText })
      .then((result) => {
        console.log(result.data)
      });
  };

请注意,URL包括服务器位置。本文档说明这可以优化性能。
在函数中,我将snap更改为data。我还去掉了配置和初始化的内容,代码在没有它的情况下运行。

  • 索引.js*
import * as functions from "firebase-functions";

export const addMessage = functions.https.onCall((data, context) => {
  try {
    const original = data.text;
    const uppercase = original.toUpperCase();
    return uppercase;
  } catch (error) {
    console.error(error);
  }
});

添加hostingfirebase.json是不必要的,我把它去掉了。
app.component.ts的样板文件太多。让我们改为引用environment

  • 应用程序.组件.ts*
import { Component } from '@angular/core';
import { getFunctions, httpsCallableFromURL } from "firebase/functions";
import { initializeApp } from 'firebase/app';
import { getFirestore } from "firebase/firestore";
import { environment } from '../environments/environment';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  firebaseConfig = environment.firebaseConfig;
  firebaseApp = initializeApp(this.firebaseConfig);
  db = getFirestore(this.firebaseApp);

  messageText: string | null = null;
  functions = getFunctions(this.firebaseApp);

  callMe(messageText: string | null) {
    const addMessage = httpsCallableFromURL(this.functions, 'http://localhost:5001/triggerable-functions-project/us-central1/addMessage');
    addMessage({ text: messageText })
      .then((result) => {
        console.log(result.data)
      });
  };
}

相关问题