Server là nơi tiếp nhận những yêu cầu và phản hồi lại những yêu cầu đó cho khách hàng. Nơi để tiếp nhận đó chính ra route. Route tạo ra những nơi tiếp nhận cho từng yêu cầu khác nhau. Cùng mình tìm hiểu route trong Express ngay bây giờ !!!

Giới thiệu

Router là phương thức khai báo để đáp lại requrest từ client, nó có thể hiểu tương tự như một Request mapping. Nói đơn giản hơn là định nghĩa URL cho trang web mà người dùng sẽ tương tác. Một số định nghĩa router thường dùng :

Định nghĩa cho trường hợp người dùng nhập URL cơ bản : http://localhost:3000/

router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

Sử dụng nodemon để không phải restart server bằng tay mỗi khi thay đổi code

Install nodemon bằng npm

npm install --save-dev nodemon

Update code trong file package.json

"scripts": {
  "start": "nodemon my_file.js"
},

sau đó chạy command:

npm run start

Cấu trúc định tuyến cơ bản

Trong express.js định tuyến có cấu trúc như sau

app.METHOD(Path, Handler...)

trong đó

  • app: là 1 instance của express
  • METHOD: là một HTTP method
  • Path: là một đường dẫn trên máy chủ
  • Handler : là một function sẽ thực thi khi một route được trùng khớp

Giải thích

  • Handler : có thể có một hoặc nhiều function
  • Một route được xác định bằng Path (đường dẫn) và request method.
  • Khái niệm route trùng khớp là chỉ việc một người dùng thực hiện request với Path (đường dẫn) và Method trùng khớp với định nghĩa trong route.

Route methods

Express hỗ trợ rất nhiều loại HTTP methods khác nhau, bao gồm :

get, post, put, head, delete, options, trace, copy, lock, mkcol, move, purge, unlock, report, mkactivity, checkout, merge, m-search, notify, subscribe, unsubscribe, patch and searc

Trong đó sử dụng nhiều nhất là: Get, Post, Put, Head, Delete và Options

khai báo đơn giản như sau:

// GET method route
app.get('/', function (req, res) {
  res.send('GET request to the homepage')
})

// POST method route
app.post('/', function (req, res) {
  res.send('POST request to the homepage')
})

Có một method đặc biệt là app.all(). dùng để load middleware tạo một đường dẫn cho tất cả các http request methods. Ví dụ: trình xử lý sau được thực thi cho các yêu cầu đến tuyến “/ secret” cho dù sử dụng GET, POST, PUT, DELETE hoặc bất kỳ phương thức yêu cầu HTTP nào khác

app.all('/secret', function (req, res, next) {
  console.log('Accessing the secret section ...')
  next() // pass control to the next handler
})

Route paths

Đây là phần trọng tâm của bài học hôm nay, route path có thể là một chuỗi thông thường (String) hoặc là một chuỗi có ký hiệu biểu thức chính quy (string patterns) hoặc là một biểu thức chính quy (regular expressions). Ví dụ

  • /users/nghuuquyen : là một đường dẫn thông thường
