Trong hướng dẫn này, chúng tôi sẽ chỉ cho bạn cách làm việc với Biểu mẫu HTML trong Express bằng Pug. Cụ thể, chúng ta sẽ thảo luận cách viết biểu mẫu để tạo, cập nhật và xóa tài liệu khỏi cơ sở dữ liệu của trang web
điều kiện tiên quyết. Hoàn thành tất cả các chủ đề hướng dẫn trước đó, bao gồm Hướng dẫn cấp tốc Phần 5. Hiển thị dữ liệu thư việnMục tiêu. Để hiểu cách viết biểu mẫu để lấy dữ liệu từ người dùng và cập nhật cơ sở dữ liệu với dữ liệu nàyBiểu mẫu HTML là một nhóm gồm một hoặc nhiều trường/tiện ích trên trang web có thể được sử dụng để thu thập thông tin từ người dùng để gửi tới máy chủ. Biểu mẫu là một cơ chế linh hoạt để thu thập thông tin đầu vào của người dùng vì có sẵn các đầu vào biểu mẫu phù hợp để nhập nhiều loại dữ liệu khác nhau—hộp văn bản, hộp kiểm, nút radio, bộ chọn ngày, v.v. Biểu mẫu cũng là một cách tương đối an toàn để chia sẻ dữ liệu với máy chủ, vì chúng cho phép chúng tôi gửi dữ liệu theo yêu cầu POST
với tính năng bảo vệ giả mạo yêu cầu trên nhiều trang web
Làm việc với các biểu mẫu có thể phức tạp. Nhà phát triển cần viết HTML cho biểu mẫu, xác thực và làm sạch đúng cách dữ liệu đã nhập trên máy chủ [và có thể cả trong trình duyệt], đăng lại biểu mẫu có thông báo lỗi để thông báo cho người dùng về bất kỳ trường không hợp lệ nào, xử lý dữ liệu khi đã gửi thành công
Trong hướng dẫn này, chúng tôi sẽ chỉ cho bạn cách thực hiện các thao tác trên trong Express. Đồng thời, chúng tôi sẽ mở rộng trang web LocalLibrary để cho phép người dùng tạo, chỉnh sửa và xóa các mục khỏi thư viện
Ghi chú. Chúng tôi chưa xem xét cách hạn chế các tuyến cụ thể đối với người dùng được xác thực hoặc được ủy quyền, vì vậy tại thời điểm này, bất kỳ người dùng nào cũng có thể thực hiện thay đổi đối với cơ sở dữ liệu
Đầu tiên là tổng quan ngắn gọn về Biểu mẫu HTML. Hãy xem xét một biểu mẫu HTML đơn giản, với một trường văn bản duy nhất để nhập tên của một số "nhóm" và nhãn được liên kết của nó
Biểu mẫu được định nghĩa trong HTML là một tập hợp các phần tử bên trong các thẻ …
, chứa ít nhất một phần tử
npm install express-validator
0 của npm install express-validator
1
Enter name:
Mặc dù ở đây chúng tôi chỉ bao gồm một trường [văn bản] để nhập tên nhóm, một biểu mẫu có thể chứa bất kỳ số lượng thành phần đầu vào nào khác và nhãn liên quan của chúng. Thuộc tính
npm install express-validator
2 của trường xác định loại tiện ích nào sẽ được hiển thị. npm install express-validator
3 và npm install express-validator
4 của trường được sử dụng để xác định trường trong JavaScript/CSS/HTML, trong khi npm install express-validator
5 xác định giá trị ban đầu cho trường khi nó được hiển thị lần đầu. Nhãn nhóm phù hợp được chỉ định bằng cách sử dụng thẻ npm install express-validator
6 [xem "Nhập tên" ở trên], với trường npm install express-validator
7 chứa giá trị npm install express-validator
4 của npm install express-validator
0 được liên kếtDữ liệu đầu vào
const { body, validationResult } = require["express-validator"];
0 sẽ được hiển thị dưới dạng một nút [theo mặc định]—người dùng có thể nhấn nút này để tải dữ liệu chứa trong các thành phần đầu vào khác lên máy chủ [trong trường hợp này, chỉ là const { body, validationResult } = require["express-validator"];
1]. Các thuộc tính biểu mẫu xác định HTTP const { body, validationResult } = require["express-validator"];
2 được sử dụng để gửi dữ liệu và đích của dữ liệu trên máy chủ [const { body, validationResult } = require["express-validator"];
3]
3. Tài nguyên/URL nơi dữ liệu sẽ được gửi để xử lý khi biểu mẫu được gửi. Nếu điều này không được đặt [hoặc được đặt thành một chuỗi trống], thì biểu mẫu sẽ được gửi trở lại URL của trang hiện tạiconst { body, validationResult } = require["express-validator"];
2. Phương thức HTTP được sử dụng để gửi dữ liệu.const { body, validationResult } = require["express-validator"];
POST
hoặc
7const { body, validationResult } = require["express-validator"];
- Phương pháp
POST
phải luôn được sử dụng nếu dữ liệu sẽ dẫn đến thay đổi cơ sở dữ liệu của máy chủ, bởi vì điều này có thể được thực hiện để chống lại các cuộc tấn công yêu cầu giả mạo giữa các trang web - Chỉ nên sử dụng phương thức
7 cho các biểu mẫu không thay đổi dữ liệu người dùng [e. g. mẫu tìm kiếm]. Nó được khuyến nghị khi bạn muốn có thể đánh dấu hoặc chia sẻ URLconst { body, validationResult } = require["express-validator"];
- Phương pháp
Xử lý biểu mẫu sử dụng tất cả các kỹ thuật giống như chúng tôi đã học để hiển thị thông tin về các mô hình của chúng tôi. tuyến đường gửi yêu cầu của chúng tôi đến chức năng điều khiển thực hiện bất kỳ hành động cơ sở dữ liệu nào được yêu cầu, bao gồm đọc dữ liệu từ các mô hình, sau đó tạo và trả về trang HTML. Điều khiến mọi thứ trở nên phức tạp hơn là máy chủ cũng cần có khả năng xử lý dữ liệu do người dùng cung cấp và hiển thị lại biểu mẫu với thông tin lỗi nếu có bất kỳ sự cố nào
Sơ đồ quy trình để xử lý yêu cầu biểu mẫu được hiển thị bên dưới, bắt đầu bằng yêu cầu cho trang chứa biểu mẫu [hiển thị bằng màu xanh lá cây]
Như thể hiện trong sơ đồ trên, những điều chính mà mã xử lý biểu mẫu cần làm là
- Hiển thị biểu mẫu mặc định khi người dùng yêu cầu lần đầu tiên
- Biểu mẫu có thể chứa các trường trống [e. g. nếu bạn đang tạo một bản ghi mới] hoặc nó có thể được điền sẵn các giá trị ban đầu [e. g. nếu bạn đang thay đổi bản ghi hoặc có các giá trị ban đầu mặc định hữu ích]
- Nhận dữ liệu do người dùng gửi, thường là trong một yêu cầu HTTP
POST
- Xác thực và vệ sinh dữ liệu
- Nếu bất kỳ dữ liệu nào không hợp lệ, hãy hiển thị lại biểu mẫu—lần này với mọi giá trị do người dùng điền và thông báo lỗi cho các trường có vấn đề
- Nếu tất cả dữ liệu hợp lệ, hãy thực hiện các hành động cần thiết [e. g. lưu dữ liệu trong cơ sở dữ liệu, gửi email thông báo, trả về kết quả tìm kiếm, tải tệp lên, v.v. ]
- Khi tất cả các hành động hoàn tất, hãy chuyển hướng người dùng đến một trang khác
Thông thường, mã xử lý biểu mẫu được triển khai bằng cách sử dụng tuyến đường
const { body, validationResult } = require["express-validator"];
7 để hiển thị ban đầu của biểu mẫu và tuyến đường POST
đến cùng một đường dẫn để xử lý xác thực và xử lý dữ liệu biểu mẫu. Đây là cách tiếp cận sẽ được sử dụng trong hướng dẫn nàyBản thân Express không cung cấp bất kỳ hỗ trợ cụ thể nào cho các hoạt động xử lý biểu mẫu, nhưng nó có thể sử dụng phần mềm trung gian để xử lý các tham số POST
và
const { body, validationResult } = require["express-validator"];
7 từ biểu mẫu và để xác thực/khử trùng các giá trị của chúngTrước khi dữ liệu từ một biểu mẫu được lưu trữ, nó phải được xác thực và làm sạch
- Xác thực kiểm tra xem các giá trị đã nhập có phù hợp với từng trường không [ở đúng phạm vi, định dạng, v.v. ] và các giá trị đó đã được cung cấp cho tất cả các trường bắt buộc
- Sanitization xóa/thay thế các ký tự trong dữ liệu có khả năng được sử dụng để gửi nội dung độc hại đến máy chủ
Đối với hướng dẫn này, chúng tôi sẽ sử dụng mô-đun trình xác thực nhanh phổ biến để thực hiện cả xác thực và làm sạch dữ liệu biểu mẫu của chúng tôi
Cài đặt
Cài đặt mô-đun bằng cách chạy lệnh sau trong thư mục gốc của dự án
npm install express-validator
Sử dụng trình xác thực nhanh
Ghi chú. Hướng dẫn trên GitHub cung cấp tổng quan tốt về API. Chúng tôi khuyên bạn nên đọc nó để có ý tưởng về tất cả các khả năng của nó [bao gồm cả việc sử dụng xác thực lược đồ và tạo trình xác thực tùy chỉnh]. Dưới đây chúng tôi chỉ đề cập đến một tập hợp con hữu ích cho LocalLibrary
Để sử dụng trình xác thực trong bộ điều khiển của chúng tôi, chúng tôi chỉ định các chức năng cụ thể mà chúng tôi muốn nhập từ mô-đun trình xác thực nhanh, như được hiển thị bên dưới
const { body, validationResult } = require["express-validator"];
Có nhiều chức năng có sẵn, cho phép bạn kiểm tra và làm sạch dữ liệu từ các tham số yêu cầu, nội dung, tiêu đề, cookie, v.v. , hoặc tất cả chúng cùng một lúc. Đối với hướng dẫn này, chúng tôi sẽ chủ yếu sử dụng
[
// …
body["name", "Empty name"].trim[].isLength[{ min: 1 }].escape[],
// …
];
5 và [
// …
body["name", "Empty name"].trim[].isLength[{ min: 1 }].escape[],
// …
];
6 [như "bắt buộc" ở trên]Các chức năng được định nghĩa như dưới đây
- Chỉ định một tập hợp các trường trong nội dung yêu cầu [một tham số
POST
] để xác thực và/hoặc làm sạch cùng với một thông báo lỗi tùy chọn có thể được hiển thị nếu nó không vượt qua được các bài kiểm tra. Các tiêu chí xác nhận và làm sạch được liên kết chặt chẽ với phương pháp
9. Ví dụ: dòng bên dưới trước tiên xác định rằng chúng tôi đang kiểm tra trường "tên" và lỗi xác thực sẽ đặt thông báo lỗi "Tên trống". Sau đó, chúng tôi gọi phương thức khử trùng[ // … body["name", "Empty name"].trim[].isLength[{ min: 1 }].escape[], // … ];
0 để xóa khoảng trắng ở đầu và cuối chuỗi, sau đó là[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
1 để kiểm tra xem chuỗi kết quả có trống không. Cuối cùng, chúng tôi gọi[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
2 để xóa các ký tự HTML khỏi biến có thể được sử dụng trong các cuộc tấn công tập lệnh chéo trang JavaScript[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
Thử nghiệm này kiểm tra xem trường tuổi có phải là một ngày hợp lệ hay không và sử dụng[ // … body["name", "Empty name"].trim[].isLength[{ min: 1 }].escape[], // … ];
3 để chỉ định rằng các chuỗi rỗng và rỗng sẽ không thất bại trong quá trình xác thực[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
Bạn cũng có thể xâu chuỗi các trình xác nhận khác nhau và thêm thông báo được hiển thị nếu các trình xác nhận trước đó là đúng[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
[ // … body["name"] .trim[] .isLength[{ min: 1 }] .withMessage["Name empty."] .isAlpha[] .withMessage["Name must be alphabet letters."], // … ];
- Chạy xác thực, làm cho các lỗi có sẵn ở dạng đối tượng kết quả
5. Điều này được gọi trong một cuộc gọi lại riêng biệt, như hình dưới đây[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
Chúng tôi sử dụng phương pháp[req, res, next] => { // Extract the validation errors from a request. const errors = validationResult[req]; if [!errors.isEmpty[]] { // There are errors. Render form again with sanitized values/errors messages. // Error messages can be returned in an array using `errors.array[]`. } else { // Data from form is valid. } };
6 của kết quả xác thực để kiểm tra xem có lỗi hay không và phương pháp[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
7 của nó để lấy tập hợp các thông báo lỗi. Xem API kết quả xác thực để biết thêm thông tin[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
Chuỗi xác thực và vệ sinh là phần mềm trung gian sẽ được chuyển đến trình xử lý tuyến Express [chúng tôi thực hiện việc này một cách gián tiếp, thông qua bộ điều khiển]. Khi phần mềm trung gian chạy, mỗi trình xác nhận/khử trùng được chạy theo thứ tự được chỉ định
Chúng tôi sẽ đề cập đến một số ví dụ thực tế khi chúng tôi triển khai các biểu mẫu Thư viện cục bộ bên dưới
Nhiều mô hình trong thư viện có liên quan/phụ thuộc—ví dụ: một
[
// …
body["age", "Invalid age"]
.optional[{ checkFalsy: true }]
.isISO8601[]
.toDate[],
// …
];
8 yêu cầu một [
// …
body["age", "Invalid age"]
.optional[{ checkFalsy: true }]
.isISO8601[]
.toDate[],
// …
];
9 và cũng có thể có một hoặc nhiều [
// …
body["name"]
.trim[]
.isLength[{ min: 1 }]
.withMessage["Name empty."]
.isAlpha[]
.withMessage["Name must be alphabet letters."],
// …
];
0. Điều này đặt ra câu hỏi về cách chúng ta nên xử lý trường hợp người dùng muốn- Tạo một đối tượng khi các đối tượng liên quan của nó chưa tồn tại [ví dụ: một cuốn sách chưa xác định đối tượng tác giả]
- Xóa một đối tượng vẫn đang được sử dụng bởi một đối tượng khác [ví dụ: xóa một
1 vẫn đang được sử dụng bởi một[ // … body["name"] .trim[] .isLength[{ min: 1 }] .withMessage["Name empty."] .isAlpha[] .withMessage["Name must be alphabet letters."], // … ];
8][ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
Đối với dự án này, chúng tôi sẽ đơn giản hóa việc triển khai bằng cách nêu rõ rằng một biểu mẫu chỉ có thể
- Tạo một đối tượng bằng cách sử dụng các đối tượng đã tồn tại [vì vậy người dùng sẽ phải tạo bất kỳ phiên bản
9 và[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
1 nào được yêu cầu trước khi cố gắng tạo bất kỳ đối tượng[ // … body["name"] .trim[] .isLength[{ min: 1 }] .withMessage["Name empty."] .isAlpha[] .withMessage["Name must be alphabet letters."], // … ];
8 nào][ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
- Xóa một đối tượng nếu đối tượng đó không được tham chiếu bởi các đối tượng khác [ví dụ: bạn sẽ không thể xóa một
8 cho đến khi tất cả các đối tượng[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
7 được liên kết đã bị xóa][ // … body["name"] .trim[] .isLength[{ min: 1 }] .withMessage["Name empty."] .isAlpha[] .withMessage["Name must be alphabet letters."], // … ];
Ghi chú. Việc triển khai "mạnh mẽ" hơn có thể cho phép bạn tạo đối tượng phụ thuộc khi tạo đối tượng mới và xóa bất kỳ đối tượng nào vào bất kỳ lúc nào [ví dụ: bằng cách xóa đối tượng phụ thuộc hoặc xóa tham chiếu đến đối tượng đã xóa khỏi cơ sở dữ liệu]
Để triển khai mã xử lý biểu mẫu của chúng tôi, chúng tôi sẽ cần hai tuyến đường có cùng mẫu URL. Lộ trình [
const { body, validationResult } = require["express-validator"];
7] đầu tiên được sử dụng để hiển thị một biểu mẫu trống mới để tạo đối tượng. Tuyến thứ hai [POST
] được sử dụng để xác thực dữ liệu do người dùng nhập, sau đó lưu thông tin và chuyển hướng đến trang chi tiết [nếu dữ liệu hợp lệ] hoặc hiển thị lại biểu mẫu có lỗi [nếu dữ liệu không hợp lệ]Chúng tôi đã tạo các tuyến đường cho tất cả các trang tạo mô hình của chúng tôi trong /routes/catalog. js [trong hướng dẫn trước]. Ví dụ: các tuyến thể loại được hiển thị bên dưới
// GET request for creating a Genre. NOTE This must come before route that displays Genre [uses id].
router.get["/genre/create", genre_controller.genre_create_get];
// POST request for creating Genre.
router.post["/genre/create", genre_controller.genre_create_post];
Các bài viết phụ sau đây sẽ đưa chúng ta qua quá trình thêm các biểu mẫu cần thiết vào ứng dụng mẫu của chúng ta. Bạn cần đọc và làm việc lần lượt qua từng cái trước khi chuyển sang cái tiếp theo
- Tạo biểu mẫu Thể loại — Xác định một trang để tạo các đối tượng
1[ // … body["name"] .trim[] .isLength[{ min: 1 }] .withMessage["Name empty."] .isAlpha[] .withMessage["Name must be alphabet letters."], // … ];
- Tạo biểu mẫu Tác giả — Xác định một trang để tạo các đối tượng
9[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
- Tạo biểu mẫu Sách — Xác định một trang/biểu mẫu để tạo các đối tượng
8[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
- Tạo biểu mẫu BookInstance — Xác định trang/biểu mẫu để tạo đối tượng
7[ // … body["name"] .trim[] .isLength[{ min: 1 }] .withMessage["Name empty."] .isAlpha[] .withMessage["Name must be alphabet letters."], // … ];
- Xóa biểu mẫu Tác giả — Xác định một trang để xóa các đối tượng
9[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
- Cập nhật biểu mẫu Sách — Xác định trang để cập nhật đối tượng
8[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
Thực hiện các trang xóa cho các mô hình
[
// …
body["age", "Invalid age"]
.optional[{ checkFalsy: true }]
.isISO8601[]
.toDate[],
// …
];
8, [
// …
body["name"]
.trim[]
.isLength[{ min: 1 }]
.withMessage["Name empty."]
.isAlpha[]
.withMessage["Name must be alphabet letters."],
// …
];
7 và [
// …
body["name"]
.trim[]
.isLength[{ min: 1 }]
.withMessage["Name empty."]
.isAlpha[]
.withMessage["Name must be alphabet letters."],
// …
];
1, liên kết chúng từ các trang chi tiết được liên kết theo cách tương tự như trang xóa Tác giả của chúng tôi. Các trang phải tuân theo cùng một phương pháp thiết kế- Nếu có các tham chiếu đến đối tượng từ các đối tượng khác, thì các đối tượng khác này sẽ được hiển thị cùng với lưu ý rằng bản ghi này không thể bị xóa cho đến khi các đối tượng được liệt kê đã bị xóa
- Nếu không có tham chiếu nào khác đến đối tượng thì dạng xem sẽ nhắc xóa nó. Nếu người dùng nhấn nút Xóa, bản ghi sẽ bị xóa
IT tiên boa
- Xóa một
1 cũng giống như xóa một[ // … body["name"] .trim[] .isLength[{ min: 1 }] .withMessage["Name empty."] .isAlpha[] .withMessage["Name must be alphabet letters."], // … ];
9 vì cả hai đối tượng đều là phần phụ thuộc của[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
8 [vì vậy trong cả hai trường hợp, bạn chỉ có thể xóa đối tượng khi các sách liên quan bị xóa][ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
- Xóa một
8 cũng tương tự, nhưng bạn cần kiểm tra xem không có[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
3 nào đi kèm// GET request for creating a Genre. NOTE This must come before route that displays Genre [uses id]. router.get["/genre/create", genre_controller.genre_create_get]; // POST request for creating Genre. router.post["/genre/create", genre_controller.genre_create_post];
- Xóa một
7 là dễ nhất vì không có đối tượng phụ thuộc. Trong trường hợp này, bạn chỉ cần tìm bản ghi được liên kết và xóa nó[ // … body["name"] .trim[] .isLength[{ min: 1 }] .withMessage["Name empty."] .isAlpha[] .withMessage["Name must be alphabet letters."], // … ];
Thực hiện các trang cập nhật cho các mô hình
[
// …
body["name"]
.trim[]
.isLength[{ min: 1 }]
.withMessage["Name empty."]
.isAlpha[]
.withMessage["Name must be alphabet letters."],
// …
];
7, [
// …
body["age", "Invalid age"]
.optional[{ checkFalsy: true }]
.isISO8601[]
.toDate[],
// …
];
9 và [
// …
body["name"]
.trim[]
.isLength[{ min: 1 }]
.withMessage["Name empty."]
.isAlpha[]
.withMessage["Name must be alphabet letters."],
// …
];
1, liên kết chúng từ các trang chi tiết liên quan giống như cách trang cập nhật Sách của chúng tôiIT tiên boa
- Trang cập nhật Sách chúng tôi mới thực hiện là khó nhất. Các mẫu tương tự có thể được sử dụng cho các trang cập nhật cho các đối tượng khác
- Trường
9 ngày mất và ngày sinh và trường[ // … body["age", "Invalid age"] .optional[{ checkFalsy: true }] .isISO8601[] .toDate[], // … ];
7 ngày đáo hạn là định dạng sai để nhập vào trường nhập ngày trên biểu mẫu [nó yêu cầu dữ liệu ở dạng "YYYY-MM-DD"]. Cách dễ nhất để giải quyết vấn đề này là xác định một thuộc tính ảo mới cho các ngày định dạng ngày phù hợp, sau đó sử dụng trường này trong các mẫu dạng xem được liên kết[ // … body["name"] .trim[] .isLength[{ min: 1 }] .withMessage["Name empty."] .isAlpha[] .withMessage["Name must be alphabet letters."], // … ];
- Nếu bạn gặp khó khăn, có các ví dụ về các trang cập nhật trong ví dụ tại đây
Các gói Express, nút và bên thứ ba trên npm cung cấp mọi thứ bạn cần để thêm biểu mẫu vào trang web của mình. Trong bài viết này, bạn đã học cách tạo biểu mẫu bằng Pug, xác thực và làm sạch đầu vào bằng trình xác thực nhanh cũng như thêm, xóa và sửa đổi các bản ghi trong cơ sở dữ liệu