【问题标题】:Firebase Hosting with dynamic cloud functions rewrites具有动态云功能的 Firebase 托管重写
【发布时间】:2017-12-11 02:17:13
【问题描述】:

我在 firebase 上有一个基于 express.js 的云函数应用程序,其函数名为 api。要使用自定义域,我正在尝试使用 Firebase 托管重写将特定 URL 路由到函数。 我正在关注有关云功能和 Firebase 托管的官方文档,https://firebase.google.com/docs/hosting/functions,并尝试了许多组合,包括以下内容:

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

"rewrites": [
      {
        "source": "/api/:path1/:dat1/dat",
        "function": "api/:path1/:dat1/dat"
      }
    ]
"rewrites": [
      {
        "source": "/api/path1/dat1/dat",
        "function": "api"
      }
    ]
"rewrites": [
      {
        "source": "/api/*/*/*",
        "function": "api"
      }
    ]

遗憾的是,它似乎不适用于任何可能的组合。 我的 express 应用程序有以下我计划使用的 GET 路径:

'/api/users/:userId/:userData'
'/api/users/:userId/:userData/json'
'/api/users/:userId/'

和其他类似的。 :userId 和 :userData 是我请求中的参数,因为它适用于 express.js

所需的功能在

中按预期工作
https://my-firebase-app.cloudfunctions.net

但它们不适合

https://my-app.firebaseapp.com

请告诉我这些应该如何工作以及我做错了什么。

编辑: 这是我的云函数导出的示例

const functions = require('firebase-functions');
const express = require('express');
const app = express();

app.get('/users/:userId/:userData/json', (req, res) => {
    // Do App stuff here
}
// A couple more app.get in the same format

exports.api = functions.https.onRequest(app);

编辑 2: 在@DougStevenson 的建议下,我尝试了以下配置

我在 firebase.json 中尝试了以下内容,

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

但我遇到了同样的问题,该函数从未被调用。 我读到了重写是最后的选择,如果主机中存在文件,它将不会转到指定的功能。(我尝试寻找提到这个 ws 的 SO 帖子,但我找不到它)所以我从主机的公共目录中删除了 404.html 和 index.html 文件,因为无论如何我都不需要它们。但问题仍然存在。

编辑 2: 好的,经过大量试验和错误后,我只需要按以下格式对路径进行硬编码:

rewrites : [
      {
        "source": "/users/**/**/json",
        "function": "api"
      },
      {
        "source": "/api/users/**/**/json",
        "function": "api"
      }
]

在此之后,快速应用程序配置如下:

app.get('/users/:userId/:userData/json', Foo)

我仍然希望有人能提出更好的方法来完成此任务,而不是在托管重写中手动放入每个所需的 Uri。

【问题讨论】:

  • 您能否编辑您的问题以简要显示您的快速应用程序的云功能方面和 https 功能导出?我需要看看你是如何设置的。
  • @DougStevenson 先生,谢谢您的回复。我对基于 JS 的后端相当缺乏经验,所以我保留了大部分内容,如 firebase-examples 所示。
  • 您有没有找到解决方案?我也一直在努力解决这个问题......
  • @Pkmmte 不完全是一个解决方案,一个解决方法。详情请查看编辑 2。
  • @AdityaAggarwal 我采取了一种稍微不同的方法来解决这个问题,它基本上涉及使用main 函数包装器。有关更多详细信息,请参阅下面的答案。 :)

标签: express firebase google-cloud-functions firebase-hosting


【解决方案1】:

似乎主要的问题是:

{
    "source": "/api",
    "function": "api"
}

实际上是重写为https://my-firebase-app.cloudfunctions.net/api/api 而不是https://my-firebase-app.cloudfunctions.net/api,就像您期望的那样。注意api 是如何重复的。

我对此的解决方案是创建一个 main 函数来托管所有其他顶级函数:

const functions = require('firebase-functions');
const express = require('express');
const app = express();

