Firebase 可调用函数的中间件
Middleware for Firebase Callable Functions
使用Firebase HTTP functions,我们可以安装express和使用中间件。中间件对于在函数执行之前检查先决条件很有用(除其他外)。例如,我们可以在中间件中检查身份验证、授权等,这样就不需要在每个端点定义中重复它们。
开发人员如何通过 Firebase callable functions 实现同样的目标?当您有大量可调用函数时,您如何提取通常位于链式中间件中的所有功能?
无法使用 Firebase 可调用函数的中间件。可调用函数强制您的端点使用特定路径、特定类型的输入(JSON 通过 POST)和特定类型的输出(也 JSON)。考虑到可调用对象的工作方式的限制,Express 不会真正帮助您。您可以在 documentation 中阅读所有可调用协议的详细信息。您可以看到可调用对象抽象出了请求和响应的所有细节,这些细节在使用 Express 时通常会处理。
根据这个 ,
对可调用函数的 HTTP 请求并非真正“来自”URL。他们来自互联网上的任何地方。它可以是网站、Android 或 iOS 应用程序,或者只是知道 protocol 来调用函数的人。
如果您正在构建 Web 应用程序并且想要传递发出请求的页面的 URL,则必须将该数据添加到客户端传递给函数的对象中,该函数显示在数据.
因此,除非您通过在可调用函数的数据中发送 URL 来解决这个问题,否则它不会起作用。即使你这样做,它也违反了可调用函数的原则,所以我建议你为此目的使用 HTTP 函数。
似乎没有现成的用于可调用函数的中间件框架,因此受到 this 的启发,我推出了自己的。 NPM 上有一些通用的链式中间件框架,但我需要的中间件非常简单,我自己动手比配置一个库来使用可调用函数更容易。
可选:如果您使用的是 TypeScript,则中间件的类型声明:
export type Middleware = (
data: any,
context: functions.https.CallableContext,
next: (
data: any,
context: functions.https.CallableContext,
) => Promise<any>,
) => Promise<any>;
中间件框架如下:
export const withMiddlewares = (
middlewares: Middleware[],
handler: Handler,
) => (data: any, context: functions.https.CallableContext) => {
const chainMiddlewares = ([
firstMiddleware,
...restOfMiddlewares
]: Middleware[]) => {
if (firstMiddleware)
return (
data: any,
context: functions.https.CallableContext,
): Promise<any> => {
try {
return firstMiddleware(
data,
context,
chainMiddlewares(restOfMiddlewares),
);
} catch (error) {
return Promise.reject(error);
}
};
return handler;
};
return chainMiddlewares(middlewares)(data, context);
};
要使用它,您可以将 withMiddlewares
附加到任何可调用函数。例如:
export const myCallableFunction = functions.https.onCall(
withMiddlewares([assertAppCheck, assertAuthenticated], async (data, context) => {
// Your callable function handler
}),
);
上面的例子中用到了2个中间件。它们被链接在一起,因此首先调用 assertAppCheck
,然后调用 assertAuthenticated
,只有在它们都通过后才会调用你的处理程序。
2 个中间件是:
assertAppCheck:
/**
* Ensures request passes App Check
*/
const assertAppCheck: Middleware = (data, context, next) => {
if (context.app === undefined)
throw new HttpsError('failed-precondition', 'Failed App Check.');
return next(data, context);
};
export default assertAppCheck;
assertAuthenticated:
/**
* Ensures user is authenticated
*/
const assertAuthenticated: Middleware = (data, context, next) => {
if (!context.auth?.uid)
throw new HttpsError('unauthenticated', 'Unauthorized.');
return next(data, context);
};
export default assertAuthenticated;
作为奖励,这里有一个验证中间件,它使用 Joi 来确保在调用处理程序之前验证数据:
const validateData: (schema: Joi.ObjectSchema<any>) => Middleware = (
schema: Joi.ObjectSchema<any>,
) => {
return (data, context, next) => {
const validation = schema.validate(data);
if (validation.error)
throw new HttpsError(
'invalid-argument',
validation.error.message,
);
return next(data, context);
};
};
export default validateData;
像这样使用验证中间件:
export const myCallableFunction = functions.https.onCall(
withMiddlewares(
[
assertAuthenticated,
validateData(
Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required(),
}),
),
],
async (data, context) => {
// Your handler
},
),
);
使用Firebase HTTP functions,我们可以安装express和使用中间件。中间件对于在函数执行之前检查先决条件很有用(除其他外)。例如,我们可以在中间件中检查身份验证、授权等,这样就不需要在每个端点定义中重复它们。
开发人员如何通过 Firebase callable functions 实现同样的目标?当您有大量可调用函数时,您如何提取通常位于链式中间件中的所有功能?
无法使用 Firebase 可调用函数的中间件。可调用函数强制您的端点使用特定路径、特定类型的输入(JSON 通过 POST)和特定类型的输出(也 JSON)。考虑到可调用对象的工作方式的限制,Express 不会真正帮助您。您可以在 documentation 中阅读所有可调用协议的详细信息。您可以看到可调用对象抽象出了请求和响应的所有细节,这些细节在使用 Express 时通常会处理。
根据这个
对可调用函数的 HTTP 请求并非真正“来自”URL。他们来自互联网上的任何地方。它可以是网站、Android 或 iOS 应用程序,或者只是知道 protocol 来调用函数的人。 如果您正在构建 Web 应用程序并且想要传递发出请求的页面的 URL,则必须将该数据添加到客户端传递给函数的对象中,该函数显示在数据.
因此,除非您通过在可调用函数的数据中发送 URL 来解决这个问题,否则它不会起作用。即使你这样做,它也违反了可调用函数的原则,所以我建议你为此目的使用 HTTP 函数。
似乎没有现成的用于可调用函数的中间件框架,因此受到 this 的启发,我推出了自己的。 NPM 上有一些通用的链式中间件框架,但我需要的中间件非常简单,我自己动手比配置一个库来使用可调用函数更容易。
可选:如果您使用的是 TypeScript,则中间件的类型声明:
export type Middleware = (
data: any,
context: functions.https.CallableContext,
next: (
data: any,
context: functions.https.CallableContext,
) => Promise<any>,
) => Promise<any>;
中间件框架如下:
export const withMiddlewares = (
middlewares: Middleware[],
handler: Handler,
) => (data: any, context: functions.https.CallableContext) => {
const chainMiddlewares = ([
firstMiddleware,
...restOfMiddlewares
]: Middleware[]) => {
if (firstMiddleware)
return (
data: any,
context: functions.https.CallableContext,
): Promise<any> => {
try {
return firstMiddleware(
data,
context,
chainMiddlewares(restOfMiddlewares),
);
} catch (error) {
return Promise.reject(error);
}
};
return handler;
};
return chainMiddlewares(middlewares)(data, context);
};
要使用它,您可以将 withMiddlewares
附加到任何可调用函数。例如:
export const myCallableFunction = functions.https.onCall(
withMiddlewares([assertAppCheck, assertAuthenticated], async (data, context) => {
// Your callable function handler
}),
);
上面的例子中用到了2个中间件。它们被链接在一起,因此首先调用 assertAppCheck
,然后调用 assertAuthenticated
,只有在它们都通过后才会调用你的处理程序。
2 个中间件是:
assertAppCheck:
/**
* Ensures request passes App Check
*/
const assertAppCheck: Middleware = (data, context, next) => {
if (context.app === undefined)
throw new HttpsError('failed-precondition', 'Failed App Check.');
return next(data, context);
};
export default assertAppCheck;
assertAuthenticated:
/**
* Ensures user is authenticated
*/
const assertAuthenticated: Middleware = (data, context, next) => {
if (!context.auth?.uid)
throw new HttpsError('unauthenticated', 'Unauthorized.');
return next(data, context);
};
export default assertAuthenticated;
作为奖励,这里有一个验证中间件,它使用 Joi 来确保在调用处理程序之前验证数据:
const validateData: (schema: Joi.ObjectSchema<any>) => Middleware = (
schema: Joi.ObjectSchema<any>,
) => {
return (data, context, next) => {
const validation = schema.validate(data);
if (validation.error)
throw new HttpsError(
'invalid-argument',
validation.error.message,
);
return next(data, context);
};
};
export default validateData;
像这样使用验证中间件:
export const myCallableFunction = functions.https.onCall(
withMiddlewares(
[
assertAuthenticated,
validateData(
Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required(),
}),
),
],
async (data, context) => {
// Your handler
},
),
);