javascript Node.js 中的 URL 路由

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11428588/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 13:11:31  来源:igfitidea点击:

URL routing in Node.js

javascriptnode.js

提问by Henrik Andersson

Homework done:

做的功课:

The Node Beginner Book

节点初学者书

How do I get started with Node.js [closed]

我如何开始使用 Node.js [关闭]

Structuring handlers in Node

在 Node 中构建处理程序

Backstory: I wanted to try and write my own framework but I'm running into some troubles, most likely due to not understanding it fully.

背景故事:我想尝试编写自己的框架,但遇到了一些麻烦,很可能是因为没有完全理解它。

What I want to achieve is a syntax that looks like this:

我想要实现的是一个看起来像这样的语法:

var app = require('./app'); //this part is understood and it works in my current code.
app.get('/someUrl', function(){ //do stuff here });
app.post('/someOtherUrl', function(){ //do stuff here });

I know of the Express-framework that has this same syntax but reading their source code still eludes me.

我知道 Express 框架具有相同的语法,但我仍然无法阅读它们的源代码。

This might be a trivial task to achieve but I simply can't produce it, yet.

这可能是一项微不足道的任务,但我根本无法完成它。

Trying to require('./app');in a file deeper in the application produces a undefined object, so I'm guessing that a server is a singleton object.

尝试require('./app');在应用程序更深的文件中生成一个未定义的对象,所以我猜测服务器是一个单例对象。

So what have I tried?

那么我尝试了什么?

My current code looks like this, and somehow I feel like this is the way to go, but I can't apparently do it like this.

我当前的代码看起来像这样,不知何故我觉得这是要走的路,但我显然不能这样做。

I'm omitting all the require();statements to keep it more readable.

我省略了所有require();语句以使其更具可读性。

server.js:

服务器.js:

var app = module.exports = {
    preProcess: function onRequest(request, response){
        processor.preRequest(request); //this object adds methods on the request object
        var path = urllib.parse(request.url).pathname;
        router.route(urls, path, request, response);
    },
    createServer: function() {
        console.log("Server start up done.");
        return this.server = http.createServer(this.preProcess);
    }
};

exports.app = app;

At the time of writing I'm experimenting with extending this object with a get()method.

在撰写本文时,我正在尝试用一种get()方法扩展这个对象。

index.js:

索引.js:

var app = require('./server');
app.createServer().listen('1337');

the router.route()bit basically sends the request onward into the application and inside the router.js-file I do some magic and route the request onward to a function that maps (so far) to the /urlThatWasRequested

router.route()位基本上将请求向前发送到应用程序和 router.js 文件内部我做了一些魔术并将请求向前路由到映射(到目前为止)到 /urlThatWasRequested 的函数

This is the behavior I'd like to leave behind. I know this might be a pretty tall order but all my code is easily discardable and I'm not afraid of rewriting the entire codebase as this is my own project.

这是我想留下的行为。我知道这可能是一个很高的要求,但我所有的代码都很容易丢弃,而且我不害怕重写整个代码库,因为这是我自己的项目。

I hope this is sufficient in explaining my question otherwise, please say what I should add to make this a bit more clear.

我希望这足以解释我的问题,否则请说我应该添加什么以使这更清楚。

Thanks in advance!

提前致谢!

回答by freakish

I'm not exactly sure what your question is, but here's some thoughts:

我不确定你的问题是什么,但这里有一些想法:

1)You are creating a circular reference here:

1)您在此处创建循环引用:

var app = module.exports = {
    // some other code
}
exports.app = app;

You add appas a property of app. There's no need for the last line.

您添加app为 的属性app。不需要最后一行。

2)You need to hold handlers in app. You can try something like this:

2)您需要将处理程序保存在app. 你可以尝试这样的事情:

var app = module.exports = {
    handlers : [],
    route : function(url, fn) {
        this.handlers.push({ url: url, fn: fn });
    },
    preProcess: function onRequest(request, response){
        processor.preRequest(request);
        var path = urllib.parse(request.url).pathname;
        var l = this.handlers.length, handler;
        for (var i = 0; i < l; i++) {
            handler = this.handlers[i];
            if (handler.url == path)
                return handler.fn(request, response);
        }
        throw new Error('404 - route does not exist!');
    },
    // some other code
}

Note that you may alter this line: if (handler.url == path)in such a way that handler.urlis a regular expression and you test pathagainst it. Of course you may implement .getand .postvariants, but from my experience it is easier to check whether a request is GETor POSTinsidethe handler. Now you can use the code above like this:

请注意,您可以更改此行:if (handler.url == path)handler.url正则表达式的方式并path针对它进行测试。当然,您可以实现.get.post变体,但根据我的经验,检查请求是否在处理程序中GETPOST处理程序内部更容易。现在你可以像这样使用上面的代码:

app.route('/someUrl', function(req, res){ //do stuff here });

The other thing is that the code I've shown you only fires the firsthandler for a given URL it matches. You would probably want to make more complex routes involving many handlers (i.e. middlewares). The architecture would be a bit different in that case, for example:

另一件事是,我向您展示的代码仅针对它匹配的给定 URL触发第一个处理程序。您可能想要制作涉及许多处理程序(即中间件)的更复杂的路由。在这种情况下,架构会有所不同,例如:

preProcess: function onRequest(request, response){
    var self = this;
    processor.preRequest(request);
    var path = urllib.parse(request.url).pathname;
    var l = self.handlers.length,
        index = 0,
        handler;
    var call_handler = function() {
        var matched_handler;
        while (index < l) {
            handler = self.handlers[index];
            if (handler.url == path) {
                matched_handler = handler;
                break;
            }
            index++;
        }
        if (matched_handler)
             matched_handler.fn(request, response, function() {
                 // Use process.nextTick to make it a bit more scalable.
                 process.nextTick(call_handler);
             });
        else
             throw new Error('404: no route matching URL!');
     };
     call_handler();
},

Now inside your route you can use

现在在您的路线内您可以使用

app.route('/someUrl', function(req, res, next){ 
    //do stuff here
    next(); // <--- this will take you to the next handler
});

which will take you to another handler (or throw exception if there are no more handlers).

这将带您到另一个处理程序(如果没有更多处理程序,则抛出异常)。

3)About these words:

3)关于这些话:

Trying to require('./app'); in a file deeper in the application produces a undefined object, so I'm guessing that a server is a singleton object.

试图要求('./app'); 在应用程序更深处的文件中产生一个未定义的对象,所以我猜测服务器是一个单例对象。

It isn't true. requirealways returns the reference to the module object. If you see undefined, then you've messed up something else (altered the module itself?).

这不是真的。require始终返回对模块对象的引用。如果你看到undefined,那么你已经搞砸了其他东西(改变了模块本身?)。

Final note:I hope it helps a bit. Writing your own framework can be a difficult job, especially since there already are excelent frameworks like Express. Good luck though!

最后一点:我希望它有点帮助。编写自己的框架可能是一项艰巨的工作,尤其是因为已经有像 Express 这样的优秀框架。不过祝你好运!

EDIT

编辑

Implementing .getand .setmethods is actually not difficult. You just need to alter routefunction like this:

实现.get.set方法其实并不难。你只需要route像这样改变功能:

route : function(url, fn, type) {
    this.handlers.push({ url: url, fn: fn, type: type });
},
get : function(url, fn) {
    this.route(url, fn, 'GET');
},
post : function(url, fn) {
    this.route(url, fn, 'POST');
},

and then in routing algorithm you check whether typeproperty is defined. If it is not then use that route (undefinedtype means: always route). Otherwise additionally check if a request's method matches type. And you're done!

然后在路由算法中检查是否type定义了属性。如果不是,则使用该路由(undefined类型表示:始终路由)。否则另外检查请求的方法是否与类型匹配。大功告成!