简体   繁体   English

Firebase 云功能上的预检错误

[英]Preflight error on Firebase cloud functions

I'm facing a preflight error when I try to call a cloud function of mine from my website.当我尝试从我的网站调用我的云 function 时,我遇到了预检错误。 I implemented the cors module in my cloud function, and my request got the cors header authorizations我在我的云 function 中实现了 cors 模块,我的请求得到了 cors Z099FB995346F31C75E3F6 授权

The cloud function:云function:

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

The website request:网站要求:

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"}
          }
        );

The error错误

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.

If you are getting a 403 Forbidden Error when trying to access your function via it's URL, you have a problem with your function's deployment, it's configuration or you have made a mistake in the URL.如果您在尝试通过 URL 访问您的 function 时遇到 403 禁止错误,则您的函数部署有问题,它的配置有问题,或者您在 ZE485903D2A8D2C5DZ 中出错了

Note: While I use "traditional" require statements here to match your example, I encourage you to use newer ES6+ JavaScript features ( const , let , async / await , import , etc.) for any newly written functions.注意:虽然我在此处使用“传统” require语句来匹配您的示例,但我鼓励您对任何新编写的函数使用更新的 ES6+ JavaScript 功能( constletasync / awaitimport等)。

Deploy using the latest firebase-tools version使用最新的firebase-tools版本进行部署

Make sure you are deploying using the latest version of the firebase-tools CLI.确保您使用最新版本的firebase-tools CLI 进行部署。

When v7.7.0 of firebase-tools released (Jan 15, 2020), the way Cloud Functions are invoked on the server changed so that functions could be invoked by only authenticated users.v7.7.0 firebase-tools v7.7.0 发布(2020 年 1 月 15 日)时,在服务器上调用 Cloud Functions 的方式发生了变化,因此只能由经过身份验证的用户调用函数。 To be accessible to Firebase Users, these functions must be made public by explicitly granting the allUsers group the Cloud Function Invoker permission.为了让 Firebase 用户可以访问,必须通过显式授予allUsersCloud Function Invoker权限来公开这些功能。

In v7.7.0 and later, this is done for you as part of deployment.v7.7.0及更高版本中,这是作为部署的一部分为您完成的。 However, if you deploy functions using an older version, you will need to configure this permission yourself or redeploy using a newer firebase-tools version.但是,如果您使用旧版本部署功能,则需要自己配置此权限或使用更新的firebase-tools版本重新部署。

Check the exported function name检查导出的 function 名称

Make sure the function you export is named what you expect once deployed.确保您导出的 function 在部署后命名为您期望的名称。

In particular, pay close attention to when your function is exported as part of a function group either deliberately or accidentally.特别是,当您的 function 被有意或无意地作为function 组的一部分导出时,请密切注意。 This often turns up when you've split your functions into multiple files.当您将函数拆分为多个文件时,通常会出现这种情况。 In the below code blocks, CLOUDFUNCTION gets exported as myFunctions-CLOUDFUNCTION and not just CLOUDFUNCTION as you may expect.在下面的代码块中, CLOUDFUNCTION被导出为myFunctions-CLOUDFUNCTION而不仅仅是CLOUDFUNCTION如您所料。

// myFunctions.js
exports.CLOUDFUNCTION = functions.https.onRequest(...);
// index.js (incorrect)
exports.myFunctions = require("./myFunctions.js");
// index.js (correct)
const myFunctions = require("./myFunctions.js");

exports.CLOUDFUNCTION = myFunctions.CLOUDFUNCTION;

Check the function's URL检查函数的 URL

Check the Cloud Functions URL you are using for typos.检查您使用的 Cloud Functions URL 是否存在拼写错误。 Function names in Cloud Functions URLs are case-sensitive. Cloud Functions URL 中的 Function 名称区分大小写。

The correct URL should follow the format:正确的 URL 应遵循以下格式:

https://<REGION>-<PROJECT_ID>.cloudfunctions.net/<EXPORTED_FUNCTION_NAME>

Example:例子:

https://us-central1-fir-sandbox.cloudfunctions.net/echo

Handle CORS errors & stop processing处理 CORS 错误并停止处理

In your code example, you pass in the NextFunction without an error handler.在您的代码示例中,您传入NextFunction而没有错误处理程序。 While using { origin: true } , this is "fine", but you'll start running into trouble when you start restricting the origins you call your function from.使用{ origin: true }时,这“很好”,但是当您开始限制您调用 function 的来源时,您将开始遇到麻烦。 This is particularly handy for preventing your functions being invoked directly by their URL (where origin would be undefined ).这对于防止您的函数被它们的 URL (其中origin将是undefined )直接调用特别方便。 Take a look at the documentation or the next section for more info.查看文档或下一部分以获取更多信息。

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');
    })
  }
);

Optional: Tighten the cors configuration可选:拧紧cors配置

While you can reflect the Access-Control-* headers using the cors package, consider explicitly setting these server-side.虽然您可以使用cors package 反映Access-Control-*标头,但请考虑明确设置这些服务器端。

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');
    })
  }
);

This simplifies your client-side code:这简化了您的客户端代码:

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

Optional: Use Callable Functions可选:使用可调用函数

If your functions will require you to do actions on behalf of a user, you could make use of Callable Cloud Functions instead of the more bare-bones HTTPS Request functions.如果您的函数需要您代表用户执行操作,您可以使用Callable Cloud Functions而不是更简单的 HTTPS 请求函数。 This version of a HTTPS Function handles CORS, authentication, and supports Promise-based returning of data.这个版本的 HTTPS Function 处理 CORS,身份验证,并支持基于 Promise 的数据返回。

Note: This will still require the function to be public as described above.注意:这仍然需要 function 如上所述公开。

On the server side:在服务器端:

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";
});

On the client side:在客户端:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM