具有动态云功能重写的 Firebase 托管
Firebase Hosting with dynamic cloud functions rewrites
我在 firebase 上有一个基于 express.js 的云函数应用程序,其函数名为 api
。要使用自定义域,我正在尝试使用 Firebase Hosting 重写将特定 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 post 但我不能找到它)所以我从主机的 public 目录中删除了 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。
似乎主要的问题是:
{
"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);
您可以将单个 Firebase 托管重写规则与 Express 中的补充重写中间件结合使用。
在您的 firebase.json
文件中添加一个 rewrite。
{
"source": "/api/**",
"function": "api"
}
包含一个 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);
另一种选择,如果您希望 cloudFunction
和 hosted
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;
};
使用查询参数将数据直接传递到云函数就可以了,没有自定义 express 中间件。
下面是 url 的样子:
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"
}
这对我有用。
在 firebase.json
文件中:
"rewrites": [{
"source": "/api/**",
"function": "/"
}]
确保您的函数托管在 us-central1
区域
我在 firebase 上有一个基于 express.js 的云函数应用程序,其函数名为 api
。要使用自定义域,我正在尝试使用 Firebase Hosting 重写将特定 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 post 但我不能找到它)所以我从主机的 public 目录中删除了 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。
似乎主要的问题是:
{
"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);
您可以将单个 Firebase 托管重写规则与 Express 中的补充重写中间件结合使用。
在您的
firebase.json
文件中添加一个 rewrite。{ "source": "/api/**", "function": "api" }
包含一个 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);
另一种选择,如果您希望 cloudFunction
和 hosted
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;
};
使用查询参数将数据直接传递到云函数就可以了,没有自定义 express 中间件。
下面是 url 的样子:
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"
}
这对我有用。
在 firebase.json
文件中:
"rewrites": [{
"source": "/api/**",
"function": "/"
}]
确保您的函数托管在 us-central1
区域