如何在 firebase 函数环境中解决 CORS?

Posted

技术标签:

【中文标题】如何在 firebase 函数环境中解决 CORS?【英文标题】:How to solve CORS in firebase functions enviroment? 【发布时间】:2019-03-06 22:09:39 【问题描述】:

我遇到了CORS 问题。在我的functions/index.js 我有:

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

我的端点是https://sis-t.redsys.es:25443/sis/realizarPago。我需要使用来自客户端的适当参数对此 url 执行 POST,这是在 firebase 环境之外。我也有允许外部网络的正确计划,但是向与原始地址不同的地址发出请求会触发CORS 问题:

error log network log

我已经读到您只需要修改标头,但这仅适用于您向自己的服务器发出请求时。当你执行http.onRequest() 时,你可以在函数内部使用中间件,但是当你向外部服务器进行 POST 时会发生什么?

这是执行POSTaxios 函数:

cardPay: function () 
  this.cardProcess = true
    axios(
      method: 'post',
      url: 'https://us-central1-cutre-windrider.cloudfunctions.net/cardPay',
      data: 
        verifiedUserLogged: this.userLogged.uid,
        cart: this.cartItemList,
        finalPrice: this.serverPrice,
        deliveryInfo: this.userLogged.deliveryAdress,
        name: this.userLogged.displayName || false,
        email: this.userLogged.email
      
    )
    .then(response => 
      this.requestData = response
      this.redsysRedirect(response.data.data)
    )
    .catch(console.log)
    ,
redsysRedirect: function (data) 
  axios.post('https://sis-t.redsys.es:25443/sis/realizarPago', 
    'Ds_SignatureVersion': 'HMAC_SHA256_V1',
    'Ds_MerchantParameters': data.merchantParameters,
    'Ds_Signature': data.signature
  ).then(console.log).catch(console.log)

这些是服务器端函数:

exports.cardPay = functions.https.onRequest((req, res) => 
  return cors(req, res, () => 
    const cart = req.body.cart
    const user = req.body.verifiedUserLogged
    const key = admin.database().ref(`sales/$user`).push().key
    processCart(cart).then(result => 
      console.info(createPayment(result, key))
      return res.json( "data": createPayment(result, key) ).end()
    ).catch(console.log)
  )
)

function processCart(cart) 
  return new Promise((resolve, reject) => 
    Promise.all(cart.map(i => switcher(i)))
      .then(prices => resolve(
        prices.reduce(
          (finalPrice, price) => price + finalPrice, 0)
      )).catch(console.log)
  );


function switcher(item) 
  switch (item.destiny) 
    case 'bookedLessons':
        return lessonPrice(item.name, item.index)
      case 'bookedRentals':
        return rentalPrice(item.id, item.index, item.insurancePurchased, item.insuranceId)
      case 'bookedLodgins':
        return item.reservationData ? roomPriceWithReservation(item.id, item.quantity, item.persons, item.reservationData) : roomPriceNoReservation(item.id, item.quantity, item.persons)
      case 'deliveries':
        return productPrice(item.id, item.quantity)
      case 'bookedCar':
        return carPrice(item.id, item.index)
      case 'bookedStorage':
        return storagePrice(item.index)
      case 'bookedTransportation':
        return transportationPrice(item.id, item.index, item.persons, item.roundTrip)
      case 'bookedDoublePack':
        return doublePack(item.id, item.persons)
      case 'bookedTriplePack':
        return triplePack(item.id, item.persons)
      default:
        break
  


function createPayment(total, orderId) 
  let redsys = new Redsys();
  let mParams = 
      "DS_MERCHANT_AMOUNT":total.toString(),
      "DS_MERCHANT_ORDER":orderId,
      "DS_MERCHANT_MERCHANTCODE":   "025988262",
      // "DS_MERCHANT_MERCHANTCODE":tpvInfo.fucCode,
      "DS_MERCHANT_CURRENCY":"978",
      // "DS_MERCHANT_CURRENCY":tpvInfo.currency,
      "DS_MERCHANT_TRANSACTIONTYPE":"0",
      // "DS_MERCHANT_TRANSACTIONTYPE":tpvInfo.transaction_type,
      "DS_MERCHANT_TERMINAL":   "001",
      // "DS_MERCHANT_TERMINAL":tpvInfo.terminal,
      "DS_MERCHANT_MERCHANTURL":'http://localhost:8080',
      "DS_MERCHANT_URLOK":'http://localhost:8080/home?foo=true',
      "DS_MERCHANT_URLKO":'http://localhost:8080/home?foo=false'
  ;
  return  signature: redsys.createMerchantSignature(/* tpvInfo.secret */   "sq7HjrUOBfKmC576ILgskD5srU870gJ7", mParams) , merchantParameters: redsys.createMerchantParameters(mParams), raw: mParams;

