Part 4: Tạo chức năng login và register sử dụng nodejs, expressjs, mongodb

Nội dung bài viết

Ở phần 4 này chúng ta sẽ bước sâu hơn trong kiến trúc của mongodb. Chức năng register và login chính là hai chức năng làm tiền đề cho những chức năng tiếp theo của bất cứ dự án nào. Chính vì vậy nó cũng quan trọng không kém những phần sau. Chúng ta cùng theo dõi tiếp phần 4 trong series "Build project using nodejs, expressjs, và mongodb"

Series build a project

Đề tài: Build một blog + chat sử dụng nodejs, expressjs, mongodb, firebase

Part 1 - Vì sao mỗi developer nên có một blog

Part 2 - Vì sao lại phát triển web application sử dụng nodejs và expressjs?

Part 3 - Setup project with nodejs, expressjs và connect mongodb

Part 4: Tạo chức năng login và register sử dụng nodejs, expressjs, mongodb

Yêu cầu tối thiểu cho phần 4

- Đọc lại phần 3

Sơ qua về Schema Mongoose

Mọi thứ ở Mongoose bắt đầu với một Schema. Mỗi một schema đều map với một Collection ở mongodb, ở đó những documents được định nghĩa nhiều kiểu như ví dụ dưới đây:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var blogSchema = new Schema({
    title:  String, // String is shorthand for {type: String}
    author: String,
    body:   String,
    comments: [{ body: String, date: Date }],
    date: { type: Date, default: Date.now },
    hidden: Boolean,
    meta: {
      votes: Number,
      favs:  Number
    }
});

Xem SchemaTypes hỗ trợ bao nhiêu kiểu nào:

  • String
  • Number
  • Date
  • Buffer
  • Boolean
  • Mixed
  • ObjectId
  • Array
  • Decimal128
  • Map

