我想将 remix 应用程序部署到 Firebase Cloud Functions,使用静态资产托管。该函数定义为:
const functions = require("firebase-functions");
const express = require("express");
const compression = require("compression");
const morgan = require("morgan");
const { createRequestHandler } = require("@remix-run/express");
const app = express();
app.use(compression());
app.use(morgan("tiny"));
app.all("*", createRequestHandler({ build: require("./build") }));
const api = functions.https.onRequest(app);
module.exports = {
api,
};
如here所述,请求主体在请求传递到
api
函数之前由firebase解析。但该应用程序期待“未触及”的请求。这会导致 remix 中的请求正文为空。
有没有办法禁用或撤消请求正文解析?我在中间件中尝试过
req.body = req.rawBody;
,但没有成功。
无法禁用 Cloud Functions 中对请求进行的预处理。 Firebase 和 Google Cloud 变体都是如此。
如果您想完全控制处理请求的代码,请考虑使用不同的产品,例如 Cloud Run,它使您能够控制您构建和部署的 docker 映像中所有代码的行为。
有一种方法可以使 Firebase 函数与 Remix 表单数据一起使用。
您可以通过将表单数据添加到上下文来传递表单数据:
在 function/index.js 中,您需要获取帖子负载并将其添加到上下文中
app.all("*", createRequestHandler({build: require("./build"),
getLoadContext(req) {
return {body: req.body || null};
}}));
然后在您的路线中执行操作,您可以阅读上下文:
export const action = async ({ request, context }) => {
console.log(context)
return {context};
}
扩展 Jkarttunen 的答案,您应该像这样定义 Firebase 处理程序函数:
// Initialize the Firebase admin SDK app. This must only be done once, so we must do it outside our handler.
const { initializeApp } = require('firebase-admin/app');
initializeApp();
const remixExpress = require("@remix-run/express");
const functions = require("firebase-functions");
exports.api = functions.https.onRequest((req, res, next) => {
return remixExpress.createRequestHandler({
build: require("./build"),
getLoadContext(req) {
return { body: req.body || null };
}
})(req, res, next);
});
然后,如果您想让代码与 Remix 中通常处理代码的方式保持一致(使用 FormData 对象),则可以在应用程序的某个位置定义一个“getFormData”函数,如下所示:
import { FormData } from "@remix-run/web-form-data";
export const getFormData = async (request: Request, context: any) => {
const requestBody = context?.body;
// Was the request passed to Remix with a 'body'? If so, then the request was already processed by 'bodyParser',
// which means that we need to get the data from the 'body' (the context gets set in our Firebase handler function)
if (requestBody) {
const form = new FormData();
for (const key of Object.keys(requestBody)) {
form.append(key, requestBody[key]);
}
return form;
}
return request.formData();
};
然后你的
action
函数可以如下所示:
export const action: ActionFunction = async ({ request, context }) => {
const form = await getFormData(request, context);
// Get the data the same way you normally would. For example:
const firstName = form.get("firstName");
}
虽然没有办法(据我所知)禁用该解析,但在某些情况下,您可以通过创建包含 firebase 函数层最初读取的相同(原始)内容的新流来获得类似的效果。
示例:(对于将输入请求发送到外部服务的代理的情况)
import request from 'request';
import express from 'express';
import intoStream from "into-stream";
[...]
app.use(function(req, res) {
const extServiceDomain = 'https://api.example.com';
const newReq = request[req.method.toLowerCase()](extServiceDomain + req.url);
// the firebase-function layer (which we don't control), reads through the body stream (leaving it empty), so we need to recreate it
const reqBodyStream_recreated = intoStream(req["rawBody"]); // the firebase-function layer at least gives us the raw Buffer with the read bytes
// TypeScript doesn't see these properties, but they're necessary
reqBodyStream_recreated["method"] = req.method;
reqBodyStream_recreated["headers"] = req.headers;
reqBodyStream_recreated.pipe(newReq).pipe(res);
});
链接: