From 24714b9fe830d45ebf58763fcd50d87feff596cc Mon Sep 17 00:00:00 2001 From: Darnell Date: Thu, 15 Aug 2024 01:58:53 -0400 Subject: [PATCH 1/3] Use single GET request for search functionality --- nodeJS/express/forms_and_data_handling.md | 59 +++++++++++++---------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/nodeJS/express/forms_and_data_handling.md b/nodeJS/express/forms_and_data_handling.md index 357db3b488c..69e02d91549 100644 --- a/nodeJS/express/forms_and_data_handling.md +++ b/nodeJS/express/forms_and_data_handling.md @@ -19,9 +19,9 @@ Let's create a simple HTML form, with a single text field for collecting a full The HTML structure would look something like this: ```html -
+ - +
``` @@ -39,7 +39,7 @@ The `form` attributes define how to communicate with the server: - `action`: The resource/URL where data is sent for processing when the form is submitted. If this is not set, or is an empty string, the form will be submitted back to the current page URL - `method`: Defines the HTTP method to use (`POST` or `GET`). -`POST` is generally more secure because it keeps sensitive information out of the URL, which means they won't show up in server logs, and is the standard choice for creating or updating data on the server side. `GET` is for forms that don't modify data, such as search forms, or when you want the form submission to be bookmarkable or shareable via URL. +`POST` is generally more secure because it keeps sensitive information out of the URL, which means they won't show up in server logs, and is the standard choice for creating or updating data on the server side. `GET` is for forms that don't modify data, such as search forms, or when you want the form submission to be bookmarkable or shareable via URL. The form data here is sent as a query string as part of the request url. #### Form handling process @@ -51,8 +51,8 @@ We then generate a new or updated view with the controller's response and redire Before the data from a form is sent off to our server, we should consider two important steps: -- *Validation* ensures user input meets the specified criteria (e.g. required fields, correct format). -- *Sanitization* cleans user input to prevent malicious data from being processed by removing or encoding potentially malicious characters. +- _Validation_ ensures user input meets the specified criteria (e.g. required fields, correct format). +- _Sanitization_ cleans user input to prevent malicious data from being processed by removing or encoding potentially malicious characters. We don't always have to sanitize data right when we get it - sometimes it makes sense to sanitize just before we use it instead. @@ -60,7 +60,7 @@ We'll be using a library called `express-validator` to help us out with both of #### Installing express-validator -We start as usual by installing the correct package in the *root* folder of our project. +We start as usual by installing the correct package in the _root_ folder of our project. ```bash npm install express-validator @@ -82,7 +82,7 @@ The `body()` function allows you to specify which fields in the request body sho [ body("birthdate", "Must be a valid date.") .optional({ values: "falsy" }) - .isISO8601() // Enforce a YYYY-MM-DD format. + .isISO8601(), // Enforce a YYYY-MM-DD format. ]; ``` @@ -99,7 +99,7 @@ You can also chain multiple validation methods, with unique error messages if th .notEmpty() .withMessage("Name can not be empty.") .isAlpha() - .withMessage("Name must only contain alphabet letters."), + .withMessage("Name must only contain alphabet letters."), ]; ``` @@ -122,11 +122,15 @@ When unescaped, this would be rendered into HTML as: ```html
- About Me: ! + About Me: + + !
``` -To prevent this [cross-site scripting (XSS) attack](https://en.wikipedia.org/wiki/Cross-site_scripting), we can *escape* the output (you may also see this referred to as *encoding*). Escaped HTML replaces special characters, like `<`, with their respective HTML entities, in this case `<`. In EJS, we can escape the output using `<%= %>`. +To prevent this [cross-site scripting (XSS) attack](https://en.wikipedia.org/wiki/Cross-site_scripting), we can _escape_ the output (you may also see this referred to as _encoding_). Escaped HTML replaces special characters, like `<`, with their respective HTML entities, in this case `<`. In EJS, we can escape the output using `<%= %>`. ```ejs
@@ -191,7 +195,7 @@ In our form, the action would look something like this:
``` -`/users/:id/update` is an *endpoint* we've created on our Express server. +`/users/:id/update` is an _endpoint_ we've created on our Express server. ### Putting it together @@ -220,7 +224,7 @@ const PORT = process.env.PORT || 3000; app.listen(PORT, () => console.log(`Express app listening on port ${PORT}!`)); ``` -Most simple forms will use the `Content-Type: application/x-www-form-urlencoded` HTTP header when sending data to the server. Express, however, can't natively parse that data. We can use the `express.urlencoded()` middleware to handle this for us and automatically set form's data to the `req.body` field. When `extended` is `false`, our server will only accept a `string` or an array of data, so we set it to `true` for some added flexibility. Note that if the `Content-Type` doesn't match `application/x-www-form-urlencoded`, then your server will show the data as an empty object `{}`. +Most simple forms will use the `Content-Type: application/x-www-form-urlencoded` HTTP header when sending data to the server. Express, however, can't natively parse that data. We can use the `express.urlencoded()` middleware to handle this for us and automatically set form's data to the `req.body` field. When `extended` is `false`, our server will only accept a `string` or an array of data, so we set it to `true` for some added flexibility. Note that if the `Content-Type` doesn't match `application/x-www-form-urlencoded`, then your server will show the data as an empty object `{}`. Let's create a new router called `usersRouter.js` in the routes folder: @@ -364,12 +368,18 @@ const alphaErr = "must only contain letters."; const lengthErr = "must be between 1 and 10 characters."; const validateUser = [ - body("firstName").trim() - .isAlpha().withMessage(`First name ${alphaErr}`) - .isLength({ min: 1, max: 10 }).withMessage(`First name ${lengthErr}`), - body("lastName").trim() - .isAlpha().withMessage(`Last name ${alphaErr}`) - .isLength({ min: 1, max: 10 }).withMessage(`Last name ${lengthErr}`), + body("firstName") + .trim() + .isAlpha() + .withMessage(`First name ${alphaErr}`) + .isLength({ min: 1, max: 10 }) + .withMessage(`First name ${lengthErr}`), + body("lastName") + .trim() + .isAlpha() + .withMessage(`Last name ${alphaErr}`) + .isLength({ min: 1, max: 10 }) + .withMessage(`Last name ${lengthErr}`), ]; // We can pass an entire array of middleware validations to our controller. @@ -386,7 +396,7 @@ exports.usersCreatePost = [ const { firstName, lastName } = req.body; usersStorage.addUser({ firstName, lastName }); res.redirect("/"); - } + }, ]; ``` @@ -475,7 +485,7 @@ exports.usersUpdatePost = [ const { firstName, lastName } = req.body; usersStorage.updateUser(req.params.id, { firstName, lastName }); res.redirect("/"); - } + }, ]; ``` @@ -541,11 +551,10 @@ Don't forget to update the view to display these new fields! What if we want to search for a specific user in a list of thousands? We'll need a new route and view that lets clients search our list of users. -1. Add a form (in `createUser.ejs` or another view) which accepts a `name` or `email` (or both!) -1. Create a new route `/search` which accepts `GET` and `POST` requests. -1. Add the search logic to your controller which searches your list for a matching user. - - Your `POST` request should handle searching for the user. - - Your `GET` request should then render the search result. +1. Add a form with a `GET` method (in `createUser.ejs` or another view) which accepts a `name` or `email` (or both!) +1. Create a new route `/search` which accepts a `GET` request. +1. Add the search logic to your controller which searches your list for a matching user. Form data that has been sent via a `GET` request will not be available in the `req.body`. You will need to use `req.query` instead. + - Your `GET` request should handle searching for the user and then render the search result. 1. Display the search results in a new view: `search.ejs`. #### Further Reading From 9d20c4aa84928a479006a9b139e5583ffba872a5 Mon Sep 17 00:00:00 2001 From: Darnell Date: Thu, 15 Aug 2024 14:28:59 -0400 Subject: [PATCH 2/3] Undo prettier formatting and lint according to project rules --- nodeJS/express/forms_and_data_handling.md | 48 +++++++++-------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/nodeJS/express/forms_and_data_handling.md b/nodeJS/express/forms_and_data_handling.md index 69e02d91549..a93f86d2a48 100644 --- a/nodeJS/express/forms_and_data_handling.md +++ b/nodeJS/express/forms_and_data_handling.md @@ -19,9 +19,9 @@ Let's create a simple HTML form, with a single text field for collecting a full The HTML structure would look something like this: ```html -
+ - +
``` @@ -51,8 +51,8 @@ We then generate a new or updated view with the controller's response and redire Before the data from a form is sent off to our server, we should consider two important steps: -- _Validation_ ensures user input meets the specified criteria (e.g. required fields, correct format). -- _Sanitization_ cleans user input to prevent malicious data from being processed by removing or encoding potentially malicious characters. +- *Validation* ensures user input meets the specified criteria (e.g. required fields, correct format). +- *Sanitization* cleans user input to prevent malicious data from being processed by removing or encoding potentially malicious characters. We don't always have to sanitize data right when we get it - sometimes it makes sense to sanitize just before we use it instead. @@ -60,7 +60,7 @@ We'll be using a library called `express-validator` to help us out with both of #### Installing express-validator -We start as usual by installing the correct package in the _root_ folder of our project. +We start as usual by installing the correct package in the *root* folder of our project. ```bash npm install express-validator @@ -82,7 +82,7 @@ The `body()` function allows you to specify which fields in the request body sho [ body("birthdate", "Must be a valid date.") .optional({ values: "falsy" }) - .isISO8601(), // Enforce a YYYY-MM-DD format. + .isISO8601() // Enforce a YYYY-MM-DD format. ]; ``` @@ -99,7 +99,7 @@ You can also chain multiple validation methods, with unique error messages if th .notEmpty() .withMessage("Name can not be empty.") .isAlpha() - .withMessage("Name must only contain alphabet letters."), + .withMessage("Name must only contain alphabet letters."), ]; ``` @@ -122,15 +122,11 @@ When unescaped, this would be rendered into HTML as: ```html
- About Me: - - ! + About Me: !
``` -To prevent this [cross-site scripting (XSS) attack](https://en.wikipedia.org/wiki/Cross-site_scripting), we can _escape_ the output (you may also see this referred to as _encoding_). Escaped HTML replaces special characters, like `<`, with their respective HTML entities, in this case `<`. In EJS, we can escape the output using `<%= %>`. +To prevent this [cross-site scripting (XSS) attack](https://en.wikipedia.org/wiki/Cross-site_scripting), we can *escape* the output (you may also see this referred to as *encoding*). Escaped HTML replaces special characters, like `<`, with their respective HTML entities, in this case `<`. In EJS, we can escape the output using `<%= %>`. ```ejs
@@ -195,7 +191,7 @@ In our form, the action would look something like this:
``` -`/users/:id/update` is an _endpoint_ we've created on our Express server. +`/users/:id/update` is an *endpoint* we've created on our Express server. ### Putting it together @@ -224,7 +220,7 @@ const PORT = process.env.PORT || 3000; app.listen(PORT, () => console.log(`Express app listening on port ${PORT}!`)); ``` -Most simple forms will use the `Content-Type: application/x-www-form-urlencoded` HTTP header when sending data to the server. Express, however, can't natively parse that data. We can use the `express.urlencoded()` middleware to handle this for us and automatically set form's data to the `req.body` field. When `extended` is `false`, our server will only accept a `string` or an array of data, so we set it to `true` for some added flexibility. Note that if the `Content-Type` doesn't match `application/x-www-form-urlencoded`, then your server will show the data as an empty object `{}`. +Most simple forms will use the `Content-Type: application/x-www-form-urlencoded` HTTP header when sending data to the server. Express, however, can't natively parse that data. We can use the `express.urlencoded()` middleware to handle this for us and automatically set form's data to the `req.body` field. When `extended` is `false`, our server will only accept a `string` or an array of data, so we set it to `true` for some added flexibility. Note that if the `Content-Type` doesn't match `application/x-www-form-urlencoded`, then your server will show the data as an empty object `{}`. Let's create a new router called `usersRouter.js` in the routes folder: @@ -368,18 +364,12 @@ const alphaErr = "must only contain letters."; const lengthErr = "must be between 1 and 10 characters."; const validateUser = [ - body("firstName") - .trim() - .isAlpha() - .withMessage(`First name ${alphaErr}`) - .isLength({ min: 1, max: 10 }) - .withMessage(`First name ${lengthErr}`), - body("lastName") - .trim() - .isAlpha() - .withMessage(`Last name ${alphaErr}`) - .isLength({ min: 1, max: 10 }) - .withMessage(`Last name ${lengthErr}`), + body("firstName").trim() + .isAlpha().withMessage(`First name ${alphaErr}`) + .isLength({ min: 1, max: 10 }).withMessage(`First name ${lengthErr}`), + body("lastName").trim() + .isAlpha().withMessage(`Last name ${alphaErr}`) + .isLength({ min: 1, max: 10 }).withMessage(`Last name ${lengthErr}`), ]; // We can pass an entire array of middleware validations to our controller. @@ -396,7 +386,7 @@ exports.usersCreatePost = [ const { firstName, lastName } = req.body; usersStorage.addUser({ firstName, lastName }); res.redirect("/"); - }, + } ]; ``` @@ -485,7 +475,7 @@ exports.usersUpdatePost = [ const { firstName, lastName } = req.body; usersStorage.updateUser(req.params.id, { firstName, lastName }); res.redirect("/"); - }, + } ]; ``` From e7b93d0187c0c62de495641a882ce30e0de69b0c Mon Sep 17 00:00:00 2001 From: Darnell Date: Fri, 16 Aug 2024 01:34:42 -0400 Subject: [PATCH 3/3] Remove extra space before closing tag on line 22 --- nodeJS/express/forms_and_data_handling.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nodeJS/express/forms_and_data_handling.md b/nodeJS/express/forms_and_data_handling.md index a93f86d2a48..a5b82deca7e 100644 --- a/nodeJS/express/forms_and_data_handling.md +++ b/nodeJS/express/forms_and_data_handling.md @@ -19,7 +19,7 @@ Let's create a simple HTML form, with a single text field for collecting a full The HTML structure would look something like this: ```html -
+ @@ -543,7 +543,7 @@ What if we want to search for a specific user in a list of thousands? We'll need 1. Add a form with a `GET` method (in `createUser.ejs` or another view) which accepts a `name` or `email` (or both!) 1. Create a new route `/search` which accepts a `GET` request. -1. Add the search logic to your controller which searches your list for a matching user. Form data that has been sent via a `GET` request will not be available in the `req.body`. You will need to use `req.query` instead. +1. Add the search logic to your controller which searches your list for a matching user. Form data that has been sent via a `GET` request will not be available via `req.body`. You will need to use `req.query` instead. - Your `GET` request should handle searching for the user and then render the search result. 1. Display the search results in a new view: `search.ejs`.