Как обойти ранее объявленный json body-parser в Nodebb? - PullRequest
0 голосов
/ 05 февраля 2020

Я пишу приватный плагин для nodebb (открытый форум программного обеспечения). В файле веб-сервера nodebb. js есть строка, которая, похоже, включает все входящие данные json.

app.use(bodyParser.json(jsonOpts));

Я пытаюсь преобразовать все входящие данные json для одной из моих конечных точек в необработанные данные. Однако проблема в том, что я не могу удалить или изменить строку выше.

Следующий код работает ТОЛЬКО если я временно удаляю строку выше.

    var rawBodySaver = function (req, res, buf, encoding) {
      if (buf && buf.length) {
        req.rawBody = buf.toString(encoding || 'utf8');
      }
    }

    app.use(bodyParser.json({ verify: rawBodySaver }));

Однако, как только я вставляю промежуточное ПО app.use(bodyParser.json(jsonOpts)); обратно в файл веб-сервера. js он останавливается за работой. Таким образом, похоже, что body-parser обрабатывает только первый анализатор, который соответствует входящему типу данных, а затем пропускает все остальные?

Как я могу обойти это? Я не смог найти никакой информации в их официальной документации.

Любая помощь с благодарностью.

** Обновление **

Проблема, которую я пытаюсь решить, заключается в правильной обработке входящего события чередования полос. В официальной документации полосы они предложили мне сделать следующее:

  // Match the raw body to content type application/json
  app.post('/webhook', bodyParser.raw({type: 'application/json'}), 
  (request, response) => {
    const sig = request.headers['stripe-signature'];

    let event;

    try {
      event = stripe.webhooks.constructEvent(request.body, sig, 
  endpointSecret);
    } catch (err) {
      return response.status(400).send(Webhook Error: 
  ${err.message});
    }

Оба метода, оригинал в верхней части этого поста и рекомендованный путь официальной полосы, правильно строят событие полосы, но только если я удаляю промежуточное программное обеспечение в веб-сервере. Теперь я понимаю, что у вас не может быть нескольких промежуточных программ для обработки одних и тех же входящих данных. У меня мало места для маневра, когда дело доходит до первого промежуточного программного обеспечения, за исключением возможности изменить аргумент (jsonOpts), который ему передается и приходит из файла. json. Я попытался добавить поле проверки, но я не мог понять, как добавить функцию в качестве значения. Я надеюсь, что это имеет смысл, и извините за то, что не указали, какую проблему я пытаюсь решить изначально.

Ответы [ 2 ]

0 голосов
/ 05 февраля 2020

Единственное решение, которое я могу найти без изменения кода NodeBB, - это вставить ваше промежуточное ПО в удобный хук (это будет позже, чем вы хотите), а затем взломать список слоев в маршрутизаторе приложения, чтобы переместить это промежуточное ПО ранее в список слоев приложения, чтобы получить его перед тем, что вы хотите видеть перед собой.

Это хак, поэтому, если Express изменит свою внутреннюю реализацию в будущем, это может сломаться. Но если бы они когда-либо изменили эту часть реализации, она, вероятно, была бы только в основной редакции (как в Express 4 ==> Express 5), и вы могли бы просто адаптировать код для соответствия новой схеме или, возможно, К тому времени NodeBB предоставит вам соответствующий хук.

Основная концепция c заключается в следующем:

  1. Получите маршрутизатор, который вам нужно изменить. Похоже, это app маршрутизатор, который вы хотите для NodeBB.
  2. Вставьте ваше промежуточное ПО / маршрут, как обычно, чтобы Express выполнил все обычные настройки для вашего промежуточного ПО / маршрута и вставил его во внутренний Список слоев в маршрутизаторе приложения.
  3. Затем перейдите в список, снимите его с конца списка (там, где он был только что добавлен) и вставьте его ранее в список.
  4. Рисунок где положить его ранее в списке. Вы, вероятно, не хотите его в самом начале списка, потому что это поместило бы его после некоторого полезного системного промежуточного программного обеспечения, которое делает такие вещи, как синтаксический анализ параметров запроса. Итак, код ищет первое промежуточное программное обеспечение, которое имеет имя, которое мы не распознаем, из встроенных имен, которые мы знаем, и вставляем его сразу после этого.

Вот код для вставляемой функции ваше промежуточное программное обеспечение.

function getAppRouter(app) {
    // History:
    //   Express 4.x throws when accessing app.router and the router is on app._router
    //      But, the router is lazy initialized with app.lazyrouter()
    //   Express 5.x again supports app.router 
    //      And, it handles the lazy construction of the router for you
    let router;
    try {
        router = app.router;      // Works for Express 5.x, Express 4.x will throw when accessing
    } catch(e) {}
    if (!router) {
        // Express 4.x
        if (typeof app.lazyrouter === "function") {
            // make sure router has been created
            app.lazyrouter();
        }
        router = app._router;
    }

    if (!router) {
        throw new Error("Couldn't find app router");
    }
    return router;
} 

// insert a method on the app router near the front of the list
function insertAppMethod(app, method, path, fn) {
    let router = getAppRouter(app);
    let stack = router.stack;

    // allow function to be called with no path
    // as insertAppMethod(app, metod, fn);
    if (typeof path === "function") {
        fn = path;
        path = null;
    }

    // add the handler to the end of the list
    if (path) {
        app[method](path, fn);
    } else {
        app[method](fn);
    }
    // now remove it from the stack
    let layerObj = stack.pop();
    // now insert it near the front of the stack, 
    // but after a couple pre-built middleware's installed by Express itself
    let skips = new Set(["query", "expressInit"]);
    for (let i = 0; i < stack.length; i++) {
        if (!skips.has(stack[i].name)) {
            // insert it here before this item
            stack.splice(i, 0, layerObj);
            break;
        }
    }
}

Затем вы могли бы использовать этот метод для вставки своего метода таким образом из любой ловушки NodeBB, которая предоставляет вам объект app во время запуска. Он создаст ваш /webhook обработчик маршрута и затем вставит его ранее в список слоев (до другого промежуточного программного обеспечения анализатора тела).

let rawMiddleware = bodyParser.raw({type: 'application/json'});

insertAppMethod(app, 'post', '/webhook', (request, response, next) => {
    rawMiddleware(request, response, (err) => {
        if (err) {
            next(err);
            return;
        }
        const sig = request.headers['stripe-signature'];

        let event;

        try {
          event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
          // you need to either call next() or send a response here
        } catch (err) {
          return response.status(400).send(`Webhook Error: ${err.message}`);
        }
    });
});
0 голосов
/ 05 февраля 2020

Промежуточное ПО bodyParser.json() выполняет следующие действия:

  1. Проверьте тип ответа на входящий запрос, чтобы узнать, является ли он application/json.
  2. Если это тот тип, то читать тело из входящего потока, чтобы получить все данные из потока.
  3. Когда у него есть все данные из потока, проанализируйте его как JSON и поместите результат в req.body, чтобы последующие обработчики запросов могли получить доступ к уже прочитанным и уже проанализированным данным.

Поскольку он считывает данные из потока, больше нет больше данных в потоке. Если он не сохраняет исходные данные где-либо (я не смотрел, чтобы убедиться, что это так), то исходные данные RAW исчезают - они уже считаны из потока. Вот почему вы не можете иметь несколько разных промежуточных программ, пытающихся обрабатывать одно и то же тело запроса. Независимо от того, кто идет сначала, читает данные из входящего потока, а затем исходные данные больше не находятся в потоке.

Чтобы помочь вам найти решение, нам нужно знать, какую конечную проблему вы действительно пытаетесь решить решать? У вас не будет двух промежуточных программ, которые ищут один и тот же тип контента и оба читают тело запроса. Вы могли бы заменить bodyParser.json(), который делает то же, что и сейчас, и делает что-то еще для ваших целей в том же промежуточном программном обеспечении, но не в отдельном промежуточном программном обеспечении.

...