在 Cloud Functions for Firebase 中启用 CORS

Enabling CORS in Cloud Functions for Firebase

我目前正在学习如何使用新的 Cloud Functions for Firebase,我遇到的问题是我无法访问我通过 AJAX 请求编写的函数。我收到 "No 'Access-Control-Allow-Origin'" 错误。这是我编写的函数示例:

exports.test = functions.https.onRequest((request, response) => {
  response.status(500).send({test: 'Testing functions'});
})

函数位于此 url: https://us-central1-fba-shipper-140ae.cloudfunctions.net/test

Firebase 文档建议在函数内添加 CORS 中间件,我试过了但对我不起作用:https://firebase.google.com/docs/functions/http-events

我是这样做的:

var cors = require('cors');    

exports.test = functions.https.onRequest((request, response) => {
   cors(request, response, () => {
     response.status(500).send({test: 'Testing functions'});
   })
})

我做错了什么?如果对此有任何帮助,我将不胜感激。

更新:

Doug Stevenson 的回答很有帮助。添加 ({origin: true}) 解决了这个问题,我还不得不将 response.status(500) 更改为 response.status(200) ,我一开始完全错过了。

Firebase 团队提供了两个 sample functions 来演示 CORS 的使用:

第二个示例使用与您当前使用的不同的方式处理 cors。

考虑像这样导入,如示例所示:

const cors = require('cors')({origin: true});

函数的一般形式如下:

exports.fn = functions.https.onRequest((req, res) => {
    cors(req, res, () => {
        // your function body here - use the provided req and res from cors
    })
});

我刚刚就此发表了一篇小文章:

https://mhaligowski.github.io/blog/2017/03/10/cors-in-cloud-functions.html

一般来说,您应该使用 Express CORS package,这需要一些技巧才能满足 GCF/Firebase 函数中的要求。

希望对您有所帮助!

一条额外的信息,只是为了那些在一段时间后谷歌搜索的人: 如果您使用的是 firebase 托管,您还可以设置重写,例如 url like (firebase_hosting_host)/api/myfunction 重定向到 (firebase_cloudfunctions_host)/doStuff 函数.这样,由于重定向是透明的并且在服务器端进行,因此您不必处理 cors。

您可以在 firebase.json 中使用重写部分进行设置:

"rewrites": [
        { "source": "/api/myFunction", "function": "doStuff" }
]

我对@Andreys 对他自己的问题的回答有一点补充。

看来您不必在 cors(req, res, cb) 函数中调用回调,因此您只需在函数顶部调用 cors 模块即可,而无需将所有代码嵌入回调中。如果你想在之后实施 cors,这会快得多。

exports.exampleFunction = functions.https.onRequest((request, response) => {
    cors(request, response, () => {});
    return response.send("Hello from Firebase!");
});

不要忘记初始化cors,如开头所述post:

const cors = require('cors')({origin: true});

更新: 任何需要时间的响应函数在这个实现中都有 CORS 错误的风险,因为它没有合适的 async/await。不要在 return 静态数据的快速原型端点之外使用。

对于任何试图在 Typescript 中执行此操作的人来说,这是代码:

import * as cors from 'cors';
const corsHandler = cors({origin: true});

export const exampleFunction= functions.https.onRequest(async (request, response) => {
       corsHandler(request, response, () => {});
       //Your code here
});

没有适合我的 CORS 解决方案...直到现在!

不确定是否有其他人 运行 遇到了与我相同的问题,但我从我发现的示例中以 5 种不同的方式设置了 CORS,但似乎没有任何效果。我用 Plunker 建立了一个最小的例子,看看它是否真的是一个错误,但是这个例子 运行 很漂亮。我决定检查 firebase 函数日志(在 firebase 控制台中找到),看看是否可以告诉我任何信息。 我的节点服务器代码中有几个错误与 CORS 无关,当我调试时 释放了我的 CORS 错误消息。我不知道为什么与 CORS 无关的代码错误 returns CORS 错误响应,但它让我误入了错误的兔子洞好几个小时...

tl;dr - 如果 CORS 解决方案不起作用,请检查你的 firebase 函数日志,并调试你遇到的任何错误

