Introduction
Node.js enables JavaScript\TypeScript scripts to run on the server (backend) without the browser environment (actually encapsulating the Chrome V8 engine), providing an option for us to develop the backend. Unlike the front-end, which has a unified browser standard, if you don't follow it, the browser will not be able to display properly; the development of the back-end is relatively free, and there are many options for development languages, such as Java, PHP, python, C, Go, etc. Our technology The Xie website uses the TypeScript language, and knowledge about TypeScript has been introduced in previous courses.
If you want to build a simple backend yourself, in theory you can receive and send http requests by simply calling a library such as http-server. Although these libraries have helped us deal with many low-level problems, it is still quite troublesome to implement more complex functions. Express is an open source framework based on Node.js, which encapsulates receiving and sending http requests at a higher level and more user-friendly. The official website calls it "fast, open, and minimalist", which shows that while Express ensures performance, it is very easy to write code and is open source. It enables the creation of powerful APIs quickly and easily using a variety of http utilities and middleware of the developer's choice.
Configuration Environment
Install express
Use yarn to install, as follows
yarn init#取号名字,一路默认
yarn add express
Preparation
In the working directory where Express is installed just created, we create a new folder src to save the source code, and create a new entry file app.ts in it. Then import the Express package and create an instance of the Express class in it:
import express from "express"; // 导入Express包
const app = express(); // 创建Express类的实例
The Express package imported in this way is written in JavaScript and lacks the type declaration file required by TypeScript syntax, so a reminder will appear on the import line. We can install type declarations using the following command:
yarn add @types/express --dev
After the installation is complete, we see that the reminder has been dismissed.
Then we let the program listen to port 3000, so that requests sent to http://localhost:3000 can be captured by us.
const port = 3000
app.listen(port, () => {
// 开始监听
console.log(`listening on port ${
port}`);
});
The question is, how can we run the backend? We can use babel
First install the tools
yarn add @babel/core @babel/cli @babel/preset-env @babel/preset-typescript @babel/node nodemon --dev
Second, we configure Babel. Create a new babel.config.json file in the working directory and write it (remove comments):
{
"presets": [
// 指定要使用的工具集。更多配置详见官方文档:https://www.babeljs.cn/docs/
"@babel/preset-env",
"@babel/preset-typescript"
]
}
Then, we configure nodemon. Open the package.json file in the working directory and create a new field as follows (remove the comments):
"nodemonConfig": {
// 更多配置详见官方文档:https://www.npmjs.com/package/nodemon
"watch": [ // 监听的文件目录
"src"
],
"ext": "ts, json", // 监听的文件后缀
"exec": "babel-node --extensions \".ts\" src/app.ts" // 当运行nodemon命令时执行
// babel-node与node的区别在于应用了babel.config.json中配置的工具,src/app.ts是你的入口文件
}
Finally, we set up yarn's execution script to simply run the programs in node_modules. Create a new field in the package.json file as follows (remove comments):
"scripts": {
"start": "nodemon" // 等价于./node_modules/nodemon/bin/nodemon.js
}
In this way, as long as we enter yarn start in the terminal, we will see that nodemon starts and uses Babel to generate and run JavaScript code. At this time, entering rs and pressing Enter will cause the backend to restart; pressing Ctrl+C will cause the backend to stop.
Postman
Install
Postman is an API debugging tool that can simulate the front-end sending requests to the back-end, which is very useful when we develop the back-end. You can go to its official website to register an account (the advantage is that your HTTP requests will be backed up and synchronized on different devices) https://www.postman.com/, and then click on the download desktop app related options or directly link to https://www .postman.com/downloads/, download the installer and install it.
After opening Postman and logging in, you can click the plus sign in Collections to create a new Collection, and then click the plus sign in the upper bar to create a new Request and save it to this Collection. This way you can edit, save and send the Request. Requests for the same type of API can be saved under the same Collection to facilitate management. It is recommended that you read the official Postman documentation (https://learning.postman.com/docs/getting-started/introduction/) to learn more practical knowledge about Postman.
use
Create a new request and fill in the local ip:port
http://localhost:3000/
Then you can send a get request and get an error, because we have not yet routed to process this request. The specific usage of routing will be introduced later.
HTTP
Basic structure of HTTP request
To understand how Express is routed, it is necessary to first understand the format and structure of HTTP requests. HTTP (Hypertext Transfer Protocol) is based on the TCP/IP protocol to transfer data and is designed for communication between clients and servers. The format of the client request is as follows:
The format of the server response is as follows:
Express routes based on the HTTP request method and URI/URL. The API written by the developer processes the request and sends the status code and other response content back to the client. Let’s first understand the HTTP request method:
serial number | method | describe |
---|---|---|
1 | GET | Make a request to the specified resource to obtain the resource, and the data is included in the URL |
2 | HEAD | It is equivalent to sending a GET request to the server, but does not obtain the response body, only the response header. |
3 | POST | Submit data to the specified resource to process the request (such as submitting a form or uploading a file), and the data is included in the request body |
4 | PUT | Upload its latest content to the designated resource |
5 | DELETE | Request the server to delete the resource identified by Request-URL |
6 | CONNECT | The HTTP/1.1 protocol is reserved for proxy servers that can change connections to pipes. |
7 | OPTIONS | Returns the server's support for a specific resource, allowing clients to view the server's performance |
8 | TRACE | Echo requests received by the server, mainly used for testing or diagnostics |
9 | PATCH | It is a supplement to the PUT method and is used to locally update known resources. |
Generally, we commonly use GET, POST and PUT requests. Next, let’s take a look at the HTTP response status codes. The following table lists some status codes and descriptions that may be encountered:
status code | English description | Chinese description |
---|---|---|
200 | OK | Request successful |
400 | Bad Request | The syntax of the client request is incorrect and the server cannot understand it. |
401 | Unauthorized | The identity authentication requested by the client is missing or incorrect, and the authentication fails. |
403 | Forbidden | The server understands the client's request, but refuses to execute it |
404 | Not Found | The server cannot find the resource (webpage) based on the client's request |
500 | Internal Server Error | Internal server error, unable to complete the request |
501 | Not Implemented | The server does not support the requested feature and cannot complete the request |
502 | Bad Gateway | A server working as a gateway or proxy received an invalid response from the remote server while trying to perform a request. |
HTTP URL
URL = uniform resource locator, Uniform Resource Locator, is a method of identifying addresses on the network. Specifically in the application of HTTP protocol, the form is as follows:
http://[host]:[port]/[path]?[searchpart]
Among them, <port>
the default is 80 (default is 443 for HTTPS protocol), <path>
is an HTTP selector, <searchpart>
and is the query string. For example:
The protocol is https
, the host address is baike.baidu.com
(will be resolved into an IP address by DNS), and the default 443
port is used; the path is /item/%E7%BB%9F.../5937042
, with path parameters /%E7%BB%9F.../5937042
; the query string is fromtitle=url&fromid=110640
.
routing
Introduction
The process of parsing URLs is routing. We implement the response to front-end requests through the process of matching URLs.
Basic usage
The basic format of a route is:
app.METHOD(PATH, HANDLER);
in:
app
Isexpress
an instance of class.METHOD
Is the HTTP request method.PATH
is the path on the server.HANDLER
Is a function executed when a route is matched.
Note that the number of parameters here is not fixed. There will be 3 parameters and 4 parameters in the subsequent usage.
For example:
// “/”路径接收到的GET方法匹配至该路由
app.get("/", (req, res) => {
res.status(200).send("GET request");
});
// “/”路径接收到的POST方法匹配至该路由
app.post("/", (req, res) => {
res.status(200).send("POST request");
});
// “/”路径接收到的所有方法匹配至该路由
app.all("/", (req, res) => {
res.status(200).send("Any request");
});
For the Handler function, Express will call it when the route matches. There are three parameters passed in when calling (generally named req, res, next):
- The req object represents "request", and commonly used attributes are body (requested package body), query (requested query string) and params (requested path parameters);
- The res object represents "response", and the commonly used methods are status (setting the response status code) and send (setting the response content);
- When the next function is called, it means handing over to the next middleware for processing.
match path
Matching paths can be ordinary strings, string templates, and regular expressions.
Ordinary strings such as:
app.get("/", (req, res) => {
// 可匹配“/”
res.send("root");
});
app.get("/about", (req, res) => {
// 可匹配“/about”
res.send("about");
});
app.get("/random.text", (req, res) => {
// 可匹配“/random.text”
res.send("random.text");
});
String template such as:
app.get("/ab?cd", (req, res) => {
// 可匹配“acd”或“abcd”
res.send("ab?cd");
});
app.get("/ab+cd", (req, res) => {
// 可匹配“abcd”,“abbcd”,“abbbcd”等
res.send("ab+cd");
});
app.get("/ab*cd", (req, res) => {
// 可匹配“abcd”,“abxcd”,“abRANDOMcd”,“ab123cd”等
res.send("ab*cd");
});
app.get("/ab(cd)?e", (req, res) => {
// 可匹配“abe”或“abcde”
res.send("ab(cd)?e");
});
Regular expressions such as:
app.get(/a/, (req, res) => {
// 可匹配任何带有‘a’的路径
res.send("/a/");
});
app.get(/.*fly$/, (req, res) => {
// 可匹配“butterfly”,“dragonfly”
res.send("/.*fly$/"); // 但不可匹配“butterflyman”,“dragonflyman”
}); // 相当于匹配以“fly”结尾的所有路径
query string
The parameter in the path (route parameter) refers to the method of passing parameters directly as part of the URL path, which is our previous path
The query string is the part of the URL that is separated by ? after the path. Although both of them pass parameters in the URL, their usage is different.
Still taking the example of the above URL: https://baike.baidu.com/item/%E7%BB%9F%E4%B8%80%E8%B5%84%E6%BA%90%E5%AE%9A% E4%BD%8D%E7%B3%BB%E7%BB%9F/5937042?fromtitle=url&fromid=110640. It can be resolved through the following routes:
app.get("/item/:param1/:param2", (req, res) => {
console.log(req.params.param1); // 统一资源定位系统,中间那段%unicode编码的翻译
console.log(req.params.param2); // 5937042
console.log(req.query.fromtitle); // url
console.log(req.query.fromid); // 110640
res.status(200).send("ok");
});
-
In addition, there are some advanced usages, such as inserting dashes ( ) and points ( ) in the path .
, and even introducing regular expressions in the path:
// Request URL: http://localhost:3000/flights/LAX-SFO
app.get("/flights/:from-:to", (req, res) => {
console.log(req.params.from); // LAX
console.log(req.params.to); // SFO
res.status(200).send("ok");
});
// Request URL: http://localhost:3000/date/2020.2.1
app.get("/date/:year.:month.:day", (req, res) => {
console.log(req.params.year); // 2020
console.log(req.params.month); // 2
console.log(req.params.day); // 1
res.status(200).send("ok");
});
Route processing function
Express's route handler functions are very flexible and can be passed multiple functions, arrays of functions, or a mixture of the two. When it is necessary to transition from the previous processing function to the next processing function, the passed in next function needs to be called.
var cb0 = function (req, res, next) {
console.log("CB0");
next();
};
var cb1 = function (req, res, next) {
console.log("CB1");
next();
};
app.get(
"/example/d",
[cb0, cb1],
function (req, res, next) {
console.log("the response will be sent by the next function ...");
next();
},
(req, res) => {
res.send("Hello from D!");
}
);
Router
The express.Router class can be used to build modular routing processing functions (middleware)
We create a new route1.ts file under the src folder and write:
import express from "express";
const router = express.Router(); // 实例化express.Router类
router.get("/", (req, res) => {
res.status(200).send("ok!");
});
export default router; // 导出后,这个Router就成为了一个中间件
Then modify the app.ts class:
import express from "express";
import route1 from "./route1"; // 导入route1中间件
const app = express();
const port = 3000;
app.use("/route1", route1); // 在route1路径下使用route1中间件
app.listen(port, () => {
console.log(`Example app listening on port ${
port}`);
});
middleware
Middleware is a modular route processing function. We have just seen the use of route-level middleware (create middleware through express.Router, and then import middleware through app.use). In addition, in a broad sense, all routing processing just mentioned Functions can also be regarded as the most common middleware. There are also the following considerations when creating and using middleware:
The middleware loaded with app.use will be called regardless of the HTTP request method. If you want to limit an HTTP request method, you need to use app.METHOD. At the same time, if no path parameter is given, the middleware will be called by default for requests with any path.
Middleware is loaded and executed in order. If a matching route has already appeared before, then even if the subsequent route matches, the corresponding middleware will not be called. The next function will transfer control to the next processing function of the current route by default. If you want to transfer control to the next route, you need to call next('route'). If non-"route" parameters are passed to the next function, Express will think that the middleware has an error and directly execute the exception handling function, bypassing other normal handling functions.
app.get(
"/user/:id",
(req, res, next) => {
// if the user ID is 0, skip to the next route
if (req.params.id === "0") next("route");
// otherwise pass the control to the next middleware function in this stack
else next();
},
(req, res, next) => {
// send a regular response
res.send("regular");
}
);
// handler for the /user/:id path, which sends a special response
app.get("/user/:id", (req, res, next) => {
res.send("special");
});
Exception handling
For synchronous programming middleware, when an exception is thrown and there is no self-built exception handling function, Express will handle the exception by itself. Express will set status codes, error messages, etc. based on the error err.status
(or ). err.statusCode
We can also create an exception handling function ourselves. If four parameters are passed to the Express middleware, usually named (err, req, res, next), Express will treat this middleware as an exception handling function.
app.get("/", (req, res) => {
throw new Error("error occurs!"); // 异常由Express自建异常处理函数来处理
});
app.use((err, req, res, next) => {
res.status(500).send("error occurs!"); // 或者可以自己编写异常处理函数
});
For asynchronous programming middleware, to handle exceptions, you need to call the next function and pass a value to it. If the middleware returns a Promise, next(value) will be called automatically. The following asynchronous function returns Promise. If an exception or reject is thrown in the middle, Express will call the next function by default and pass in the exception information.
app.get("/user/:id", async (req, res, next) => {
var user = await getUserById(req.params.id);
res.send(user);
});
More often, we handle exceptions inside middleware instead of passing them to Express:
app.get("/", async (req, res) => {
try{
...
return res.status(200).send("ok");
} catch(err) {
...
return res.status(500).send("Internal Server Error");
}
})
Commonly used middleware
Express provides some built-in middleware for users to choose from. For example, the commonly used express.json will parse the request body in json format, and express.urlencoded will parse the request body in urlencoded format. There are also some third-party middleware that can be installed through the package manager, such as cookie-parser.