Firebase 云函数上的预检错误

Aio*_*ion 3 javascript cors firebase typescript google-cloud-functions

当我尝试从我的网站调用我的云函数时,我遇到了预检错误。我在我的云函数中实现了cors模块,并且我的请求获得了cors标头授权

云功能:

const cors = require('cors')({ origin: true });
exports.CLOUDFUNCTION = functions.https.onRequest(
  (request: any, response: any) => {
    cors(request, response, async () => {
      response.status(200).send('hello');
    })
  }
);
Run Code Online (Sandbox Code Playgroud)

网站要求:

fetch('FIREBASE_URL/CLOUDFUNCTION',
          {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              'Access-Control-Allow-Origin': '*',
              'Access-Control-Allow-Headers': 'Content-Type',
              'Access-Control-Allow-Headers': 'Authorization'
               
            },
            body: JSON.stringify(body), // body is a simple {"variable": "value"}
          }
        );
Run Code Online (Sandbox Code Playgroud)

错误

Access to fetch at 'FIREBASE_URL/CLOUDFUNCTION' from origin 'MYWEBSITE' 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.
Run Code Online (Sandbox Code Playgroud)

sam*_*man 10

如果您在尝试通过 URL 访问函数时收到 403 禁止错误,则说明函数的部署、配置存在问题,或者您在 URL 中犯了错误。

注意:虽然我在这里使用“传统”require语句来匹配您的示例,但我鼓励您对任何新编写的函数使用较新的 ES6+ JavaScript 功能(constletasync/ awaitimport等)。

使用最新firebase-tools版本进行部署

确保您使用最新版本的firebase-toolsCLI 进行部署。

v7.7.0发布后firebase-tools(2020 年 1 月 15 日),在服务器上调用 Cloud Functions 的方式发生了变化,只有经过身份验证的用户才能调用函数。为了供 Firebase 用户访问,必须通过显式授予该allUsersCloud Function Invoker权限来公开这些函数。

v7.7.0以后的版本中,这将作为部署的一部分为您完成。但是,如果您使用旧版本部署功能,则需要自行配置此权限或使用较新firebase-tools版本重新部署。

检查导出的函数名称

确保导出的函数按照部署后的预期命名。

特别要密切注意您的函数何时有意或无意地作为函数组的一部分导出。当您将函数拆分为多个文件时,通常会出现这种情况。在下面的代码块中,CLOUDFUNCTION导出为myFunctions-CLOUDFUNCTION而不是CLOUDFUNCTION如您所期望的那样。

// myFunctions.js
exports.CLOUDFUNCTION = functions.https.onRequest(...);
Run Code Online (Sandbox Code Playgroud)
// index.js (incorrect)
exports.myFunctions = require("./myFunctions.js");
Run Code Online (Sandbox Code Playgroud)
// index.js (correct)
const myFunctions = require("./myFunctions.js");

exports.CLOUDFUNCTION = myFunctions.CLOUDFUNCTION;
Run Code Online (Sandbox Code Playgroud)

检查函数的 URL

检查您使用的 Cloud Functions URL 是否有拼写错误。Cloud Functions URL 中的函数名称区分大小写。

正确的 URL 应遵循以下格式:

// myFunctions.js
exports.CLOUDFUNCTION = functions.https.onRequest(...);
Run Code Online (Sandbox Code Playgroud)

例子:

// index.js (incorrect)
exports.myFunctions = require("./myFunctions.js");
Run Code Online (Sandbox Code Playgroud)

处理 CORS 错误并停止处理

在您的代码示例中,您传入的NextFunction没有错误处理程序。在使用时{ origin: true },这“很好”,但是当您开始限制调用函数的来源时,您就会开始遇到麻烦。这对于防止您的函数被其 URL(其中是origin)直接调用特别方便undefined。请查看文档或下一节以获取更多信息。

const cors = require('cors')({ origin: true });
exports.CLOUDFUNCTION = functions.https.onRequest(
  (request, response) => { // <-- don't use `: any` here, as you are disabling the built-in types provided by firebase-functions
    cors(request, response, async (err) => {
      if (err) {
        // Denied by CORS/error with CORS configuration
        console.error("CORS blocked request -> ", err);
        response.status(403).send("Forbidden by CORS");
        return;
      }

      response.status(200).send('hello');
    })
  }
);
Run Code Online (Sandbox Code Playgroud)

可选:收紧cors配置

虽然您可以Access-Control-*使用cors包反映标头,但请考虑在服务器端显式设置这些标头。

const { projectId: PROJECT_ID } = JSON.parse(process.env.FIREBASE_CONFIG);

const cors = require('cors')({
  // during emulation, allow localhost & calling directly (i.e. no origin specified);
  // at all other times, restrict to deployed hosting sites only
  origin: process.env.FUNCTIONS_EMULATOR === "true"
    ? /^(https?:\/\/localhost:\d+|undefined)$/
    : [`https://${PROJECT_ID}.firebaseapp.com`, `https://${PROJECT_ID}.web.app`],
  allowedHeaders: ['Content-Type', 'Authorization']
});

exports.CLOUDFUNCTION = functions.https.onRequest(
  (request, response) => {
    cors(request, response, async (err) => {
      if (err) {
        // Denied by CORS/error with CORS configuration
        console.error("CORS blocked request -> ", err);
        response.status(403).send("Forbidden by CORS");
        return;
      }

      response.status(200).send('hello');
    })
  }
);
Run Code Online (Sandbox Code Playgroud)

这简化了您的客户端代码:

fetch('FIREBASE_URL/CLOUDFUNCTION',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  }
);
Run Code Online (Sandbox Code Playgroud)

可选:使用可调用函数

如果您的函数需要您代表用户执行操作,您可以使用可调用云函数而不是更简单的 HTTPS 请求函数。此版本的 HTTPS 函数处理 CORS、身份验证,并支持基于 Promise 的数据返回。

注意:这仍然需要如上所述将该函数公开。

在服务器端:

exports.CLOUDFUNCTION = functions.https.onCall(async (data, context) => {
  if (!context.auth) {
    // users must be logged in
    throw new functions.https.HttpsError(
      'failed-precondition',
      'The function must be called while authenticated.'
    );
  }

  if (data.variable === undefined)) {
    throw new functions.https.HttpsError(
      'invalid-argument',
      'Parameter "variable" must be a string'
    );
  }

  // you can return a promise here
  // this sends back the JSON string "hello world"
  return "hello world";
});
Run Code Online (Sandbox Code Playgroud)

在客户端:

const callFunction = firebase.functions().httpsCallable('CLOUDFUNCTION');

callFunction(body)
  .then(
    (responseData) => {
      // TODO: handle response
    },
    (functionError) => {
      // TODO: handle error
    }
  );
Run Code Online (Sandbox Code Playgroud)