您可以像这样在云函数中设置 CORS

response.set('Access-Control-Allow-Origin', '*');

无需导入 cors

值得一提的是,我在将 app 传递给 onRequest 时遇到了同样的问题。我意识到问题是 firebase 函数请求 url 的尾部斜杠。 Express 正在寻找 '/',但我没有函数 [project-id].cloudfunctions.net/[function-name] 的尾部斜线。 CORS 错误是假阴性。当我添加尾部斜杠时,我得到了预期的响应。

如果您不使用 Express 或只是想使用 CORS。下面的代码将有助于解决

const cors = require('cors')({ origin: true, });   
exports.yourfunction = functions.https.onRequest((request, response) => {  
   return cors(request, response, () => {  
        // *Your code*
    });
});

只有这种方式对我有用,因为我在我的请求中获得了授权:

exports.hello = functions.https.onRequest((request, response) => {
response.set('Access-Control-Allow-Origin', '*');
response.set('Access-Control-Allow-Credentials', 'true'); // vital
if (request.method === 'OPTIONS') {
    // Send response to OPTIONS requests
    response.set('Access-Control-Allow-Methods', 'GET');
    response.set('Access-Control-Allow-Headers', 'Content-Type');
    response.set('Access-Control-Max-Age', '3600');
    response.status(204).send('');
} else {
    const params = request.body;
    const html = 'some html';
    response.send(html)
} )};

这可能会有帮助。 我用 express(custom URL)

创建了 firebase HTTP 云函数
const express = require('express');
const bodyParser = require('body-parser');
const cors = require("cors");
const app = express();
const main = express();

app.post('/endpoint', (req, res) => {
    // code here
})

app.use(cors({ origin: true }));
main.use(cors({ origin: true }));
main.use('/api/v1', app);
main.use(bodyParser.json());
main.use(bodyParser.urlencoded({ extended: false }));

module.exports.functionName = functions.https.onRequest(main);

请确保您添加了重写部分

"rewrites": [
      {
        "source": "/api/v1/**",
        "function": "functionName"
      }
]

如果有像我这样的人:如果你想从与云函数本身相同的项目中调用云函数,你可以初始化 firebase sdk 并使用 onCall 方法。它会为您处理一切:

exports.newRequest = functions.https.onCall((data, context) => {
    console.log(`This is the received data: ${data}.`);
    return data;
})

像这样调用这个函数:

// Init the firebase SDK first    
const functions = firebase.functions();
const addMessage = functions.httpsCallable(`newRequest`);

Firebase 文档:https://firebase.google.com/docs/functions/callable

如果你不能初始化SDK这里是其他建议的精华:

如果您在本地测试 firebase 应用程序,则需要将函数指向 localhost 而不是云。默认情况下,firebase servefirebase emulators:start 将函数指向服务器而不是本地主机,当您在 Web 应用程序上使用它时。

在 firebase 初始化脚本之后的 html 头部添加以下脚本:

 <script>
      firebase.functions().useFunctionsEmulator('http://localhost:5001')
 </script> 

Make sure to remove this snippet when deploying code to server.

如果您不t/can不使用 cors 插件,则在处理程序函数中首先调用 setCorsHeaders() 函数也可以。

回复时也使用 respondSuccess/Error 功能。

const ALLOWED_ORIGINS = ["http://localhost:9090", "https://sub.example.com", "https://example.com"]


// Set CORS headers for preflight requests
function setCorsHeaders (req, res) {
  var originUrl = "http://localhost:9090"


  if(ALLOWED_ORIGINS.includes(req.headers.origin)){
    originUrl = req.headers.origin
  }

  res.set('Access-Control-Allow-Origin', originUrl);
  res.set('Access-Control-Allow-Credentials', 'true');

  if (req.method === 'OPTIONS') {
    // Send response to OPTIONS requests
    res.set('Access-Control-Allow-Methods', 'GET,POST','PUT','DELETE');
    res.set('Access-Control-Allow-Headers', 'Bearer, Content-Type');
    res.set('Access-Control-Max-Age', '3600');
    res.status(204).send('');
  }
}

function respondError (message, error, code, res) {
  var response = {
    message: message,
    error: error
  }
  res.status(code).end(JSON.stringify(response));
}


function respondSuccess (result, res) {
  var response = {
    message: "OK",
    result: result
  }
  res.status(200).end(JSON.stringify(response));
}

在我的案例中,错误是由云函数调用程序限制访问引起的。请将 allUsers 添加到云函数调用程序。请抓住 link. Please refer to article 了解更多信息

true 更改为 "*" 对我有用,所以它看起来像这样:

const cors = require('cors')({ origin: "*" })

我尝试了这种方法,因为通常情况下,响应 header 是这样设置的:

'Access-Control-Allow-Origin', '*'

请注意,这将允许任何域调用您的端点,因此不安全。

此外,您还可以阅读更多文档: https://github.com/expressjs/cors

更新的答案: 使用支持 Typescript 的 cors 库:

安装cors

npm i -S cors
npm i --save-dev @types/cors

index.ts:

import * as cors from "cors";
const corsHandler = cors({ origin: true });

// allow cors in http function
export const myFunction = functions.https.onRequest((req, res) => {
corsHandler(req, res, async () => {

// your method body

 });
});

旧答案: (不再工作了)
找到了一种无需导入任何 'cors' 库即可启用 cors 的方法。它还适用于 Typescript 并在 chrome 版本 81.0.

中进行了测试
exports.createOrder = functions.https.onRequest((req, res) => {
// browsers like chrome need these headers to be present in response if the api is called from other than its base domain
  res.set("Access-Control-Allow-Origin", "*"); // you can also whitelist a specific domain like "http://127.0.0.1:4000"
  res.set("Access-Control-Allow-Headers", "Content-Type");

  // your code starts here

  //send response
  res.status(200).send();
});

如果其他解决方案中的 none 有效,您可以尝试在调用开头添加以下地址以启用 CORS - 重定向:

https://cors-anywhere.herokuapp.com/

带有 JQuery AJAX 请求的示例代码:

$.ajax({
   url: 'https://cors-anywhere.herokuapp.com/https://fir-agilan.web.app/gmail?mail=asd@gmail.com,
   type: 'GET'
});

补充一下我的经验。 我花了几个小时试图找出我出现 CORS 错误的原因。

碰巧我重命名了我的云函数(我在大升级后第一次尝试)。

因此,当我的 firebase 应用程序使用不正确的名称调用云函数时,它应该抛出 404 错误,而不是 CORS 错误。

修复我的 firebase 应用中的云函数名称解决了这个问题。

我已经在此处填写了有关此问题的错误报告 https://firebase.google.com/support/troubleshooter/report/bugs

使用 Google 云控制台仪表板的简单解决方案:

  1. 转到您的 GCP 控制台仪表板:

https://console.cloud.google.com/home/dashboard

  1. 转到菜单

"Cloud Functions" ("Compute" section)

  1. Select 您的云功能,例如“MyFunction”,右侧应该会出现一个侧边菜单,向您显示它的访问控制设置

  2. 点击“添加成员”,输入“allUsers”和select角色“Cloud Function Invoker”

  3. 保存 -> 现在,您应该会在云函数列表中看到“允许未经身份验证”的备注

现在每个人都可以通过正确配置访问您的 GCP 或 Firebase 项目。 (小心)

如果您没有捕获函数中的错误,可能会发生 cors 错误。我的建议是在你的 corsHandler

中实现一个 try catch
const corsHandler = (request, response, handler) => {
    cors({ origin: true })(request, response, async () => {
        try {
            await handler();
        }
        catch (e) {
            functions.logger.error('Error: ' + e);
            response.statusCode = 500;
            response.send({
                'status': 'ERROR' //Optional: customize your error message here
            });
        }
    });
};

用法:

exports.helloWorld = functions.https.onRequest((request, response) => {
    corsHandler(request, response, () => {
        functions.logger.info("Hello logs!");
        response.send({
            "data": "Hello from Firebase!"
        });
    });
});

感谢 Whosebug 用户:Hoang Trinh, and