OK, xem vậy thôi chứ không đi sau nhiều vào nó. Mất thời gian, mấy ông dev tự tìm hiều về Schema nghe. CÒn bây giờ, tránh câu giờ chúng ta đi vào phần register. Mà nói qua ở đây cho anh em về bí mật đặt tên của các documents trong Schema. Mỗi công ty tôi làm qua thì có nhiều kiểu định nghĩa khác nhau và rất khoa học. Có lần tôi được làm việc với một ông Anh làm ở Chứng Khoán ở ĐỨC, thì cũng học qua một cách quy định về cách đặt biến trong database rât hay. Nhưng chắc tôi không thể nói ra được :(. Thay vì mỗi sinh viên được thấy thầy cô mình làm như thế này, ví dụ trong bảng users thì sẽ quy ước như sau

username, email, phone, address,... Kiểu vậy đó, nhưng hầu như trong môi trường công ty, họ không hề làm những điều quy định như vậy. Thay vào đó họ có một quy định rất hay, họ sẽ đưa ra những ký hiệu để cho team hiểu mà thôi. Tôi biết những trò đó, nhưng ỏ đây tôi không thể nói ra, dù rất muốn. Xin lỗi! Thôi lại dài dòng nữa rồi, tiếp đê....

Tạo Schema Mongodb cho 2 collections Users và Login

Trước tiên thì quy trình sẽ không như các bạn sinh viên hay làm là cho chung vào một collections. Nhưng cách tôi làm có thể khác, là login collection chỉ save về thống tin đăng nhập thôi. Còn User Collection mới là nơi chứa thông tin chi tiết. Chưa hiểu bây giờ, nhưng hết project này thì chúng ta sẽ hiểu hơn những điều tôi nói.

Notes: Những bạn nào chưa biết folder nào thì vui lòng xem lại part 3, và có thể git clone sourcecode về và có thể làm tiếp đến đây.

Tạo file /app/models/login_model.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const login = new Schema({
    email: { type: String, default: ''},
    password: { type: String, default: ''},

    loginAt: { type: Date, default: Date.now},
    logoutAt: { type: Date, default: Date.now},
    action: { type: String, default: 'System'},
    
}, { collection: 'login' })

// login.index({ first: 1, last: -1 }) Nơi đánh index
module.exports = mongoose.model('login', login)

Tham số:

email và password thì quá dễ rồi, nhưng 3 tham số còn lại thì để lưu log cho sau này, thống kê xem users login hệ thống bao nhiêu thời gian. etc...

Notes: default sẽ có hiệu lực khi bạn không khai báo trong hàm tạo.

Tiếp file /app/models/users_model.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const users = new Schema({
    email: { type: String, default: ''},
    phone: { type: String, default: ''},
    fullname: { type: String, default: ''},
    roles: {type: Array, default: []},
    status: { type: String, default: 'noactive'},
    type_regis: { type: String, default: 'WE'},

    deleteAt: { type: Date, default: Date.now},
    createAt: { type: Date, default: Date.now},
    updateAt: { type: Date, default: Date.now},
    action: { type: String, default: 'System'},
    
}, { collection: 'users' })

users.index({ email: 1}) //Nơi đánh index
module.exports = mongoose.model('users', users)

Các dev có thể thấy giữa login và users có một key chung đó là email. Và chúng là sẽ set email là khoá chính của bảng User thay vì id mặc định của mongoose. Chính vì vậy, tôi sẽ tạo index cho email này. Các tham số, không khác nhau là mấy, chỉ là save thông tin thôi.

Code Controller

Trong file routes/index.js ta có như sau:

'use strict';
const index_Controller = require('../controllers/')
const middleware = require('../utils/middleware')

module.exports = function(app) {
    app.route('/api/test').get(index_Controller.test); 
    app.route('/').get(index_Controller.home);
    app.route('/login').get(index_Controller.login).post(index_Controller.login)
    app.route('/profile').get([middleware.checkLogin, index_Controller.profile]).post([middleware.checkLogin, index_Controller.profile])
}

Khai báo routes trong express route, có một điều lưu ý ở đây đó là file middleware, tôi sẽ nói sau bài này.

Trong folder controller tôi sẽ tạo thêm hai controller bao gồm login_controller.js và users_controller.js. Mỗi controller này sẽ có chức năng khác nhau, nên tách ra, với lại làm như vậy các bạn sẽ quen cách làm việc trong một nhóm, chứ không riêng như các bạn vừa làm.

Nhiệm vụ file index_controller thì sẽ làm việc chung, nhưng tôi khuyên các bạn cũng nên tách ra sau này, vì sẽ lớn hơn và có nhiều user tham gia làm hơn.

Trong file index_controller ta sẽ khai báo như sau cho chức năng login khi User submit từ http://localhost:8080/login

login: async (req, res) => {
        if(req.session.user){
            return res.redirect('http://localhost:8080/profile?step=session')
        }
        if(req.method === 'GET'){
            return res.render('login.ejs')
        }

        if(req.method === 'POST'){
            //su dung Util check missing key neu can
            let q = req.body;
            let check = _UTIL.checkMissingKey(q, ['email', 'password'])
            console.log('check>>>>', check)
            if(q.TYPE === 'RE'){
                console.log('user >>> register');
                let rsLogin = await _LOGIN_CON.function_register(q)
                if(rsLogin){
                    let rsUser = await _USERS_CON.function_register_users(q);
                    if(rsUser){
                        return res.redirect('http://localhost:8080/login?step=login')
                    }
                }

                return res.redirect('http://localhost:8080/login?step=error_login')
            }

            //xu ly cho login

            if(q.TYPE === 'LO'){
                let objLogin = await _LOGIN_CON.function_login(q)
                if(objLogin){
                    //storage session here
                    req.session.user = objLogin;
                    return res.redirect('http://localhost:8080/profile?step=login_success')
                }
                return res.redirect('http://localhost:8080/login?step=error')
            }
        }
       
    },

Ở đó chia ra 2 case đó là GET POST. GET là hiện thị giao diện, POST là khi submit data về xử lý.

NOtes: name="TYPE" ở đây tôi chia ra 2 trường hợp để biết khi nào login và regis.

Sau khi register thành công thì giao diện sẽ chuyển sang login cho người dùng sử dụng. Sau khi login thành công thì:

if(objLogin){
    //storage session here
    req.session.user = objLogin;
    return res.redirect('http://localhost:8080/profile?step=login_success')
}

Đây chính là thành công khi login chúng ta, sẽ tạo sesion cho req.session.user = objLogin, và đồng thời redirect sang page profile để cho User có thể update lại thông tin cần thiết. NOtes: Giữ session trong nodejs bạn xem ở bài viết này "NodeJS Session sử dụng Express Sessionanonystick."

Code trong file index_Controller.js

profile: async (req, res) => {
    return res.render('profile.ejs', {user: req.session.user})
},

Check missing key in Objects

let check = _UTIL.checkMissingKey(q, ['email', 'password'])

Hàm này được sử dụng, khi bạn muốn check xem thiếu params nào, trước khi đưa vào insert data cho mongoose.

Và điều cuối cùng nói về bài viết này đó là checkLogin middleware. Bình thường nếu như chúng ta không triển khai một middleware để làm việc đó thì điều đó đồng nghĩa với việc bạn phải check có session hay không từng phần. Như trong đoạn code này,

app.route('/profile').get([middleware.checkLogin, index_Controller.profile]).post([middleware.checkLogin, index_Controller.profile])

Có nghĩa là, muốn đi vào http://localhost:8080/profile thì phải check qua function checkLogin xem đã có session hay chưa, nếu chưa có thì sẽ redirect qua trang login. Và ngược lại nếu có thì sẽ cho đi vào page profile. Hãy xem nội dung của funciton checkLogin là gì?

File utils/middleware.js

'use strict';
var self = module.exports = {
    checkLogin: (req, res, next) => {

        console.log('middleware req.session.user>>', req.session.user);
        if(req.session.user){
            return next();
        }
        return res.redirect('/login')
        
    }
}

Kết quả phần 5

Tôi đã đưa file login.ejsprofile.ejs lên github, các bạn bây giờ có thể lên đó để clone về tiếp.

Tạo chức năng update thông tin User, password và phần quyền.

Tới đây, tôi xin phép được dừng tại đây, ở phần 5 chúng ta sẽ khám phá tiếp những chức năng tiếp theo đó là "update thông tin User, password và phần quyền". Hy vọng ở phần 4 này, sẽ giúp bạn có một góc nhìn sâu hơn. Nếu bạn chưa hiểu, xin vui lòng xem lại những bài trước dưới dây.

Sorry các bạn, hôm nay lu bu quá, nên chưa up code lên github. Sourcecode

Có thể bạn đã bị miss