【问题讨论】:

【参考方案1】:

对于将来会遇到这个问题的人(或我未来的自己):

如果您已经使用 cors 包配置了 CORS,并且您认为您配置正确,但在浏览器控制台中仍然出现 CORS 错误,请查看这篇文章:

https://haha.world/firebase-cors/

基本上,这是从 Google Cloud Functions 返回的误导性错误,而实际上该错误在您的代码逻辑中(与 CORS 完全无关)

因此,修复此错误的第一步是检查 Google Cloud Functions 日志(或 Firebase Cloud Functions 日志),以查看您的函数是否因代码中的任何错误而崩溃。然后修复它。


注意:对于没有我上面描述的问题的人,您可以查看其他答案,或查看以下资源:

    https://expressjs.com/en/resources/middleware/cors.html https://firebase.google.com/docs/functions/http-events#using_existing_express_apps

【讨论】:

对我来说,问题是我需要重新导出 Google 凭据文件。如果 CORS 之前为您工作过,请忽略此错误并在其他地方查找问题。 在您的 Firebase 日志中注意“未处理的拒绝” 哇。就是这样。函数错误,返回 cors 错误。 绝对正确!就我而言,我使用“Test”字符串调用“test”函数,但遇到了与 CORS 相关的错误。【参考方案2】:

在您的 Firebase 函数响应标头中,您可以明确允许所有来源:

exports.handler = ((req, res) => 
    res.set( 'Access-Control-Allow-Origin': '*' ).sendStatus(200)
)

或者您可以修改它以仅允许特定来源。这通常是我过去使用 Firebase 函数解决 CORS 问题的方式。

【讨论】:

然后响应 res.json(foo: bar)?因为这会覆盖标题,并导致 Can't set headers after they are sent 是的,如果您想发送 json 响应,您可以这样做:res.set( 'Access-Control-Allow-Origin': '*' ).status(200).json(foo: bar) Access-Control-Allow-Origin': '*' 非常危险,难道没有更安全的方法可以从本地应用程序测试您的功能吗?【参考方案3】:

查看https://cloud.google.com/functions/docs/writing/http#handling_cors_requests。从那个文件 -

exports.corsEnabledFunction = (req, res) => 
  // Set CORS headers
  // e.g. allow GETs from any origin with the Content-Type header
  // and cache preflight response for an 3600s
  res.set("Access-Control-Allow-Origin", "*");
  res.set("Access-Control-Allow-Methods", "GET");
  res.set("Access-Control-Allow-Headers", "Content-Type");
  res.set("Access-Control-Max-Age", "3600");
  // Send response to OPTIONS requests and terminate the function execution
  if (req.method == 'OPTIONS') 
    res.status(204).send('');
  
  // Continue with function code
  ...

【讨论】:

在您附加的文档中,指定使用 firebase 函数时如何处理 CORS:yourdomain.com 向 region-project.cloudfunctions.net/yourfunction 发出请求,但情况并非如此,在这种情况下您可以访问 @ 987654327@,但我必须为一个不是函数的站点创建一个POST。顺便说一句,除非您指的是全局配置它们,否则我没有使用 Express。 有什么方法可以将这些相同的标头添加到来自sis-t.redsys.es:25443/sis/realizarPago 的响应中?如果是这样,即使它不是云功能,它也应该可以工作。 sis-t.redsys.es:25443/sis/realizarPago 只是我需要POST 的网址,在向该网址发出请求之前我不会得到回复。使用@Jason Dark 的anwser:res.set( 'Access-Control-Allow-Origin': '*' ).sendStatus(200) return res.send(200, "data": createPayment(result, key) ).end(); 这会导致Can't set headers after they are sent 当您尝试我上面提到的内容时会发生什么? @jasonDark 的答案有一个缺陷,它一直在发送响应代码,这就是您收到错误的原因。此外,它不会发送 204 状态代码来响应预检核心选项。可能值得通过 developer.mozilla.org/en-US/docs/Web/HTTP/CORS 阅读有关 CORS 的更多信息。 我花了太多时间寻找这个!干得好

以上是关于如何在 firebase 函数环境中解决 CORS?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NestJs 在 firebase 函数中激活 CORS

如何在firebase函数中将参数传递给cors

在 firebase 函数中启用 CORS

使用 CORS 测试 firebase HTTPS 调用

在firebase函数中启用CORS

Firebase 函数中的 CORS