app.get('/users/:userId/:userData/json', (req, res) => {
    // Do App stuff here
}
// A couple more app.get in the same format

// Create "main" function to host all other top-level functions
const main = express();
main.use('/api', app);

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

您现在可以使用此main 函数来委托给所有其他函数,而不会破坏您的 URL 结构:

{
    "source": "/api/**", // "**" ensures we include paths such as "/api/users/:userId"
    "function": "main"
}

瞧!您现在可以像您期望的那样通过https://my-app.firebaseapp.com/api/users/:userId/:userData 访问所有api 函数调用。

调用此端点,现在重写为https://my-firebase-app.cloudfunctions.net/main/api,这在技术上是正确的。然后,您可以添加更多顶级函数,只需将它们添加到您的 main 函数中即可:

const hooks = express();
main.use('/hooks/, hooks);

【讨论】:

  • @Pkmmte 我不明白为什么 { "source": "/api", "function": "api" } 会导致 /api/api 在 url 中重复
  • @MuhammadHassan 这就是 Firebase 出于某种原因在内部工作的方式。我也不同意。它说“嘿,你想将/api 重定向到你的api 函数吗?好的,我会把它作为api + /api 传递给那里”。也许他们这样做是为了让您可以将 /cat 之类的内容重定向到一个名为 animals 的函数,如下所示:animals/cat
  • 这为我节省了大量时间和金钱来尝试在应用引擎上托管我的 api。非常感谢!
  • 谢谢,这在部署时效果很好,但是在本地我仍然需要使用重复的api 来访问端点才能工作。我怎样才能让它在本地和部署时都工作?
  • "source": "/api/**" 将捕获对/api/xyz/api/ 的请求,但不会捕获对/api 的请求。要使其具有包容性,请使用"source": "/api{,/**}"。来源:firebase.google.com/docs/hosting/full-config#rewrites
【解决方案2】:

您可以使用单个 Firebase 托管重写规则和 Express 中的补充重写中间件。

  1. 在您的 firebase.json 文件中添加 rewrite

    {
      "source": "/api/**",
      "function": "api"
    }
    
  2. 包含一个 app.use() 中间件来重写请求 url。

    const functions = require('firebase-functions');
    const express = require('express');
    
    const API_PREFIX = 'api';
    const app = express();
    
    // Rewrite Firebase hosting requests: /api/:path => /:path
    app.use((req, res, next) => {
        if (req.url.indexOf(`/${API_PREFIX}/`) === 0) {
            req.url = req.url.substring(API_PREFIX.length + 1);
        }
        next();
    });
    
    app.get('/users/:userId/:userData/json', (req, res) => {
        // Do App stuff here
    });
    
    exports[API_PREFIX] = functions.https.onRequest(app);
    

【讨论】:

  • 这让我困惑了很久。谢谢!
  • 效果很好,谢谢!我认为这是一个比接受的答案给出的第二个快速服务器更好的解决方案,但都解决了问题
  • 这确实有效。 Firebase 应该为他们不直观的文档感到羞耻,并且需要制作这样的噱头以使其在本地和生产中以相同的方式工作。谢谢!
【解决方案3】:

如果您希望cloudFunctionhosted URLS 都工作,另一种选择是检查 URL 是否来自托管 URL。

您可以在需要参数的任何时候使用此函数。

export const splitParams = (req: any): string[] => {
  let params = req.params[0];
  const vals: string[] = [];

  // If params starts with a '/' remove it
  params = params.startsWith('/')
    ? params.substr(1, params.length - 1)
    : params;
  params = params.split('/');

  // For hosted URLs the parameters need to be shifted
  if (
    req.headers['x-forwarded-host'] === 'myURL' 
  ) {
    params.shift();
  }

  for (const param of params) {
    if (param !== '') {
      vals.push(param);
    }
  }

  return vals;
};

【讨论】:

    【解决方案4】:

    使用查询参数直接将数据传递到云函数即可,无需自定义快速中间件。

    网址如下所示:

    http://localhost:5000/api/?userId=yop&chart=blue
    

    云功能:

    export const api = functions.https.onRequest(async (req: any, res: any) => {
    
     const userId = req.query.userId
     const chartId = req.query.chart 
    
    })
    

    重写保持在最低限度,因为 url 仍然是带有查询参数的 '/api'

    "rewrites": [ {
      "source": "/api",
      "function": "api"
    }
    

    【讨论】:

    • 我不知道你提供的参数在哪里
    • 请求提供对参数 req.query 的访问。{{params}}
    【解决方案5】:

    这对我有用。

    firebase.json 文件中:

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

    确保您的函数托管在 us-central1 区域中

    【讨论】:

    • 我试过了,效果很好。我有一个用于 api 子域的辅助网站。 api.mycustomdomain ` "rewrites": [ { "source": "**", "function": "/" } ] ` api.mycustomdomain/function-name 运行就像 https://us-central1-project-name.cloudfunctions.net/function-name
    猜你喜欢
    • 2018-04-11
    • 2020-06-13
    • 2020-03-15
    • 2021-03-28
    • 2020-12-27
    • 2020-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多