Express Tutorial Part 6: Working with forms

In this tutorial we'll show you how to work with HTML Forms in Express using Pug. In particular, we'll discuss how to write forms to create, update, and delete documents from the site's database.

Prerequisites: Complete all previous tutorial topics, including Express Tutorial Part 5: Displaying library data
Objective: To understand how to write forms to get data from users, and update the database with this data.

概述

An HTML Form is a group of one or more fields/widgets on a web page that can be used to collect information from users for submission to a server. Forms are a flexible mechanism for collecting user input because there are suitable form inputs available for entering many different types of data—text boxes, checkboxes, radio buttons, date pickers, etc. Forms are also a relatively secure way of sharing data with the server, as they allow us to send data in POST requests with cross-site request forgery protection.

Working with forms can be complicated! Developers need to write HTML for the form, validate and properly sanitize entered data on the server (and possibly also in the browser), repost the form with error messages to inform users of any invalid fields, handle the data when it has successfully been submitted, and finally respond to the user in some way to indicate success.

In this tutorial, we're going to show you how the above operations may be performed in Express . Along the way, we'll extend the LocalLibrary website to allow users to create, edit and delete items from the library.

注意: We haven't looked at how to restrict particular routes to authenticated or authorized users, so at this point, any user will be able to make changes to the database.

HTML Forms

First a brief overview of HTML Forms . Consider a simple HTML form, with a single text field for entering the name of some "team", and its associated label:

Simple name field example in HTML form

The form is defined in HTML as a collection of elements inside <form>...</form> tags, containing at least one input 元素的 type="submit" .

<form action="/team_name_url/" method="post">
    <label for="team_name">Enter name: </label>
    <input id="team_name" type="text" name="name_field" value="Default name for team.">
    <input type="submit" value="OK">
</form>

						

While here we have included just one (text) field for entering the team name, a form may contain any number of other input elements and their associated labels. The field's type attribute defines what sort of widget will be displayed. The 名称 and id of the field are used to identify the field in JavaScript/CSS/HTML, while value defines the initial value for the field when it is first displayed. The matching team label is specified using the label tag (see "Enter name" above), with a for field containing the id value of the associated input .

submit input will be displayed as a button (by default)—this can be pressed by the user to upload the data contained by the other input elements to the server (in this case, just the team_name ). The form attributes define the HTTP 方法 used to send the data and the destination of the data on the server ( action ):

  • action : The resource/URL where data is to be sent for processing when the form is submitted. If this is not set (or set to an empty string), then the form will be submitted back to the current page URL.
  • 方法 : The HTTP method used to send the data: POST or GET .
    • POST method should always be used if the data is going to result in a change to the server's database, because this can be made more resistant to cross-site forgery request attacks.
    • GET method should only be used for forms that don't change user data (e.g. a search form). It is recommended for when you want to be able to bookmark or share the URL.

Form handling process

Form handling uses all of the same techniques that we learned for displaying information about our models: the route sends our request to a controller function which performs any database actions required, including reading data from the models, then generates and returns an HTML page. What makes things more complicated is that the server also needs to be able to process the data provided by the user, and redisplay the form with error information if there are any problems.

A process flowchart for processing form requests is shown below, starting with a request for a page containing a form (shown in green):

As shown in the diagram above, the main things that form handling code needs to do are:

  1. Display the default form the first time it is requested by the user.
    • The form may contain blank fields (e.g. if you're creating a new record), or it may be pre-populated with initial values (e.g. if you are changing a record, or have useful default initial values).
  2. Receive data submitted by the user, usually in an HTTP POST request.
  3. Validate and sanitize the data.
  4. If any data is invalid, re-display the form—this time with any user populated values and error messages for the problem fields.
  5. If all data is valid, perform required actions (e.g. save the data in the database, send a notification email, return the result of a search, upload a file, etc.)
  6. Once all actions are complete, redirect the user to another page.

Often form handling code is implemented using a GET route for the initial display of the form and a POST route to the same path for handling validation and processing of form data. This is the approach that will be used in this tutorial.

Express itself doesn't provide any specific support for form handling operations, but it can use middleware to process POST and GET parameters from the form, and to validate/sanitize their values.

Validation and sanitization

Before the data from a form is stored it must be validated and sanitized:

  • Validation checks that entered values are appropriate for each field (are in the right range, format, etc.) and that values have been supplied for all required fields.
  • Sanitization removes/replaces characters in the data that might potentially be used to send malicious content to the server.

For this tutorial, we'll be using the popular express-validator module to perform both validation and sanitization of our form data.

安装

Install the module by running the following command in the root of the project.

npm install express-validator

						

Using express-validator

注意: express-validator guide on Github provides a good overview of the API. We recommend you read that to get an idea of all its capabilities (including using schema validation and creating custom validators ). Below we cover just a subset that is useful for the LocalLibrary .

To use the validator in our controllers, we specify the particular functions we want to import from the express-validator module, as shown below:

const { body,validationResult } = require('express-validator');

						

There are many functions available, allowing you to check and sanitize data from request parameters, body, headers, cookies, etc., or all of them at once. For this tutorial, we'll primarily be using body and validationResult (as "required" above).

The functions are defined as below:

  • body([fields, message]) : Specifies a set of fields in the request body (a POST parameter) to validate and/or sanitize along with an optional error message that can be displayed if it fails the tests. The validation and sanitize criteria are daisy-chained to the body() 方法。 For example, the line below first defines that we're checking the "name" field and that a validation error will set an error message "Empty name". We then call the sanitization method trim() to remove whitespace from the start and end of the string, and then isLength() to check the resulting string isn't empty. Finally, we call escape() to remove HTML characters from the variable that might be used in JavaScript cross-site scripting attacks.
    body('name', 'Empty name').trim().isLength({ min: 1 }).escape(),
    
    								
    This test checks that the age field is a valid date and uses optional() to specify that null and empty strings will not fail validation.
    body('age', 'Invalid age').optional({ checkFalsy: true }).isISO8601().toDate(),
    
    								
    You can also daisy chain different validators, and add messages that are displayed if the preceding validators are true.
    body('name').trim().isLength({ min: 1 }).withMessage('Name empty.')
        .isAlpha().withMessage('Name must be alphabet letters.'),
    
    								
  • validationResult(req) : Runs the validation, making errors available in the form of a validation result object. This is invoked in a separate callback, as shown below:
    (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.
        }
    }
    
    								
    We use the validation result's isEmpty() method to check if there were errors, and its array() method to get the set of error messages. See the Validation Result API 了解更多信息。

The validation and sanitization chains are middleware that should be passed to the Express route handler (we do this indirectly, via the controller). When the middleware runs, each validator/sanitizer is run in the order specified.

We'll cover some real examples when we implement the LocalLibrary forms below.

Form design

