具有动态云功能的 Firebase 托管重写

问题描述 投票:0回答:6

我在 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"
  }
}

但我遇到了同样的问题,该函数从未被调用。 我读到了重写如何成为最后的手段选项,如果托管中存在文件,它将不会转到指定的功能。(我尝试寻找提到此内容的SO帖子,但我找不到它)所以我从托管的公共目录中删除了 404.html 和 index.html 文件,因为我根本不需要它们。但问题仍然存在。

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

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

在此之后,express 应用程序的配置如下:

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

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

express firebase google-cloud-functions firebase-hosting
6个回答
46
投票

主要问题似乎是:

{
    "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"
}

瞧!您现在可以像您期望的那样通过

api
访问所有
https://my-app.firebaseapp.com/api/users/:userId/:userData
函数调用。

调用此端点,现在重写为

https://my-firebase-app.cloudfunctions.net/main/api
,这在技术上是正确的。然后,如果您愿意,您可以通过简单地将更多顶级函数添加到您的
main
函数中来添加它们:

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

30
投票

您可以使用单个 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);
    

0
投票

如果您希望

cloudFunction
hosted
URL 都起作用,另一个选项是检查 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;
};

0
投票

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

网址如下所示:

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

0
投票

对于使用 Firebase 设置本地和生产环境的任何人,尤其是在连接自定义域时,以下是截至 2024 年 10 月的正确设置。

我正在使用 Firebase 托管和功能,对于本地开发,我使用 Firebase 模拟器运行所有内容。

按键设置

在 firebase.json 中,您可以像这样配置模拟器:

{
  "emulators": {
    "functions": {
      "port": 5001
    },
    "hosting": {
      "port": 5003,
      "rewrites": [
        {
          "source": "/api/users/**",
          "function": "users"
        },
        {
          "source": "**",
          "destination": "/index.html"
        }
      ]
    }
  }
}

重要提示: 本地托管:确保您的本地托管环境 (env.development) 指向托管模拟器(端口 5003)而不是功能端口。

示例

SOME_ENV_SERVER=http://localhost:5003/api

避免将请求直接指向函数模拟器,如果这样做,您将需要维护 2 种不同的路由,一种用于开发,一种用于生产:

SOME_ENV_SERVER=http://localhost:5001/myapp-1234/us-central1

此设置可确保 API 调用通过本地托管模拟器正确路由,从而实现本地和生产中的无缝开发。


-2
投票

这对我有用。

firebase.json
文件中:

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

确保您的函数托管在

us-central1
区域

© www.soinside.com 2019 - 2024. All rights reserved.