【问题标题】:How to create extendable controllers in ExpressJS如何在 ExpressJS 中创建可扩展的控制器
【发布时间】:2013-09-06 23:17:19
【问题描述】:

我是 Node 新手,我正在尝试使用 ExpressJS (http://expressjs.com/) 创建一个 MVC 应用程序。我使用的文件夹结构与 GitHub 上的 MVC 示例 (https://github.com/visionmedia/express/tree/master/examples/mvc) 相同。

在我的控制器文件夹中,我有 2 个文件夹:主文件夹和系统文件夹。我想要的是在 /controllers/system/index.js 中定义一个基本控制器,并让 /controllers/main/index.js 继承系统控制器。每个其他模块都将扩展系统并覆盖一些功能以生成页面。

在另一个教程中,我找到了以下代码。

Base.js

var _ = require("underscore");
module.exports = {
    name: "base",
    extend: function(child) {
        return _.extend({}, this, child);
    },
    run: function(req, res, next) {

    }
};

Home.js

var BaseController = require("./Base"),
    View = require("../views/Base"),
    model = new (require("../models/ContentModel"));

module.exports = BaseController.extend({ 
    name: "Home",
    content: null,
    run: function(req, res, next) {
        model.setDB(req.db);
        var self = this;
        this.getContent(function() {
            var v = new View(res, 'home');
            v.render(self.content);
        })
    },
    getContent: function(callback) {
        var self = this;
        this.content = {};
        model.getlist(function(err, records) {
            if(records.length > 0) {
                self.content.bannerTitle = records[0].title;
                self.content.bannerText = records[0].text;
            }
            model.getlist(function(err, records) {
                var blogArticles = '';
                if(records.length > 0) {
                    var to = records.length < 5 ? records.length : 4;
                    for(var i=0; i<to; i++) {
                        var record = records[i];
                        blogArticles += '\
                            <div class="item">\
                                <img src="' + record.picture + '" alt="" />\
                                <a href="/blog/' + record.ID + '">' + record.title + '</a>\
                            </div>\
                        ';
                    }
                }
                self.content.blogArticles = blogArticles;
                callback();
            }, { type: 'blog' });
        }, { type: 'home' });
    }
});

如果没有 Underscore 的扩展功能,你如何做到这一点? Express 是否有内置方法来扩展模块?我正在使用 doT.js 进行模板化,因此我不想为一个函数包含另一个大型库。

谢谢!

编辑:必须进行一些更改才能使基本代码从 dc5 工作。系统可以工作,但对于 main 我在继承调用时收到此错误:

util.js:555
  ctor.prototype = Object.create(superCtor.prototype, {
                          ^
TypeError: Object prototype may only be an Object or null
    at Function.create (native)
    at Object.exports.inherits (util.js:555:27)

/controllers/system/index.js:

var util = require( 'util' ),
    system = { };

system.index = function( req, res, next ) {
    res.render( 'main' );
};

module.exports = system;

/controllers/main/index.js:

var util = require( 'util' ),
    system = require( './../system/index' ),
    main = { };

util.inherits( main, system );

module.exports = main;

【问题讨论】:

  • 在这种情况下,system 和 main 都是简单的对象。 util.inherits 的两个参数都需要是构造函数。
  • 我不确定我是否遵循。我试过把 main.constructor、main.prototype 和 main.prototype.constructor 都放进去,但它们都不起作用?
  • 系统被初始化为 {},它是一个 Object 类型的空对象。我很快就会在我的答案中添加一个使用相同名称和目录结构的更新。

标签: javascript node.js express dot.js


【解决方案1】:

您可以将util.inherits 用于您所描述的内容。它不是_.extend() 的替代品,但上面的示例只需要直接继承。

用法:util.inherits(constructor, superConstructor)

base.js:

var util = require('util');

function Base() {…}

Base.prototype.run = function(req,res,next) {…}
module.exports = Base;

home.js:

var util = require('util');
var BaseController = require("./base");

function Home() {…}

util.inherits(home, BaseController);

Home.prototype.run = function(req,res,next) {…}

或者标准的JS继承模式:

base.js:

function Base() {…}

Base.prototype.run = function(req,res,next) {…}
module.exports = Base;

home.js:

var BaseController = require("./base");

function Home() {
    BaseController.apply(this, arguments);
}

Home.prototype = Object.create(BaseController.prototype);
Home.prototype.run = function(req,res,next) {…}

根据问题中的更新示例,模块应如下所示:

系统:

var util = require('util');

function System() {
    this.txt  = "hello from ";
    this.name = "System"; 
}

System.prototype.sayHello = function() {
    console.log(this.txt + this.name);
}

System.prototype.run = function(req,res,next) {
    this.sayHello();

    console.log('calling ==> overrideMe');
    this.overrideMe();

    console.log('calling ==> noOverride');
    this.noOverride();
    next ? next() : "";
}

System.prototype.overrideMe = function() {
    console.log('System.overrideMe');
}

System.prototype.noOverride = function() {
    console.log('System.noOverride');
}


module.exports = System;

主要:

var util = require('util');
var System = require("../system/");

function Main() {
    // Makes sure the System constructor is run
    System.apply(this, arguments);
    this.name = "Main"; 
}

util.inherits(Main, System);

Main.prototype.run = function(req,res,next) {
    this.sayHello();

    console.log('calling ==> overrideMe');
    this.overrideMe();

    console.log('calling ==> noOverride');
    this.noOverride();

    next ? next() : "";
}

Main.prototype.overrideMe = function() {
    console.log('Main.overrideMe');
}

module.exports = Main;

根目录下的app.js - 一个简化的快递服务器:

var System  = require('./controllers/system');
var Main    = require('./controllers/main');
var express = require('express');
var path    = require('path');
var http    = require('http');
var app     = express();
var server;

var system = new System();
var main   = new Main();

app.configure(function() {
    "use strict";
    app.set('port', process.env.PORT || 3000, '127.0.0.1');
    app.use(system.run.bind(system));
    app.use(main.run.bind(main));
    app.use(app.router);
    //app.use(express.compress());
    app.use(express.static(path.join(__dirname, '..', 'public'), {redirect: false}));
    app.use(express.static(path.join("/Users/dave/personal/playground/yo"), {redirect: false}));
});


server = http.createServer(app);
server.listen(app.get('port'), function() {
    "use strict";
    console.log("Express server listening on port " + app.get('port'));
});

从浏览器访问:http://localhost:3000/

控制台输出:

Express server listening on port 3000
hello from System
calling ==> overrideMe
System.overrideMe
calling ==> noOverride
System.noOverride
hello from Main
calling ==> overrideMe
Main.overrideMe
calling ==> noOverride
System.noOverride

重要

因为这个例子是使用SystemMain的实例来提供路由,所以run方法在传递时必须绑定到实例来表达。请参阅:MDN .bind documentation 了解更多信息。

【讨论】:

  • 感谢您的回复,我尝试了代码并收到此错误:util.js:555 ctor.prototype = Object.create(superCtor.prototype, { ^ TypeError: Object prototype may only是一个对象或空
  • 感谢您的详细回复,不幸的是我无法让它工作。如果在系统中我执行 module.exports = system (function not instanced) 那么在 main 我可以执行 module.exports = new main() 我没有收到任何错误,但导出的方法不适用于 MVC 示例中的动态路由我链接了。如果在系统中我创建了一个实例(module.exports = new system()),该模块将按预期工作,但是当我尝试在 main 中扩展它时,我得到相同的 Object.create 错误。我似乎无法扩展工作并且仍然让两个控制器都将方法传递给 module.exports?有什么想法吗?
  • @Jonathan 我已将示例更新为基本的快递应用程序,以展示如何使用run 方法作为快递中的路线。最重要的是使用 bind() 在 express 调用 run 路由时提供适当的上下文。
【解决方案2】:

为避免复杂的配置和解决方法,请使用ZinkyJS,它提供了一种更好的方法来使模块继承成为可能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-26
    • 1970-01-01
    • 1970-01-01
    • 2018-11-04
    • 1970-01-01
    相关资源
    最近更新 更多