Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (6.83 MB, 72 trang )
<span class="text_page_counter">Trang 1</span><div class="page_container" data-page="1">
<b>TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT TP. HỒ CHÍ MINHKHOA ĐÀO TẠO CHẤT LƯỢNG CAO</b>
<b>MỤC LỤC</b>
<b>FOLDER 3: LET’S START WITH SHOPIT BACKEND...4</b>
1. Basic Project...4
2. Installing_Setting Up Nodemon...7
3. Creating First Route...9
4. Setting Up Postman Environment...9
5. Connecting To MongoDB...10
<b>FOLDER 4: ADDING PRODUCTS RESOURCE...13</b>
1. Creating Product Model...13
2. Admin - Create _ Save Product...13
3. Creating Data Seeder...16
4. Display all products...17
5. Get Single Product...18
6. Admin _ Update Product...20
7. Admin _ Delete Product...22
<b>FOLDER 5: BACKEND ERROR HANDLING...25</b>
1. Creating Error Handler Class...25
2. Creating Errors Middleware...25
3. Production Vs Development Errors...27
4. Catching Async Errors...28
5. Handling Unhandled Promise Rejection...29
6. Handling Uncaught Exceptions...31
7. Validation _ Mongose ID Error...32
<b>FOLDER 6. ADDING FILTER, PAGINATION, SEARCH...34</b>
1. Search Products by Keyword...34
2. Filter Products...36
3. Adding Pagination...37
<b>FOLDER 7. BACKEND AUTHENTICATION & AUTHORIZATION...38</b>
1. Creating User Model...38
2. Encrypting Password while Registration...39
3. Generate JSON Web Token...41
4. Login User & Assign Token...42
</div><span class="text_page_counter">Trang 3</span><div class="page_container" data-page="3">5. Sending JWT Token in Cookie...45
6. Protect Routes from Unauthorized Users...47
7. Logout User...50
8. Authorize User Roles and Permissions...51
9. Adding User in Products...53
10. Generate Forgot Password token...55
11. Send Password Recovery Email...56
12. Reset New Password...59
13. Handle Wrong JWT Token & Expire JWT Error...64
<b>FOLDER 8. USER ROUTES...66</b>
1. Show User Profile...66
2. Change Update Password...66
3. Update User Profile...68
4. Admin - Get All Users & Specific User...69
5. Admin - Update & Delete User...71
</div><span class="text_page_counter">Trang 4</span><div class="page_container" data-page="4"><b>FOLDER 3: LET’S START WITH SHOPIT BACKEND1. Basic Project</b>
<b>Tóm tắt: Phần chính trong video này, tác giả muốn đề cập về phần set up mơi </b>
trường để code với các gói package bắt buộc ở các folder 1 và 2 như Express, Mongul hay Mongo’s. Cũng như bắt tay vô xây dựng những bước cơ bản ban đầu và chạy thử sever trong video này.
Đầu tiên, ta tạo ra 2 folder lần lượt là <b>backend</b> và <b>frontend.</b>
Ta mở phần <b>terminal</b> trong visual code ra.
Tại đây, trên root folder ta gõ lệnh “<b>npm init</b>” và nhấn enter để khởi tạo ứng dụng cho phần backend.
</div><span class="text_page_counter">Trang 5</span><div class="page_container" data-page="5">Sau khi nhấn enter ở bước trên, lập tức các thông tin liên quan về project này sẽ hiện lên và ta cần phải điền đầy đủ các thông tin như: package name, version, description, entry point, test command, git respository,…
Sau khi hồn tất các thơng tin thì visual code sẽ tự động sinh ra 1 file json như bên dưới đây chứa các nội dung mà ta vừa điền.
Ta trở xuống thanh terminal để install một vài package trước khi đến với các bước tiếp theo sau nhé!
Trước tiên, ta install <b>express</b> và <b>dotenv </b>để đọc được các biến trong môi trường. Và cài đặt cả Mongoose cho phần MongoDB.
<b>Ta dùng lệnh: npm I express dotenv mongoose và nhấn enter.</b>
</div><span class="text_page_counter">Trang 6</span><div class="page_container" data-page="6">Sau khi cài đặt xong thì visual code sẽ sinh ra file package-lock.json như bên dưới:
Trong folder backend, ta tạo một folder tên là <b>config</b> (cấu hình), và trong folder
<b>config</b>, ta tạo 1 file là <b>config.env</b>
Trong file này ta định nghĩa các cho các biến với PORT bằng 4000 và NODE_ENV = DEVELOPMENT.
Trong folder backend, ta tạo lần lượt 2 file là app.js và sever.js app.js
</div><span class="text_page_counter">Trang 7</span><div class="page_container" data-page="7"><b>Sau đó, ta gõ lên terminal bằng lệnh node backend/sever.js để start thử sever. </b>
Được kết quả như bên dưới đây.
<b>2. Installing_Setting Up Nodemon</b>
<b>Tóm tắt: Trong video này, tác giả chủ yếu tiếp tục hướng dẫn cài đặt và set up </b>
phần Nodemon trước khi sang các bước kế tiếp.
Đầu tiên,ta gõ lên thanh terminal lệnh: <b>npm i nodemon –save-dev </b>để cài đặt.
</div><span class="text_page_counter">Trang 8</span><div class="page_container" data-page="8">Sau khi cài đặt thành cơng thì trong file <b>package.json</b> ta sẽ thấy tự động có thêm phần devDependencies.
Cũng tại file <b>package.json, </b>ta set up lại phần scripts như sau:
Sau khi set up xong, ta chạy thử với lệnh: <b>npm run dev</b> hoặc <b>nmp run prod.</b>
</div><span class="text_page_counter">Trang 9</span><div class="page_container" data-page="9"><b>3. Creating First Route</b>
<b>Tóm tắt: Tạo 2 folder lần lượt là controller và routes.</b>
Trong phần backend, ta tạo 1 folder là <b>controller</b>, trong <b>controller ta tạo file productController.js. File này có chức năng sẽ xử lí các product có liên quan </b>
tới logic hoặc chứa các hàm controller. Cụ thể bên dưới này, ta xây dựng 1 hàm
<b>có tên là “getProducts”.</b>
<b>Tạo 1 folder là route trong phần backend để xử lí cho tất cả các route, trong route</b> ta tạo file <b>product.js.</b>
<b>4. Setting Up Postman EnvironmentTóm tắt: Tập trung cài đặt môi trường Postman.</b>
- Download Postman về máy: -> Đăng ký tài khoản Postman -> Đăng nhập thành cơng vào Postman.
Tại góc bên tay phải, click vào biểu tượng đánh số như hình dưới, tại đây ta <b>1</b>
click chọn button “<b>Add</b>” để thêm môi trường mới vào.
</div><span class="text_page_counter">Trang 10</span><div class="page_container" data-page="10">Ta đặt tên cho môi trường là ShopIT, Variable là DOMAIN và INITIAL VALUE là http://localhost:4000.
Tạo một <b>(1)</b> Collection mới đặt tên là <b>(2) </b>“Shop IT”, -> tạo folder “Product”. Ta sẽ save lại route trong folder này bằng cách gõ lệnh <b>(3)</b>
<b>{{DOMAIN}}/api/v1/products.</b> Click button <b>(4)</b> Send. Và cuối cùng nếu thành công sẽ cho ra kết quả như <b>(5). </b>Sau cùng, ta save lại và đặt tên route là “Get All Products”.
Như vậy là ta đã hoàn thành việc set up xong cho Postman.
<b>5. Connecting To MongoDB</b>
</div><span class="text_page_counter">Trang 11</span><div class="page_container" data-page="11"><b>Tóm tắt: Kết nối ứng dụng với MongoDB database và bắt đầu thêm sản phẩm </b>
vào database.
Trong folder <b>Config</b>, ta tạo 1 file là <b>database.js</b>, chủ yếu dùng để chứa hàm connectDatabase.
<b>Trở lại file server.js, tại đây import vào và gọi hàm ra.</b>
Sau khi, save lại ta nhận thấy ở terminal sẽ xuất hiện hai dịng thơng báo như bên dưới đây. Nghĩa là database đã được kết nối thành công với localhost của ta.
</div><span class="text_page_counter">Trang 13</span><div class="page_container" data-page="13"><b>FOLDER 4: ADDING PRODUCTS RESOURCE1. Creating Product Model</b>
<b>Tóm tắt: Tiến hành tạo product model, sau đó thêm product vào database ở </b>
video kết tiếp.
Đầu tiên, trong folder <b>backend</b>, tạo 1 folder mới tên là <b>models</b>. Sau đó, tạo 1
<b>file là product.js</b> trong folder <b>models</b>. Trong file này, ta set up một product model bao gồm tên sản phẩm, giá thành, mô tả, tỷ giá,...
Vậy là ta đã set up thành công product model này và trong video tiếp theo ta sẽ thêm route đầu tiên để tạo ra một product mới.
<b>2. Admin - Create _ Save Product</b>
<b>Tóm tắt: Tạo route đầu tiên để tạo ra product mới và save product đó lên </b>
<b>Đầu tiên, tại thư mục controllers</b> -> file <b>productController.js</b>
</div><span class="text_page_counter">Trang 14</span><div class="page_container" data-page="14"><b>Đi đến folder routes</b> -> <b>product.js</b>
Trong folder <b>backend</b>, ta tạo 1 folder mới là <b>data</b>, tại đây ta tạo 1 file
<b>product.js. </b>
File này sẽ chứa data của một vài product.
</div><span class="text_page_counter">Trang 15</span><div class="page_container" data-page="15">Chẳng hạn với sản phẩm “Tony Buoi Sang” như hình dưới thì sản phẩm có thể có 2 hình ảnh.
Ta mở <b>postman</b> lên, chỉ admin mới có thể thêm product nhưng ngay lúc này, ta khơng có bất kì authentication nào hết vì vậy ai cũng có thể tạo được product hết. Điều này là hồn tồn bình thường vì ta vẫn chưa có bất kì authentication nào bây giờ.
Ta select “POST” <b>(1)</b> -> gõ DOMAIN <b>(2)</b> vào -> coppy thông tin của một product <b>(3)</b> dán vào -> click “Send” <b>(4).</b>
Kết quả:
</div><span class="text_page_counter">Trang 16</span><div class="page_container" data-page="16"><b>3. Creating Data Seeder</b>
<b>Tóm tắt: Trong video này, ta sẽ create data seeder sẽ giúp ta seed data (giúp ta </b>
push các product). Ta sẽ tạo 1 single file để push tất cả các product vào database chỉ trong một command.
Trong folder <b>backend</b>, tạo 1 folder tên là <b>utils</b>. Tại đây tạo 1 file <b>seeder.js</b>
Đồng thời, ta thêm một scrip mới trong file <b>package.json</b>
</div><span class="text_page_counter">Trang 17</span><div class="page_container" data-page="17">Sau đó, ta gõ lệnh: <b>npm run seeder</b> lên terminal, kết quả trả về như sau.
Ta qua kiểm tra bên MongoDB Compass, thì tồn bộ các product đã được load lên.
<b>4. Display all products</b>
<b>Tóm tắt: Ta sẽ display tồn bộ product trong database trong productController.js</b>
</div><span class="text_page_counter">Trang 18</span><div class="page_container" data-page="18">Ta sang postman để kiểm tra thử, click chọn <b>“send” (1</b>). Thì kết quả trả về là toàn bộ cá product. Ở <b>(3)</b> thể hiện thông số như <b>(3)</b> success là true và tổng số product là 2.
<b>5. Get Single Product</b>
<b>Tóm tắt: Trong video này, ta sẽ get một special product detail với sự giúp đỡ </b>
của ID ( ID của product mà Mongoose đã assigned cho nó).
Nếu như có ID mà ta cần tìm thì kết quả sẽ trả về success: true và thông tin product. Nếu như không có thì về success: false kèm message “Product not found”.
</div><span class="text_page_counter">Trang 19</span><div class="page_container" data-page="19">Trở lại postman, get request: Nếu có id ta cần tìm.
Nếu khơng có id cần tìm:
</div><span class="text_page_counter">Trang 20</span><div class="page_container" data-page="20"><b>6. Admin _ Update Product</b>
<b>Tóm tắt: Trong video này, ta sẽ update các product (các thông tin của product).</b>
<b>Đồng thời, ta cũng thêm bên routes</b> -> <b>product.js</b>
</div><span class="text_page_counter">Trang 21</span><div class="page_container" data-page="21">Ta thấy hiện tại, product “Tony Buoi Sang” số lượng đang còn 10.
Ta sẽ update stock lên thành 50 như sau:
</div><span class="text_page_counter">Trang 22</span><div class="page_container" data-page="22"><b>7. Admin _ Delete Product</b>
<b>Tóm tắt: Trong video này, ta sẽ xử lí việc xóa các product.Controller</b> -> <b>productController.js</b>
<b>Đồng thời, ta cũng thêm bên routes</b> -> <b>product.js</b>
</div><span class="text_page_counter">Trang 23</span><div class="page_container" data-page="23">Trở sang bên postman, ta sẽ xóa product có id như hình <b>(2)</b>. Và thơng báo xuất hiện là đã xóa thành công!
Bây giờ ta sẽ tiến hành xem liệu product đó có cịn trong database hay khơng nhé !
</div><span class="text_page_counter">Trang 24</span><div class="page_container" data-page="24"><b>Như ta thấy thì lúc này đã khơng cịn product đó trong database nữa rồi (4).</b>
</div><span class="text_page_counter">Trang 25</span><div class="page_container" data-page="25"><b>FOLDER 5: BACKEND ERROR HANDLING1. Creating Error Handler Class</b>
<b>Tóm tắt: Làm thế nào để ta có thể xử lí các lỗi ở phần backend? Và đơi khi ta </b>
buộc phải xử lí các lỗi async xảy ra trong các method controller.
<b>Đầu tiên, ta sẽ tạo 1 class có tên là errorHandler trong folder utils. </b>Ngay bây giờ, ta không thể sử dụng ngay class này được vì ta phải tạo ra các error và sau đó mới có thể dùng class này để xử lí các lỗi.
<b>2. Creating Errors Middleware</b>
<b>Tóm tắt: Trong video này, ta sẽ create middleware cho các error.</b>
<b>Đầu tiên, ta tạo ra một folder có tên là “middlewares”, tại đây ta tạo một file có tên là error.js</b>
Ta chứa function của module trong này.
</div><span class="text_page_counter">Trang 26</span><div class="page_container" data-page="26">Trong file app.js để dùng hàm mới tạo ở trên.
<b>Và bên folder controller</b> -> <b>productController.js</b>
Ta thực dùng hàm ErrorHandler thay vì sử dụng cả một cấu trúc cũ.
</div><span class="text_page_counter">Trang 27</span><div class="page_container" data-page="27">Ta thử test trên postman.
<b>3. Production Vs Development Errors</b>
<b>Tóm tắt: Tác giả đề cập là muốn phân loại giữa production error và </b>
development errror ra để show chính xác được message. Ý chính là để quản lý các biến không phải là ENV bao gồm development và production.
</div><span class="text_page_counter">Trang 46</span><div class="page_container" data-page="46">Cookie khơng thể được truy cập bằng bất kì javascript nào bên phía client. Mã Token có thể được lưu trong này một cách an tồn thay vì lưu trên local storage.
Bậy giờ ta thực hiện một đoạn code để triển khai điều này:
Trong file utils/jwtToken.js:
Ở đây tạo một token, trong options ta tiến hành set thời gian hết hạn cho cookie và ta cũng lưu COOKIE_EXPIRES_TIME ở trong file .env, thời gian hết hạn này được qui đổi về mili giây.
httpOnly: true – điều này giúp bảo vệ token ngăn chặn truy cập vào trong cookie.
Hàm cookie nhận 3 tham số: tham số thứ nhất là tên của token, tham số thứ hai là mã của token đó, tham số thứ ba là options trong đó có chứa thời gian hết hạn cũng như phương thức httpOnly.
Bây giờ ta tiến hành thay đổi lại đoạn code xử lý đăng nhập.
</div><span class="text_page_counter">Trang 47</span><div class="page_container" data-page="47"><b>6. Protect Routes from Unauthorized Users</b>
Khi người dùng chưa được xác thực truy cập vào private route, hãy lấy token từ cookie, xác minh token để có thể chuyển hướng vai trò một cách phù hợp.
Bạn sẽ nhận một token từ phái client bằng cách sử dụng một package có tên là cookie-parser. Import thư viện này vào trong app.js và nó được sử dụng như là một middleware.
Bây giờ tiến hành tạo một middleware để kiểm tra người dùng đã có token hay nói một cách khác là người dùng đã đăng nhập hay chưa.
Trong file middlewares/auth.js:
</div><span class="text_page_counter">Trang 48</span><div class="page_container" data-page="48">Trong middleware này sẽ thực hiện một số nghiệp vụ như sau:
Tiến hành lấy token trong cookies
Sau đó kiểm tra token có tồn tại hay không, nếu không tồn tại tức là người dùng chưa đăng nhập vào hệ thống.
Khi token tồn tại thì đi phân tích mã token này bằng hàm verify của jsonwebtoken. Hàm này nhận vào hai tham số, tham số thứ nhất là chuỗi token, tham số thứ hai là khóa bí mật ban đầu đã mã hóa chuỗi này.
Sau đó lưu user này vào trong req
<b>Test middleware unauthorized user</b>
Đầu tiên tiến hành import middleware vào route cần được bảo vệ, trong ví dụ route cần được bảo vệ là thêm mới một sản phẩm (‘/admin/product/new’)
</div><span class="text_page_counter">Trang 49</span><div class="page_container" data-page="49">Sử dụng postman để kiểm tra:
Kiểm tra header của request ban đầu khi chưa đăng nhập không tồn tại cookie cũng như token.
Sau khi gửi request thêm mới product sẽ gửi thông báo Not login
</div><span class="text_page_counter">Trang 50</span><div class="page_container" data-page="50">Tiến hành login và sau đó kiểm tra trên header cookie có token
Sau đó gửi request lại thì thấy đã tạo sản phẩm thành cơng.
Đó là tồn bộ q trình để kiếm tra và bảo vệ route khi chưa đăng nhập vào hệ thống.
<b>7. Logout UserChức năng logout</b>
Chức năng logout là cho người dùng thoát khỏi hệ thống
Để tạo chức năng này trong file controllers/authController.js
</div><span class="text_page_counter">Trang 51</span><div class="page_container" data-page="51">Trong hàm này thực hiện nghiệp vụ như sau: set token trong cookie thành null và set ngày hết hạn của cookie.
Sau đó tạo route ‘/logout’ trong route/auth.js
<b>8. Authorize User Roles and Permissions</b>
Đây là bước sau khi user đã login được vào hệ thống tức là ngay sau bước authentication.
Về cơ bản ở bước authentication chúng ta tạo một token xác thực lưu vào cookies, mỗi request người dùng gửi lên đều phải đi kèm với token đó, nếu đúng token thì người dùng mới có thể gọi được tới những api mà chúng ta yêu cầu xác thực.
Giờ tạo một hàm để kiểm tra đối với người đã đăng nhập vào hệ thống có role là gì và đối với role của họ thì có thể truy cập vào resource hay khơng.
Trong file middlewares/auth.js
</div><span class="text_page_counter">Trang 52</span><div class="page_container" data-page="52">Trong file này thực hiện một số logic như sau: hàm authorizationRoles này sẽ nhận vào list of roles và sau đó sẽ lấy role trong req.user đã đăng nhập đi so sánh. Nếu role của user có tồn tại trong list of role có quyền vào resource này thì thực hiện hàm next(), nếu khơng thì sẽ trả về lỗi permission.
Giờ áp dụng middleware vào một số role cần kiểm tra, trong ví dụ sẽ sử dụng role thêm mới sản phẩm và chỉ có admin mới có quyền thêm mới.
<b>Kiểm tra middleware authorization</b>
Sử dụng postman để kiểm tra
Với role hiện tại là ‘user’ khi thực hiện request lên server thêm mới sản phẩm sẽ trả về lỗi khơng có quyền truy cập.
</div><span class="text_page_counter">Trang 53</span><div class="page_container" data-page="53"><b>9. Adding User in Products</b>
- Khi thêm 1 sản phẩm mới vào danh sách sản phẩm thì sản phẩm này sẽ có thêm cột thông tin User_Id để biết được ai là người đã thêm sản phẩm này. - Đầu tiên cần thêm cột UserID vào Product Model với:
+ type: Kiểu dữ liệu là ObjectId được MongoDB tự generate khi thêm 1 document
+ ref: mongoose sẽ hiểu là cần liên kết với model này + required: yêu cầu phải nhập trường này khi tạo product mới
</div><span class="text_page_counter">Trang 54</span><div class="page_container" data-page="54">- Tại hàm createProduct sẽ lấy Id đang được đăng nhập của người dùng gán vào biến UserID trong phần body của request
- Bây giờ chỉ cần khởi tạo product mới là UserID sẽ được nhập vào
- Dùng postman để kiểm tra khi thêm product mới thì đã có thơng tin UserID đang đăng nhập.
</div><span class="text_page_counter">Trang 55</span><div class="page_container" data-page="55"><b>10. Generate Forgot Password token</b>
- Import thư viện crypto để xử lý việc mã hoá
- Tạo phương thức Generate token khi reset password
+ randomBytes: tạo ra chuỗi 20byte ngẫu nhiên nhưng vẫn có độ phức tạp, sau đó convert về kiểu hexa
+ createHash: tạo ra mã hash với nội dung được hash ngẫu nhiên bằng thuật toán sha256
+ update: thay thế nội dung được hash bằng mã resetToken + digest: trả về chuỗi hexa nếu như hash thành công
+ resetPasswordExpire: khởi tạo thời gian cho token này là 30 phút
</div><span class="text_page_counter">Trang 56</span><div class="page_container" data-page="56"><b>11. Send Password Recovery Email</b>
- Trước tiên cần tạo hàm sendEmail để thực hiện gửi mail đến người dùng - Vào trang web mailtrap.io Sandbox Inboxes SMTP Settings. Tại Integrations chọn Nodemailer, lúc này trang web sẽ tạo ra phương thức gửi mail bằng nodemailer
- Tạo hàm sendEmail tại vị trí src\app\utils\sendEmail.js - Import thư viện nodemailer
- Copy đoạn code trên web mailtrap.io dán vào hàm này, tạo object message để khai báo thông tin gửi mail như from, to, … (các giá trị đã được lưu trong file .env)
</div>