NodeJS Firebase可调用函数中间件

bq8i3lrv  于 9个月前  发布在  Node.js
关注(0)|答案(2)|浏览(132)

通过Firebase HTTP functions,我们可以安装express并使用中间件。中间件对于在函数执行之前检查前置条件非常有用。例如,我们可以在中间件中检查身份验证,授权等,这样就不需要在每个端点定义中重复。
开发人员如何用Firebase callable functions实现同样的事情?当你有大量的可调用函数时,你如何提取出通常在链式中间件中的所有功能?

eh57zj3b

eh57zj3b1#

似乎没有现成的可调用函数的中间件框架,所以受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
  }),
);


在上面的例子中使用了两个中间件,它们被链接起来,所以assertAppCheck首先被调用,然后是assertAuthenticated,只有在它们都通过之后,你的hander才会被调用。
这两个中间件是:
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
    },
  ),
);

2024-01-05第二代更新

下面是适用于gen2函数的代码:

import { CallableRequest } from 'firebase-functions/v2/https';

export type Handler<T = any, Return = any> = (
  request: CallableRequest<T>,
) => Promise<Return>;

export type Middleware<T = any, Return = any> = (
  request: CallableRequest<T>,
  next: (request: CallableRequest<T>) => Promise<Return>,
) => Promise<Return>;

export const withMiddlewares =
  <T = any, Return = any>(
    middlewares: Middleware<T, Return>[],
    handler: Handler<T, Return>,
  ) =>
  (request: CallableRequest<T>): Promise<Return> => {
    const chainMiddlewares = ([
      firstMiddleware,
      ...restOfMiddlewares
    ]: Middleware<T, Return>[]): Handler<T, Return> => {
      if (firstMiddleware)
        return (request: CallableRequest<T>): Promise<Return> =>
          firstMiddleware(
            request,
            chainMiddlewares(restOfMiddlewares),
          );
      else return handler;
    };

    return chainMiddlewares(middlewares)(request);
  };

8oomwypt

8oomwypt2#

Firebase可调用函数的中间件不可用。可调用函数强制端点使用特定路径、特定类型的输入(JSON通过POST)和某种类型的输出(也是JSON). Express不会真正帮助您,你可以在documentation中读到所有可调用协议的细节。你可以看到callables抽象了所有的细节,请求和响应,通常在使用Express时使用。
根据这个community answer
对可调用函数的HTTP请求实际上并不“来自”URL。它们来自互联网上的任何地方。它可以是一个网站,Android或iOS应用程序,或者只是知道protocol调用函数的人。如果你正在构建一个Web应用程序,并且你想传递沿着发出请求的页面的URL,你必须把这些数据添加到客户端传递给函数的对象中,这些数据显示在数据中。
因此,除非你通过在可调用函数的数据中发送URL来解决这个问题,否则它将无法工作。即使你这样做了,它也违背了可调用函数的原则,所以我建议你使用HTTP Functions。

相关问题