app.get('/users/nghuuquyen', function(req, res) {
//Do something.
});
  • /users/user/* : là một đường dẫn với ký hiệu * biểu diễn cho một chuỗi bất kỳ. Ký hiệu * ở đây nói lên là route trên khớp với mọi đường dẫn bắt đầu với /users/.
app.get('/users/*', function(req, res) {
// Do something.
});

/^[a-zA-Z0-9]{5,15}$/ : là một đường dẫn có dạng biểu thức chính quy (regular expression). Route này sẽ khớp với mọi đường dẫn kết thúc với đuôi là cool. Ví dụ YOUARESOcool sẽ khớp, nhưng YOUTOOCool thì sẽ không vì khác ký tự ‘C’.

app.get(/.*cool$/, function(req, res) {
// Do something.
});

Và một số đường dẫn theo khuôn mẫu (String pattern) các bạn có thể tham khảo thêm. Biết đâu sẽ áp dụng vào dự án của chúng ta. Chớ mình là mình ít dụng tới lắm =]]].

  • Sử dụng ?. Có hay không ký tự liền trước ? đều được. cho phép acd và abcd.
app.get('/ab?cd', function (req, res) {
res.send('ab?cd')
})
  • Sử dụng +. Thêm bao nhiêu kí tự liền trước nó vẫn phù hợp. Cho phép abcdabbbcd ….
app.get('/ab+cd', function (req, res) {
res.send('ab+cd')
}) 
  • Sử dụng *. Tại vị trí (*) thay thế bằng bất cứ ký tự, hay chuỗi hoặc số đều được: abcd, abxcd, abRANDOMcd, ab123cd …
app.get('/ab*cd', function (req, res) {
res.send('ab*cd')
})
  • Sử dụng (...). Kí tự bên trong ngoặc có hay không đều được. /abe, /abcde
app.get('/ab(cd)?e', function (req, res) {
res.send('ab(cd)?e')
})
  • Sử dụng ../. Cho phép những đường dẫn có url thỏa những ký tự đã được khai báo, phía sau nó là gì cũng được. path nào bắt đầu bằng kí tự a đều thỏa. a, ab, arandom, …
app.get(/a/, function (req, res) {
res.send('/a/')
})

Route parameters

Route parameters là những vị trí trên url được đánh dấu bằng cách đặt tên, mục đích để lấy giá trị tương ứng ra sử dụng. Tất cả cá giá trị đối số sẽ được đặt vào đối tượng 8 trong thuộc tính params. Với tên thuộc tính trùng khớp với từ khóa được xác định trên URL.

// Route path: /users/
// Request URL: http://localhost:3000/users/34/books/8989
// req.params: { "userId": "34", "bookId": "8989" }
app.route('/users/:userId/books/:bookId', function(req, res) {
  res.send(req.params);
});

Lưu ý, tên của tham số phải thỏa ([A-Za-z0-9_]).

Một số trường hợp khác khi làm việc vói - và ..

Route path: /flights/:from-:to
Request URL: http://localhost:3000/flights/LAX-SFO
req.params: { "from": "LAX", "to": "SFO" }

Route path: /plantae/:genus.:species
Request URL: http://localhost:3000/plantae/Prunus.persica
req.params: { "genus": "Prunus", "species": "persica" }

Nâng cao hơn một xíu, ta sẽ áp dụng những biểu thức chính quy cho param này.
:userId(^[A-Za-z0-9.-_]{8,30}+$): Lúc này nếu bạn nhập vào một đường dẫn có đối số :userId ngắn hơn 8 hoặc dài hơn 30 ký tự thì route trên sẽ không nhận, hoặc chứa ký tự khác dấu ., _ - , thì route cũng không nhận. Từ đó hạn chế được việc query một username bị sai quy tắc và lại an toàn tránh tấn công SQL injection.

Route handlers

Đơn giản là một hoặc nhiều function sẽ được gọi khi một route trùng khớp để đáp ứng một yêu cầu nào đó. Lưu ý các handler sẽ được gọi đúng theo thứ tự truyền vào. Ví dụ

app.get(‘/user’, [a, b]);

thì a sẽ gọi trước b. Chú ý là để b được gọi thì trong a phải gọi hàm next(). Ví dụ

Nâng cao hơn một xíu, ta sẽ áp dụng những biểu thức chính quy cho param này.
:userId(^[A-Za-z0-9.-_]{8,30}+$): Lúc này nếu bạn nhập vào một đường dẫn có đối số :userId ngắn hơn 8 hoặc dài hơn 30 ký tự thì route trên sẽ không nhận, hoặc chứa ký tự khác dấu ., _ - , thì route cũng không nhận. Từ đó hạn chế được việc query một username bị sai quy tắc và lại an toàn tránh tấn công SQL injection.

Route handlers
Đơn giản là một hoặc nhiều function sẽ được gọi khi một route trùng khớp để đáp ứng một yêu cầu nào đó. Lưu ý các handler sẽ được gọi đúng theo thứ tự truyền vào. Ví dụ

app.get(‘/user’, [a, b]);

thì a sẽ gọi trước b. Chú ý là để b được gọi thì trong a phải gọi hàm next(). Ví dụ

Nếu truyền nhiều tham số vào các hàm METHOD, thì tham số thứ 2 là middleware, các tham số sau mới là handlers.
Và tất nhiên, tham số thứ 2 chúng ta vẫn có thể truyền vào 1 array.

var cb0 = function (req, res, next) {
  console.log('CB0')
  next()
}

var cb1 = function (req, res, next) {
  console.log('CB1')
  next()
}

app.get('/example/d', [cb0, cb1], function (req, res, next) {
  console.log('the response will be sent by the next function ...')
  next()
}, function (req, res) {
  res.send('Hello from D!')
})

Túm cái váy lại: những function nào gọi next() đều là middleware đó bạn, nếu nó next() thì mới cho phép request đi tiếp, nếu không sẽ đứng lại tại đố và response gì đó về phía user và kết thúc.

app.route()

Đây là một cách định nghĩa route rõ ràng hơn, mới được hỗ trợ trong các phiên bản mới. Ví dụ thay vì bạn định nghĩa như sau.

function doGet(req, res, next) {
  // something ...
}

function doPost(req, res, next) {
  // something ...
}

function doPut(req, res, next) {
  // something ...
}

app.get('/users/:user', doGet);
app.post('/users/:user', doPost);
app.put('/users/:user', doPut);

thì áp dụng app.route chúng ta có thể làm ngắn gọn như sau

function doGet(req, res, next) {
// something ...
}

function doPost(req, res, next) {
// something ...
}

function doPut(req, res, next) {
// something ...
}

app.route('/users/:user')
    .get(doGet)
    .post(doPost)
    .put(doPut);

Express.Router

Đây là một điều thú vị được cập nhận trong phiên bản mới của Express, giúp chúng ta có thể module hóa công việc định tuyến thành cái module nhỏ có thể sử dụng lại.

Ví dụ mình định nghĩa một module route có khả năng trả về thời gian hiện tại khi truy cập /time như dưới đây . Mình để trong file là time.server.routes.js

var express = require('express')
var router = express.Router()

router.get('/time', function (req, res) {
  res.send(Date.now());
})

module.exports = router

Và ta sẽ sử dụng router này trong file app.js

var timeRoutes = require('./routes/time.server.routes');

// ...

app.use('/helpers', timeRoutes);

Như trên ta có thể truy cập để lấy thời gian ở route là GET /helper/time. Rất tiện đúng không nào.

Kết luận

Qua bài học này, mình đã giới thiệu cho tất cả các bạn các kỹ thuật cơ bản và cả nâng cao của Routing.

Các bạn cần nắm kỹ các kiến thức như sau:

  • Hiểu rõ về Routing và các định nghĩa của nó.
  • Biết được Express cung cấp cho ta những tính năng gì để hỗ trợ cho việc Routing.
  • Nắm được ý tưởng về mô hình MVC.
  • Thấy được kiến trúc thực tế của một trang website xây dựng theo mô hình MVC.

Nguồn tham khảo:

Rất mong được sự ủng hộ của mọi người để mình có động lực ra những bài viết tiếp theo.
{\__/}
( ~.~ )
/ > ♥️ I LOVE YOU 3000

JUST DO IT!