在后续调用的处理程序之前发送 HAPI 回复

HAPI reply is being sent prior to the handler on subsequent calls

构建我的第一个 HAPI api 后端,并 运行 变成一些奇怪的东西。当我第一次访问端点时(GET /api/item/{name},我可以在我的控制台中看到处理函数正在 运行(mongo 查询),然后回复被发送(有一个响应插件记录这些)。太好了。如果我用不同的参数再次访问端点,我会看到第一个调用的响应立即发出,然后处理程序函数被访问。而且,事实上,客户端得到的响应与第一次调用时相同。

我什至不确定什么对 post 最有帮助。

这里是大部分入口点js(缺少环境和winston的配置):

const Hapi = require('hapi');
server = new Hapi.Server();

var mongo_connect = 'mongodb://' + options.mongo_creds + options.mongo_host + ':' + options.mongo_port + '/' + options.mongo_db;
const dbOpts = {
    url: mongo_connect,
    settings: {
        poolSize: options.mongo_pool
    },
    decorate: true
};

server.connection({ port: options.server_port });

var routes = require('./routes');

if (options.env === "dev") {
    server.on('response', function (request) {
        winston.log('verbose', `[launch_api] ${request.info.remoteAddress}: ${request.method.toUpperCase()} ${request.url.path} --> ${request.response.statusCode}`);
    });
}

server.register({
    register: require('hapi-mongodb'),
    options: dbOpts
}, function(err) {
    if (err) {
        winston.log('error', "[launch_api] Unable to register db pool");
        throw err;
    }

    server.route(routes);

    server.start(function(err) {

        if (err) {
            throw err;
        }
        winston.log('info', `[launch_api] Server running at: ${server.info.port}`);
    });
});

路线在路线文件夹中的 index.js 中被拉在一起,但那里的每个文件看起来像:

'use strict';
var controller = require('../controllers/item-controller')

// Routes for Item

module.exports = [
    {
        method: 'POST',
        path: '/api/item',
        config: controller.create
    },
    {
        method: 'GET',
        path: '/api/items',
        config: controller.fetchAll
    },
    {
        method: 'GET',
        path: '/api/item/{name}',
        config: controller.find
    }
];

所有控制器看起来都像这样(为了简洁起见,只显示查找功能,因为这已经很长了)

const Boom = require('boom');
const Joi = require('joi');
const Item = require('../models/item');

module.exports = {
    find: {
        handler: function(request, reply) {
            Item.initFromName(request.params.name).then( function(newItem) {
                if (newItem == null) {
                    reply(Boom.notFound());
                }
                else {
                    reply(newItem);
                }
            }, function(err) {
                reply(Boom.badImplementation());
            });
        }
    }
}

最后,模型倾向于遵循这个,呃,模型(同样,删除所有原型扩展,只保留这条路线中的一个 class 函数)

const deferred = require('deferred')()
const winston = require('winston');

const collection_name = "items";


var Item = function() {
    this.name = "";
    this.description = "";
};

// private

function fillFromDB(obj, record) {
    obj._id = record._id;
    obj.name = record.name;
    obj.description = record.description;
}

// Constructor
module.exports.init = function() {
    return new Item();
};

module.exports.initFromName = function(name) {
    var item = new Item();
    const db = server.mongo.db;
    db.collection(collection_name).findOne({name: name}).then( function(opResult) {
            winston.log("debug","Item.loadFromName opResult is: " + opResult);
        if (opResult != undefined) {
            winston.log("debug","Item.loadFromName opResult json is: " + JSON.stringify(opResult));
            fillFromDB(item, opResult);
            deferred.resolve(item);
        } 
        else {
            winston.log("debug","Resolving with null" );
            deferred.resolve();
        }
    }, function(err) {
        winston.log("error", "Item.loadFromName mongo error: " + err);
        deferred.reject();
    });
    return deferred.promise;
};

因此,如果我使用 curl 访问我的端点,并且集合中的名称 ,我会得到预期的 404。如果我然后用这样的名字点击,我仍然会得到 404。

本次输入:

$ curl -X GET http://192.168.99.100:3000/api/item/not_here
{"statusCode":404,"error":"Not Found"}
$ curl -X GET http://192.168.99.100:3000/api/item/here
{"statusCode":404,"error":"Not Found"}

生成此日志:

debug: Item.loadFromName opResult is: null
debug: Resolving with null
verbose: [launch_api] 192.168.99.1: GET /api/item/not_here --> 404
verbose: [launch_api] 192.168.99.1: GET /api/item/here --> 404
debug: Item.loadFromName opResult is: [object Object]
debug: Item.loadFromName opResult json is: {"_id":"58b622908ea4d1cee2f46462","name":"here","description":"this item is here"}

请注意,相反的方向也适用。如果我停止并启动节点,然后使用存在的名称访问端点,则所有后续调用都将返回相同的对象。我只是不知道这个缓存发生在哪里。

问题是延迟对象的全局实例化。不知道我在哪里看到的,但这是个坏主意。

在模型中,将第一行更改为要求

const deferred = require('deferred');

然后,在函数中创建您的延迟对象。

module.exports.initFromName = function(name) {
    const defer = deferred();
    var item = new Item();
    const db = server.mongo.db;
    db.collection(collection_name).findOne({name: name}).then( function(opResult) {
            winston.log("debug","Item.loadFromName opResult is: " + opResult);
        if (opResult != undefined) {
            winston.log("debug","Item.loadFromName opResult json is: " + JSON.stringify(opResult));
            fillFromDB(item, opResult);
            defer.resolve(item);
        } 
        else {
            winston.log("debug","Resolving with null" );
            defer.resolve();
        }
    }, function(err) {
        winston.log("error", "Item.loadFromName mongo error: " + err);
        defer.reject();
    });
    return defer.promise;
};