通过大量搜索,我可以在同一个 firebase 文档中找到这个解决方案,只需在路径中实现 cors:

import * as express from "express";
import * as cors from "cors";


const api = express();
api.use(cors({ origin: true }));
api.get("/url", function);

Link firebase 文档:https://firebase.google.com/docs/functions/http-events

请参阅下文了解我如何使用 CORS 设置我的 Express。

'https://pericope.app' 是我的 Firebase 项目的自定义域。

看起来所有其他答案都推荐 origin:true*

我犹豫是否允许所有来源,因为它会允许其他任何人访问 api。如果您正在创建一个 public 服务,那很好,但是如果您对您的数据做任何事情,那将是有风险的,因为它是一个特权环境。例如,此管理 SDK 会绕过您为 Firestore 或存储设置的任何安全规则。

//Express
const express = require('express');
const app = express();

const cors = require('cors');
app.use(cors({
  origin: 'https://pericope.app'
}));

如果您更喜欢单一处理函数 ()

const applyMiddleware = handler => (req, res) => {
  return cors(req, res, () => {
    return handler(req, res)
  })
}
exports.handler = functions.https.onRequest(applyMiddleware(handler))

我是 Firebase 的初学者(30 分钟前注册)。我的问题是我调用了我的端点

https://xxxx-default-rtdb.firebaseio.com/myendpoint

而不是

https://xxxx-default-rtdb.firebaseio.com/myendpoint.json

如果您刚开始使用 Firebase,请确保不要忘记 .json 扩展。

我已经尝试了很长时间了。

当我进行此更改时,它终于起作用了。

app.get('/create-customer', (req, res) => {
  return cors()(req, res, () => {
    ... your code ...

最大的不同是我用了cors()(req, res...而不是直接cors(req, res...

现在完美运行。

我收到错误是因为我正在调用客户端不存在的函数。例如:

firebase.functions().httpsCallable('makeSureThisStringIsCorrect');

使用 Typescript 在 https.onRequest 上使用 cors,如下所示:

import * as cors from 'cors';
const corsHandler = cors({origin: true});

export const pingFunctionWithCorsAllowed = functions.https.onRequest((request, response) => {
  corsHandler(request, response, () => {
    response.send(`Ping from Firebase (with CORS handling)! ${new Date().toISOString()}`);
  });
});

来自Source

云功能日志记录有帮助,请检查您是否卡住了。

我的问题原来是我的云函数的类型错误,其中我有一个需要字符串的数字:

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type number (1)

出于某种原因,这让我在前端出现了 cors 错误,并且浪费了几个小时。

  1. 进入你的Google Cloud Functions。您以前可能没有见过这个平台,但这就是您解决这个 Firebase 问题的方法。
  2. 找到您要搜索的 Firebase 函数并点击名称。如果此页面为空白,您可能需要搜索 Cloud Functions 并从结果中 select 页面。
  3. 找到您的函数,单击名称。
  4. 转到权限选项卡。单击添加(添加用户)。
  5. 根据新原则,键入 'allUsers' -- 它应该在您完成键入之前自动完成。
  6. 在 select 角色下,搜索 Cloud Functions,然后选择 Invoker。
  7. 保存。
  8. 等几分钟。

这应该可以解决问题。如果没有,请执行此操作并向您的函数代码添加一个 CORS 解决方案,例如:

  exports.sendMail = functions.https.onRequest((request, response) => {
  response.set("Access-Control-Allow-Origin", "*");
  response.send("Hello from Firebase!");
});

在 devtool 控制台中出现相同的 access allow control origin 错误,我发现了其他具有更现代语法的解决方案:

我的 CORS 问题是存储(而不是 RTDB 和浏览器......),然后我没有信用卡(如上述解决方案所要求的),我的 no-credit卡片解决方案是:

  1. 安装 gsutil: https://cloud.google.com/storage/docs/gsutil_install#linux-and-macos

  2. 创建一个 cors.json 文件以使用 gsutil

    通过终端加载
gsutil cors set cors.json gs://[ your-bucket ]/-1.appspot.com

https://firebase.google.com/docs/storage/web/download-files#cors_configuration