使用动态云功能的Firebase托管重写

时间:2017-07-06 22:08:41

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

我在firebase上的一个名为api的函数中有一个基于express.js的云函数应用程序。要使用自定义域名,我尝试使用Firebase托管重写将特定网址路由到该功能。 我在此处关注云功能和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"
      }
    ]

可悲的是,它似乎并不适用于任何可能的组合。 我的快递应用程序具有以下我计划使用的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。

4 个答案:

答案 0 :(得分:22)

似乎主要问题是:

{
    "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函数委派给所有其他函数,而不会破坏您的网址结构:

{
    "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);

答案 1 :(得分:5)

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

  1. firebase.json文件中添加rewrite

    {
      "source": "/api/**",
      "function": "api"
    }
    
  2. 包含app.use()中间件以重写请求网址。

    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);
    

答案 2 :(得分:0)

如果您想同时使用cloudFunctionhosted这两个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;
};

答案 3 :(得分: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"
}