Many of the models in the library are related/dependent—for example, a Book requires an 作者 ,和 may also have one or more Genres . This raises the question of how we should handle the case where a user wishes to:

  • Create an object when its related objects do not yet exist (for example, a book where the author object hasn't been defined).
  • Delete an object that is still being used by another object (so for example, deleting a Genre that is still being used by a Book ).

For this project we will simplify the implementation by stating that a form can only:

  • Create an object using objects that already exist (so users will have to create any required 作者 and Genre instances before attempting to create any Book 对象)。
  • Delete an object if it is not referenced by other objects (so for example, you won't be able to delete a Book until all associated BookInstance objects have been deleted).

注意: A more "robust" implementation might allow you to create the dependent objects when creating a new object, and delete any object at any time (for example, by deleting dependent objects, or by removing references to the deleted object from the database).

Routes

In order to implement our form handling code, we will need two routes that have the same URL pattern. The first ( GET ) route is used to display a new empty form for creating the object. The second route ( POST ) is used for validating data entered by the user, and then saving the information and redirecting to the detail page (if the data is valid) or redisplaying the form with errors (if the data is invalid).

We have already created the routes for all our model's create pages in /routes/catalog.js (in a previous tutorial ). For example, the genre routes are shown below:

// 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);

						

Express forms subarticles

The following sub articles will take us through the process of adding the required forms to our example application. You need to read and work through each one in turn, before moving on to the next one.

  1. Create Genre form — Defining a page to create Genre 对象。
  2. Create Author form — Defining a page to create 作者 对象。
  3. Create Book form — Defining a page/form to create Book 对象。
  4. Create BookInstance form — Defining a page/form to create BookInstance 对象。
  5. Delete Author form — Defining a page to delete 作者 对象。
  6. Update Book form — Defining page to update Book 对象。

Challenge yourself

Implement the delete pages for the Book , BookInstance ,和 Genre models, linking them from the associated detail pages in the same way as our Author delete page. The pages should follow the same design approach:

  • If there are references to the object from other objects, then these other objects should be displayed along with a note that this record can't be deleted until the listed objects have been deleted.
  • If there are no other references to the object then the view should prompt to delete it. If the user presses the 删除 button, the record should then be deleted.

A few tips:

  • 删除 Genre is just like deleting an 作者 as both objects are dependencies of Book (so in both cases you can delete the object only when the associated books are deleted).
  • 删除 Book is also similar, but you need to check that there are no associated BookInstances .
  • 删除 BookInstance is the easiest of all because there are no dependent objects. In this case, you can just find the associated record and delete it.

Implement the update pages for the BookInstance , 作者 ,和 Genre models, linking them from the associated detail pages in the same way as our Book update 页面。

A few tips:

  • Book update page we just implemented is the hardest! The same patterns can be used for the update pages for the other objects.
  • 作者 date of death and date of birth fields and the BookInstance due_date field are the wrong format to input into the date input field on the form (it requires data in form "YYYY-MM-DD"). The easiest way to get around this is to define a new virtual property for the dates that formats the dates appropriately, and then use this field in the associated view templates.
  • If you get stuck, there are examples of the update pages in the example here .

摘要

Express , node, and third-party packages on NPM provide everything you need to add forms to your website. In this article, you've learned how to create forms using Pug , validate and sanitize input using express-validator , and add, delete, and modify records in the database.

You should now understand how to add basic forms and form-handling code to your own node websites!

另请参阅

In this module

发现此页面有问题吗?

最后修改: , 由 MDN 贡献者

  1. Complete beginners start here!
  2. Web 快速入门
    1. Getting started with the Web overview
    2. 安装基本软件
    3. What will your website look like?
    4. 处理文件
    5. HTML 基础
    6. CSS 基础
    7. JavaScript 基础
    8. 发布您的网站
    9. How the Web works
  3. HTML — Structuring the Web
  4. HTML 介绍
    1. Introduction to HTML overview
    2. Getting started with HTML
    3. What's in the head? Metadata in HTML
    4. HTML text fundamentals
    5. Creating hyperlinks
    6. Advanced text formatting
    7. Document and website structure
    8. Debugging HTML
    9. Assessment: Marking up a letter
    10. Assessment: Structuring a page of content
  5. 多媒体和嵌入
    1. Multimedia and embedding overview
    2. Images in HTML
    3. Video and audio content
    4. From object to iframe — other embedding technologies
    5. Adding vector graphics to the Web
    6. Responsive images
    7. Assessment: Mozilla splash page
  6. HTML 表格
    1. HTML tables overview
    2. HTML table basics
    3. HTML Table advanced features and accessibility
    4. Assessment: Structuring planet data
  7. CSS — Styling the Web
  8. CSS 第一步
    1. CSS first steps overview
    2. What is CSS?
    3. Getting started with CSS
    4. How CSS is structured
    5. How CSS works
    6. Using your new knowledge
  9. CSS 构建块
    1. CSS building blocks overview
    2. Cascade and inheritance
    3. CSS 选择器
    4. The box model
    5. Backgrounds and borders
    6. Handling different text directions
    7. Overflowing content
    8. Values and units
    9. Sizing items in CSS
    10. Images, media, and form elements
    11. Styling tables
    12. Debugging CSS
    13. Organizing your CSS
  10. 样式化文本
    1. Styling text overview
    2. Fundamental text and font styling
    3. Styling lists
    4. Styling links
    5. Web fonts
    6. Assessment: Typesetting a community school homepage
  11. CSS 布局
    1. CSS layout overview
    2. Introduction to CSS layout
    3. Normal Flow
    4. Flexbox
    5. Grids
    6. Floats
    7. 位置
    8. Multiple-column Layout
    9. Responsive design
    10. Beginner's guide to media queries
    11. Legacy Layout Methods
    12. Supporting Older Browsers
    13. Fundamental Layout Comprehension
  12. JavaScript — Dynamic client-side scripting
  13. JavaScript 第一步
    1. JavaScript first steps overview
    2. What is JavaScript?
    3. A first splash into JavaScript
    4. What went wrong? Troubleshooting JavaScript
    5. Storing the information you need — Variables
    6. Basic math in JavaScript — Numbers and operators
    7. Handling text — Strings in JavaScript
    8. Useful string methods
    9. 数组
    10. Assessment: Silly story generator
  14. JavaScript 构建块
    1. JavaScript building blocks overview
    2. Making decisions in your code — Conditionals
    3. Looping code
    4. Functions — Reusable blocks of code
    5. Build your own function
    6. Function return values
    7. 事件介绍
    8. Assessment: Image gallery
  15. 引入 JavaScript 对象
    1. Introducing JavaScript objects overview
    2. Object basics
    3. 对象原型
    4. Object-oriented programming concepts
    5. Classes in JavaScript
    6. Working with JSON data
    7. Object building practice
    8. Assessment: Adding features to our bouncing balls demo
  16. 异步 JavaScript
    1. Asynchronous JavaScript overview
    2. General asynchronous programming concepts
    3. Introducing asynchronous JavaScript
    4. Cooperative asynchronous Java​Script: Timeouts and intervals
    5. Graceful asynchronous programming with Promises
    6. Making asynchronous programming easier with async and await
    7. Choosing the right approach
  17. 客户端侧 Web API
    1. 客户端侧 Web API
    2. Introduction to web APIs
    3. Manipulating documents
    4. Fetching data from the server
    5. Third party APIs
    6. Drawing graphics
    7. Video and audio APIs
    8. Client-side storage
  18. Web forms — Working with user data
  19. Core forms learning pathway
    1. Web forms overview
    2. Your first form
    3. How to structure a web form
    4. Basic native form controls
    5. The HTML5 input types
    6. Other form controls
    7. Styling web forms
    8. Advanced form styling
    9. UI pseudo-classes
    10. Client-side form validation
    11. Sending form data
  20. Advanced forms articles
    1. How to build custom form controls
    2. Sending forms through JavaScript
    3. CSS property compatibility table for form controls
  21. Accessibility — Make the web usable by everyone
  22. Accessibility guides
    1. Accessibility overview
    2. What is accessibility?
    3. HTML: A good basis for accessibility
    4. CSS and JavaScript accessibility best practices
    5. WAI-ARIA basics
    6. Accessible multimedia
    7. Mobile accessibility
  23. Accessibility assessment
    1. Assessment: Accessibility troubleshooting
  24. Tools and testing
  25. Client-side web development tools
    1. Client-side web development tools index
    2. Client-side tooling overview
    3. Command line crash course
    4. Package management basics
    5. Introducing a complete toolchain
    6. Deploying our app
  26. Introduction to client-side frameworks
    1. Client-side frameworks overview
    2. Framework main features
  27. React
    1. Getting started with React
    2. Beginning our React todo list
    3. Componentizing our React app
    4. React interactivity: Events and state
    5. React interactivity: Editing, filtering, conditional rendering
    6. Accessibility in React
    7. React resources
  28. Ember
    1. Getting started with Ember
    2. Ember app structure and componentization
    3. Ember interactivity: Events, classes and state
    4. Ember Interactivity: Footer functionality, conditional rendering
    5. Routing in Ember
    6. Ember resources and troubleshooting
  29. Vue
    1. Getting started with Vue
    2. Creating our first Vue component
    3. Rendering a list of Vue components
    4. Adding a new todo form: Vue events, methods, and models
    5. Styling Vue components with CSS
    6. Using Vue computed properties
    7. Vue conditional rendering: editing existing todos
    8. Focus management with Vue refs
    9. Vue resources
  30. Svelte
    1. Getting started with Svelte
    2. Starting our Svelte Todo list app
    3. Dynamic behavior in Svelte: working with variables and props
    4. Componentizing our Svelte app
    5. Advanced Svelte: Reactivity, lifecycle, accessibility
    6. Working with Svelte stores
    7. TypeScript support in Svelte
    8. Deployment and next steps
  31. Angular
    1. Getting started with Angular
    2. Beginning our Angular todo list app
    3. Styling our Angular app
    4. Creating an item component
    5. Filtering our to-do items
    6. Building Angular applications and further resources
  32. Git and GitHub
    1. Git and GitHub overview
    2. Hello World
    3. Git Handbook
    4. Forking Projects
    5. About pull requests
    6. Mastering Issues
  33. Cross browser testing
    1. Cross browser testing overview
    2. Introduction to cross browser testing
    3. Strategies for carrying out testing
    4. Handling common HTML and CSS problems
    5. Handling common JavaScript problems
    6. Handling common accessibility problems
    7. Implementing feature detection
    8. Introduction to automated testing
    9. Setting up your own test automation environment
  34. Server-side website programming
  35. 第一步
    1. First steps overview
    2. Introduction to the server-side
    3. Client-Server overview
    4. Server-side web frameworks
    5. Website security
  36. Django Web 框架 (Python)
    1. Django web framework (Python) overview
    2. 介绍
    3. 设置开发环境
    4. Tutorial: The Local Library website
    5. Tutorial Part 2: Creating a skeleton website
    6. Tutorial Part 3: Using models
    7. Tutorial Part 4: Django admin site
    8. Tutorial Part 5: Creating our home page
    9. Tutorial Part 6: Generic list and detail views
    10. Tutorial Part 7: Sessions framework
    11. Tutorial Part 8: User authentication and permissions
    12. Tutorial Part 9: Working with forms
    13. Tutorial Part 10: Testing a Django web application
    14. Tutorial Part 11: Deploying Django to production
    15. Web application security
    16. Assessment: DIY mini blog
  37. Express Web Framework (node.js/JavaScript)
    1. Express Web Framework (Node.js/JavaScript) overview
    2. Express/Node introduction
    3. Setting up a Node (Express) development environment
    4. Express tutorial: The Local Library website
    5. Express Tutorial Part 2: Creating a skeleton website
    6. Express Tutorial Part 3: Using a database (with Mongoose)
    7. Express Tutorial Part 4: Routes and controllers
    8. Express Tutorial Part 5: Displaying library data
    9. Express Tutorial Part 6: Working with forms
    10. Express Tutorial Part 7: Deploying to production
  38. Further resources
  39. Common questions
    1. HTML questions
    2. CSS questions
    3. JavaScript questions
    4. Web mechanics
    5. Tools and setup
    6. Design and accessibility