Build from scratch Node.js Enterprise web server (2): Verification

Time:2021-3-3

Verification in ideal

Verification is the restriction of input conditions to avoid invalid input causing exception. The user input of web system is mainly to edit and submit various forms. On the one hand, the verification should be done when editing the form fields and submitting them. On the other hand, the interface receiving the form should also do enough verification behavior. The input conditions are jointly controlled by the front and back ends. However, when the current verification is more strict than the back-end verification, it will directly raise the threshold for users to edit information. On the contrary, when the back-end verification is more strict than the front-end verification, it will make the form hard to fill out still unable to be submitted smoothly. These two situations will seriously damage the confidence of users, and the key lies in the consistency of verification rules.

Build from scratch Node.js  Enterprise web server (2): Verification

Select calibration module

Based on the above considerations, the expected verification module should have the following characteristics:

  1. Logic can be reused across terminals
  2. Compact, Limited package size
  3. Semantic clarity
  4. Comprehensive function
  5. Stable enough

After a comprehensive comparison, chooseyupAs a verification module, now the above chapter has completed the projecthost1-tech/nodejs-server-examples – 01-api-and-layeringStart the transformation and install yup in the root directory of the project

$yarn add yup # local installation yup
# ...
info Direct dependencies
└─ [email protected]
# ...

Add back-end verification

Careful readers will find that the current store management function has no restrictions on input. For example, if the store name is set to be empty, it will be submitted successfully. Now we add back-end verification to make up for this deficiency

$MKDIR Src / molds # new Src / molds directory to store verification schema

$ tree -L 2 -I node_ Modules # display except node_ Directory content structure outside modules
.
├── Dockerfile
├── package.json
├── public
│   ├── index.html
│   └── index.js
├── src
│   ├── controllers
│   ├── moulds
│   ├── server.js
│   └── services
└── yarn.lock
// src/moulds/ShopForm.js
const Yup = require('yup');

exports.createShopFormSchema = () =>
  Yup.object({
    name: Yup.string()
      . required ('store name cannot be empty ')
      . min (3, 'store name at least 3 characters')
      . max (20, 'store name can't exceed 20 characters'),
  });
// src/controllers/shop.js
const { Router } = require('express');
const shopService = require('../services/shop');
+const { createShopFormSchema } = require('../moulds/ShopForm');

class ShopController {
  // ...
  put = async (req, res) => {
    const { shopId } = req.params;
    const { name } = req.query;
+
+    try {
+      await createShopFormSchema().validate({ name });
+    } catch (e) {
+      res.status(400).send({ success: false, message: e.message });
+      return;
+    }
+
    const shopInfo = await this.shopService.modify({
      id: shopId,
      values: { name },
    });

    if (shopInfo) {
      res.send({ success: true, data: shopInfo });
    } else {
      res.status(404).send({ success: false, data: null });
    }
  };
  // ...
}

module.exports = async () => {
  const c = new ShopController();
  return await c.init();
};

In this way, the nonstandard input is effectively prevented, and the effect is as follows:

Build from scratch Node.js  Enterprise web server (2): Verification

Plus front end verification

Now the front-end also adds verification to provide error information for usersrollupMove yup to browser:

$yarn add - D rollup @ rollup / plugin node resolve @ rollup / plugin commonjs rollup plugin terser # local installation of rollup and its plug-ins
# ...
info Direct dependencies
├─ @rollup/[email protected]
├─ @rollup/[email protected]
├─ [email protected]
└─ [email protected]
# ...
// package.json
{
  "name": "02-validate",
  "version": "1.0.0",
  "scripts": {
-    "start": "node src/server.js"
+    "start": "node src/server.js",
+    "build:yup": "rollup node_modules/yup -o src/moulds/yup.js -p @rollup/plugin-node-resolve,@rollup/plugin-commonjs,rollup-plugin-terser -f umd -n 'yup'"
  }
  // ...
}
$ yarn build:yup
# ...
created src/moulds/yup.js in 1.9s

Then the front-end verification logic is supplemented

// src/server.js
const express = require('express');
const { resolve } = require('path');
const { promisify } = require('util');
const initControllers = require('./controllers');

const server = express();
const port = parseInt(process.env.PORT || '9000');
const publicDir = resolve('public');
+const mouldsDir = resolve('src/moulds');

async function bootstrap() {
  server.use(express.static(publicDir));
+  server.use('/moulds', express.static(mouldsDir));
  server.use(await initControllers());
  await promisify(server.listen.bind(server, port))();
  console.log(`> Started on port ${port}`);
}

bootstrap();
// public/glue.js
import './moulds/yup.js';

window.require = (k) => window[k];
window.exports = window.moulds = {};
/* public/index.css */
.error {
  color: red;
  font-size: 14px;
}
<!-- public/index.html -->
<html>
  <head>
    <meta charset="utf-8" />
+    <link rel="stylesheet" href="./index.css" />
  </head>
  <body>
    <div id="root"></div>

    <script type="module">
+      import './glue.js';
      import { refreshShopList, bindShopInfoEvents } from './index.js';

      async function bootstrap() {
        await refreshShopList();
        await bindShopInfoEvents();
      }

      bootstrap();
    </script>
  </body>
</html>
// public/index.js
+import './moulds/ShopForm.js';
+const { createShopFormSchema } = window.moulds;
+
export async function refreshShopList() {
  const res = await fetch('/api/shop');
  const { data: shopList } = await res.json();
  const htmlItems = shopList.map(
    ({ id, name }) => `
<li data-shop-id="${id}">
  <div data-type="text">${name}</div>
  < input type = "text" placeholder = "enter new store name" / >
  To confirm the modification</a>
  To delete a store</a>
+  <div class="error"></div>
</li>`
  );
  document.querySelector('#root').innerHTML = `
<h1>Shop list:</h1>
<ul class="shop-list">${htmlItems.join('')}</ul>`;
}

// ...

export async function modifyShopInfo(e) {
  const shopId = e.target.parentElement.dataset.shopId;
  const name = e.target.parentElement.querySelector('input').value;
+
+  try {
+    await createShopFormSchema().validate({ name });
+  } catch ({ message }) {
+    e.target.parentElement.querySelector('.error').innerHTML = message;
+    return;
+  }
+
  await fetch(`/api/shop/${shopId}?name=${encodeURIComponent(name)}`, {
    method: 'PUT',
  });
  await refreshShopList();
}

Take a look at the effect:

Build from scratch Node.js  Enterprise web server (2): Verification

Source code of this chapter

host1-tech/nodejs-server-examples – 02-validate

Read more

Build from scratch Node.js Enterprise web server (zero): static services
Build from scratch Node.js Enterprise web server (1): interface and layering
Build from scratch Node.js Enterprise web server (2): Verification
Build from scratch Node.js Enterprise web server (3): Middleware
Build from scratch Node.js Enterprise web server (4): exception handling
Build from scratch Node.js Enterprise web server (5): database access
Build from scratch Node.js Enterprise web server (6): session
Build from scratch Node.js Enterprise web server (7): authentication login
Build from scratch Node.js Enterprise web server (8): network security
Build from scratch Node.js Enterprise web server (9): configuration items
Build from scratch Node.js Enterprise web server (x): log
Build from scratch Node.js Enterprise web server (11): timed tasks
Build from scratch Node.js Enterprise web server (12): remote call
Build from scratch Node.js Enterprise web server (XIII): breakpoint debugging and performance analysis
Build from scratch Node.js Enterprise web server (14): automated testing
Build from scratch Node.js Enterprise web server (XV): summary and Prospect