Express 具有动态云功能的Firebase主机重写

Express 具有动态云功能的Firebase主机重写,express,firebase,google-cloud-functions,firebase-hosting,Express,Firebase,Google Cloud Functions,Firebase Hosting,我在firebase上有一个基于express.js的云函数应用程序,它的函数名为api。为了使用自定义域,我尝试使用Firebase托管重写将特定URL路由到函数。 我在这里遵循有关云功能和Firebase托管的官方文档,并尝试了许多组合,包括以下组合: "rewrites": [ { "source": "/api/**", "function": "api" } ] "rewrites": [ {

我在firebase上有一个基于express.js的云函数应用程序,它的函数名为
api
。为了使用自定义域,我尝试使用Firebase托管重写将特定URL路由到函数。 我在这里遵循有关云功能和Firebase托管的官方文档,并尝试了许多组合,包括以下组合:

"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应用程序具有以下我计划使用的获取路径:

'/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"
      }
]
在此之后,express应用程序的配置如下:

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
正如您所期望的那样

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

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

您可以在Express中使用单个Firebase主机重写规则和补充重写中间件

  • firebase.json
    文件中添加一个

    {
      "source": "/api/**",
      "function": "api"
    }
    
  • 包括一个中间件来重写请求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
    URL都起作用,另一个选项是检查URL是否来自hosted 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"
    }
    

    您能否编辑您的问题以简要显示express应用程序的云功能端和https功能导出?“我需要看看你是怎么安排的。”道格·史蒂文森说,先生,谢谢你的回答。我对基于JS的后端相当缺乏经验,因此我保留了firebase-examples中所示的大部分后端。您有没有找到解决方案?我一整天都在努力解决这个问题…@Pkmmte不完全是一个解决方案,一个变通办法。请查看编辑2了解详细信息。@AdityaAggarwal我采用了一种稍微不同的方法来解决这个问题,它基本上涉及使用
    main
    函数包装器。有关更多详细信息,请参见下面的答案。:)@Pkmmte我不明白为什么{“source”:“/api”,“function”:“api”}会在url@MuhammadHassan出于某种原因,Firebase内部就是这样工作的。我也不同意。它说“嘿,你想将
    /api
    重定向到你的
    api
    函数吗?好的,我将把它作为
    api+/api
    传递到那里。”。也许他们这样做是为了让你可以将
    /cat
    重定向到一个名为
    动物
    的函数,比如:
    动物/cat
    。这为我在app engine上托管api节省了很多时间和金钱。非常感谢!谢谢,这在部署时非常有效,但是在本地,我仍然需要使用重复的
    api
    点击端点才能工作。如何使其在本地和部署时都能工作?
    “source”:“/api/**”
    将捕获对
    /api/xyz
    /api/
    发出的请求,但不会捕获对
    /api
    发出的请求。要使其具有包容性,请使用
    “source”:“/api{,/**}”
    。资料来源:这让我困惑了很久。非常感谢。非常好,谢谢!我认为这是一个比公认的答案给出的第二台express服务器更好的解决方案,但两者都解决了问题,这确实有效。Firebase应该为其不直观的文档感到羞耻,并且需要制作这样的噱头,以使其在本地和生产中以同样的方式工作。谢谢我不知道您在哪里提供了参数。请求提供了对params req.query的访问。{{params}