Cải tiến ExpressJS Routing
nodejs
64
express
6
White

Dương Minh Khoa viết ngày 13/11/2015

Vấn đề

Thời gian gần đây, do yếu tố công việc nên mình phải tiếp xúc nhiều với NodeJS, đâm ra cũng có tí thích thú muốn tìm hiểu sâu hơn về em nó.
Hôm rồi mình có đá qua tìm hiểu về ExpressJS - một framework rất nổi của NodeJS - chắc ai cũng biết rồi nên mình không nói lại nữa.

Mình rất kết cấu trúc Middleware của nó, vừa dễ hiểu mà lại dễ mở rộng, tích hợp. Tuy nhiên có điểm trừ là Routing của nó theo mình nghĩ là không được tốt lắm.

Cụ thể Routing Basic của nó như sau:

// will match request to the root
app.get('/', function (req, res) {
  res.send('root');
});

// will match requests to /about
app.get('/about', function (req, res) {
  res.send('about');
});

Tại sao lại không tốt?
Thử tưởng tượng system của bạn có rất nhiều api ( với hệ thống game lớn vài trăm API là bình thường) thì bạn sẽ thấy những bất tiện sau:

  • File route ngày một phình to và khó quản lý
  • Mỗi lần thêm API mới bạn lại thêm một công sửa file route
  • Tính mở rộng và tích hợp không cao ….

Vậy tại sao ta không cải tiến cho dễ sử dụng hơn một chút :)

Ý tưởng

Ý tưởng cải tiến của mình bắt nguồn dựa trên kinh nghiệm code PHP, hồi còn dùng Yii framework. Chỉ cần format URL lúc đầu, controller,action code đúng format là chạy.
Tuy nhiên ở đây không đơn giản như vậy. Mình sẽ định nghĩa route ngay trên mỗi API bằng comment.

Tiến hành

Cùng nhìn lại cách khai báo route basic:

app.get('/action1', function (req, res) {
  res.end(‘Hello World!');
});

Trong đó có Method: GETURI: '/action1'
Chúng ta sẽ chuyển về TestController và khai báo như sau:

/**
 * [action1 description]
 * @param   req [description]
 * @param   res [description]
 * @uri /action1
 * @mtype Get 
 */
TestController.prototype.action1 = function(req,res){
    var content = '<h1> Hello World! </h1>';
    res.end(content);
}

Như vạy chúng ta đã định danh API ở comment là: @uri /action1@mtype GET
Việc còn lại là sao để cho express tự động hiểu được định danh đó mà không cần config gì thêm.

Để đọc được comment, ta sử dụng jsdoc-parse và tạo một file router setup như sau.

/**
 * Add function that has @uri and @mtype tag to express router
 * @param  {Object} app      Express Application
 * @param  {Object} comments list of comments in controller
 * @param  {Object} module   object controller
 */
function parseComentToRouter(app,comments,module){
        for (var i = 0; i < comments.length; i++) {
            var comment = comments[i];
            // if it is not function then return
            if (comment.kind && comment.kind == 'function') {
                var method,uri,j;
                for( j in comment.customTags){
                    var cTags = comment.customTags[j];
                    if(cTags.tag == 'uri'){
                        uri = cTags.value;
                    }
                    else if(cTags.tag == 'mtype'){
                        method = cTags.value;
                    }
                }

                var func    = comment.id && module.__proto__[comment.id.split('#')[1]];
                if (method && uri) {
                    // add action vào  express
                    app[method.toLowerCase()](uri, func);
                    console.log('[ URL Routing ] Add' + comment.id + 'method :' + method);
                } else {
                    console.log('[ URL Routing ] Failure' + comment.id);
                }
            }
        }

}

/**
 * Setup router by dir
 * ex: projectDir/controllers
 * @param  {Object} app Express application
 * @param  {String} dir Controller path
 */
Routes.prototype.setup = function(app,dir){
    // Get list các file trong dir
    fs.readdirSync(dir)
    .filter(function(v) {
        if (v === 'index.js') {
            return false;
        }
        // chỉ giữ lại các file có đuôi .js
        return v.slice(v.lastIndexOf('.')) === '.js';
    })
    // tiến hành parse comment trong các file
    .forEach(function(name) {
        var module;
        var path = dir + '/' + name;
        try {
            module = require(path);
        } catch(e) {
            console.log('[ URL Routing ] Failure can not load module');
            return;
        }

        var commentStream = parse({src: path});
        commentStream.on('data',function(data){

            var comments = JSON.parse(data.toString());
            // sử lý comment 
            parseComentToRouter(app,comments,module);
            // console.log(comments);
        });
    });

};

khi chạy ta sẽ được kết quả như sau:

expressjs-routing git:(master) node app.js
[ URL Routing ] Failuretest
[ URL Routing ] AddTestController#action1method :Get
[ URL Routing ] AddTestController#action2method :Get

Mình thừa nhận là hơi kém trong việc giải thích nên mọi người có thể down source về chạy thử tại đây:
https://github.com/sukoshi/expressjs-routing

Chúc các bạn cuối tuần vui vẻ

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Dương Minh Khoa

2 bài viết.
5 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
7 3
Vấn đề Ngày trước, dự án game 3D mình làm đang chuẩn bị release thì dính phốt. Vấn đề phát sinh khi game chạy trên Editor, PC, Tablet (Nexus 9 + ...
Dương Minh Khoa viết hơn 2 năm trước
7 3
Bài viết liên quan
White
8 1
Tiếp nối bài viết về giới thiệu về REST, trong bài này chúng ta sẽ tạo RESTfull webservice và thiết kế API cho nó.Khiếp nghe vẻ to tát nhưng thực t...
Quốc Cường viết 2 năm trước
8 1
White
24 7
Phát triển ứng dụng trên local tương đối đơn giản. Bạn chỉ việc cd vào thư mục , gõ câu lệnh node app.js , ứng dụng đã hoạt động. Mọi thứ sẽ trở n...
Quốc Cường viết 2 năm trước
24 7
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
2 bài viết.
5 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!