Node.js (II. Request response principle and HTTP protocol)

Posted by balkar on Mon, 14 Feb 2022 17:02:16 +0100

1. Server side basic concepts

1.1 IP address

Unique identification of the device in the Internet. IP is the abbreviation of Internet Protocol Address, which represents the Internet Protocol Address.

1.2 domain name

Because the IP address is difficult to remember, the concept of domain name came into being. The so-called domain name is the website used for surfing the Internet at ordinary times.
Although the URL is entered in the address bar, the domain name will eventually be converted to ip to access the specified website server. For example: https://www.lgg.com => https://127.0.0/ .

1.3 port

The port is the outlet of communication between the computer and the outside world. It is used to distinguish different services provided by the server computer.

1.4,URL

Uniform Resource Locator, also known as URL (Uniform Resource Locator), is an addressing method specially designed to identify the location of resources on the Internet. What we usually call web page address refers to URL.
Composition of URL:
Transport protocol: / / server IP or domain name: port / resource location ID. (e.g.: https://www.lgg.com/newsList/lgg.html )
http: Hypertext Transfer Protocol, which provides a method to publish and accept HTML pages.

2. Create web server

⚠️ Note: the whole code cannot run normally after being copied. It needs to be split and processed as required.

// Reference system module
const http = require('http');
// get request parameter acquisition requires the help of the system module url to process the url address
const url = require('url');
// The post request parameter acquisition needs to import the system module querystring, which is used to convert HTTP parameters into object format
const queryString = require('querystring');
// Create web server
const app = http.createServer();
//When the client sends a request
app.on('request', (req, res) => {
    // 1. Get request method req method
    if(req.method == 'POST') {
        res.end('I am POST Request.')
    } else if(req.method == ''GET) {
        res.end('I am GET Request.')
    }
    // 2. Get request address req url
    if(req.url == '/index' || req.url == '/') {
        res.end('I'm the home page.')
    } else {
        res.end('Content not found, please re-enter.')
    }
    // 3. Response res.end (` < H1 > Hello, LGG! < / H1 > `)
    // 4. Get request message information req headers['accept']
    // 5. Set response message
    /*Format:
    res.writeHead('Response status code ', {' content type '})
    Response status code: 200 successful request, 300 redirection, 400 client error, 500 server error, etc.
    Content type: 'text/html', 'text/css',' application/javascript ',' image/jpeg ',' application/json ', etc.
    Attachment: 'content type': 'text / HTML; charset=utf8';  It can solve the problem of Chinese garbled code. */
    res.writeHead(200, {
        'content-type': 'text/html;charset=utf8'; // It can solve the problem of Chinese garbled code.
    })
    // 6. get request parameter acquisition.
    /*GET The request parameters are placed in the browser address bar (POST is transmitted in the request body, i.e. message), for example: http://localhost:3000/?age=25 . 
    Parameter acquisition requires the system module url to process the url address:
    (1),The first parameter is the URL address to resolve.
    (2),The second parameter is to parse the query parameters into object form: URL parse(res.url, true). query
    */
    let value = url.parse(res.url, true)
    // url is http://localhost:3000/?age=25
    console.log(res.url) // /?age=25
    console.log(value) /*
Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?age=25',
  query: [Object: null prototype] { age: '25' },
  pathname: '/',
  path: '/?age=25',
  href: '/?age=25'
}
*/
    console.log(value.query) // [Object: null prototype] { age: '25' }
});
// 7. post request processing request parameters: import the system module querystring, which is used to convert HTTP parameters into object format.
/*post Parameters are accepted through events.
Parameters are placed in the request body (i.e. message) for transmission.
To get the post parameter, you need to use the data event and the end event.
Use the querystring system module to convert parameters to object format.
data The data event is triggered when the request parameter is passed.
end The end event is triggered when the parameter transfer is completed.*/
let postParams = '';
// Listen for parameter transmission events
req.on('data', params => {
    postParams += params;
})
// Event triggered after listening for parameter transmission
req.on('end', () => {
    // Convert string to object
    let { user, password } = queryString.parse(postParams);
})
res.end('ok')

// Listening port
app.listen(3000)

3. HTTP protocol

3.1 concept of HTTP protocol

HyperText Transfer Protocol (English: HyperText Transfer Protocol, abbreviation: HTTP) specifies how to transfer hypertext from the website server to the local browser. It works based on the client server architecture and is the standard for requests and responses from the client (user) and server (website).

3.2 message

The data block transmitted in the process of HTTP request and response is called message, including the data to be transmitted and some additional information, and shall comply with the specified format.

3.2.1 request message

1. Request Method

  • GET request data
  • POST send data
    2. Request address (Request URL)
app.on('request', (req, res) => {
    req.headers // Get request message
    req.url // Get request address
    req.method // Request method
})

3.2.2 response message

1. HTTP status code
200 successful request, 300 redirection, 400 client error, 500 server error.
2. Set response message

app.on('request', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'Content type'
    })
})
3,Content type( Content-Type)
* text/html
* text/css
* application/javascript
* image/jpeg
* application/json
* ···
* Attachment: text/html;charset=utf8;It can solve the problem of Chinese garbled code.

4. HTTP request and response processing

4.1 GET request parameters

  • Parameters are placed in the address bar, such as: http://localhost:3000/?name=lgg
  • Parameter acquisition requires the help of system module url.
const http = require('http');
const url = require('url');
const app = http.createServer();
app.on('request', (req, res) => {
    // true means to resolve the parameter to object format
    let { query } = url.parse(req.url, true);
});
app.listen(3000);

4.2 POST request parameters

  • Parameters are placed in the request body for transmission.
  • data and end events are required to obtain POST parameters.
  • You need to use the system module querystring to convert parameters to object format.
···
const querystring = require('querystring');
···
app.on('request', (req, res) => {
    let postParams = '';
    app.on('data', params => {
        postParams += params;
    })
    app.on('end', () => {
        let { 'parameter', pathName } = querystring.parse(postParams )
        res.end(pathName )
    })
})

4.3 routing

Routing refers to the correspondence between the client request address and the server program code,

4.4 static resources

The server does not need to process. The resources that can respond directly to the client are static resources, such as css, js, pictures and other files. (a special folder is created on the server side to place static resources. When the client requests a file, it responds directly to the client, which is the static resource access function)

const http = require('http');
const url = require('url');
const path = require('path');

const fs = require('fs');
// The getType method in the third-party module mime is required
const mime = require('mime');

const app = http.createServer();
app.on('request', (req, res) => {
    let { pathname  } = url.parse(req.url);
    pathname = pathName = '/'? 'default.html' : pathname;
    // Convert the user's request path to the actual server hard disk path (such as user request) http://localhost:3000/default.html)
    let realPath = path.join(__dirname, 'public', pathname);
    let type = mime.getType(realPath);
    fs.readFile('realPath ', (err, result) => {
        if(err != null) {
            res.writeHead(404, {
                'Content-Type': 'text/html;charset=utf8'
            })
            res.end('Request failed!');
            return;
        }
        res.writeHead(200, {
            'Content-Type': type
        })
        res.end(result)
    })
})
app.listen(3000)

4.5 dynamic resources

The same request address (which can transmit different parameters) gets different response resources, which is dynamic resources. For example:
http://localhost:3000/news?id=1,
http://localhost:3000/news?id=2

4.6 front end request approach

4.6.1 GET mode

  • Browser address bar
  • href attribute of link tag
  • src attribute of script tag
  • src attribute of img
  • form submission
<link rel="stylesheet" href="base.css">
<script src="index.js"></script>
<img src="login.png">
<form action="example.cn" method='GET'></form>

4.6.2 POST mode

  • form submission
<form action="example.cn" method='POST'>

5,Node.js asynchronous programming

5.1. Synchronous API

Synchronous API: the next API can only be executed after the current API is executed. For example: console log(···)
Asynchronous API: the execution of the current API will not block the execution of subsequent code. Such as timer, etc.

5.2 difference between synchronous API and asynchronous API:

5.2.1. Get return value

The synchronous API can get the API execution result from the return value, but the asynchronous API cannot.

// synchronization
function sumNum(n1, n2) {
    return n1 + n2;
}
const num = sumNum(1, 2);
console.log(num); // 3
// asynchronous
function getMsg() {
    setTimeout(function() {
        return { msg: 'Hello world!'}
    }, 2000);
    // Because there is return undefined by default here;
}
const msg = getMsg();
console.log(msg) // undefined

1. Callback function
Define your own function and let others call it.

// getData function definition
function getData(callback) {}
// getData(() => {})

2. Get the result of asynchronous execution using the callback API

function getMsg(callback) {
    setTimeout(callback({msg: 'Hello world!'}), 2000)
}
getMsg(function(data) {
    console.log(data); // {msg: 'Hello world!'}
})

5.2.2 code execution sequence

The synchronization API is executed from top to bottom. The previous code will block the execution of the following code.
Asynchronous APIs do not wait for API execution to complete before executing code down.

console.log('begin');
setTimeout(() => { console.log('2s Code executed after')}, 2000)
setTimeout(() => { console.log('0s Code executed after')}, 0)
console.log('after')
// The execution sequence is: begin, after, 'code executed after 2s' and' code executed after 0s'

analysis:

  • Note: when executing code, Node JS does not execute from top to bottom, but executes all synchronous APIs first, and then executes asynchronous APIs

5.2.3,Node. Asynchronous API in JS

fs.readFile('./lgg.html', (err, result) => {})
var app = http.createServer();
app.on('request', (req, res) => {});
  • Event handling functions (req, res) = > {} we have not actively called them. The event handling functions are called by the system when the event occurs. The system passes us two objects, request object and response object. So when we define this function, we can accept these two parameters through formal parameters. Therefore, the event handling function is a callback function, and the event listening API is an asynchronous API.
  • If the execution of the code behind the asynchronous API depends on the execution result of the current asynchronous API, but in fact, the asynchronous API has not returned the result when the subsequent code is executed. What should I do?
    Requirements: read A file, B file and C file in turn.
    1. Nesting in turn, but there is a callback problem.
    Scheme I:
const fs = require('fs');

fs.readFile('./lgg1.txt', 'utf8', (err, result1) => {
	console.log(result1)
	fs.readFile('./lgg2.txt', 'utf8', (err, result2) => {
		console.log(result2)
		fs.readFile('./lgg3.txt', 'utf8', (err, result3) => {
			console.log(result3)
		})
	})
});

2,Promise
Promise appears to solve node JS asynchronous programming callback hell problem. (it is a constructor and needs to be created with new first. Promise is to wrap a layer outside our original asynchronous function. When the asynchronous API returns a result, it provides us with two methods: pass the result to the outside and reject)

  • When the resolve method is called on success, it is actually called Result = > console in then Log (result) method.
  • Calling the reject method when it fails is actually calling Error = > console in catch Log (error) method.
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        if(true) {
            resolve({msg: 'lgg'})
        } else {
            reject('err')
        }
    }, 2000)
})

promise
.then(result => console.log(result)); // {msg: 'lgg'}
.catch(error => console.log(error)) // err

Scheme II:

// As long as new is a promise, the code in it will be executed, so it should be put into the function
function lgg1() {
	return new Promise((resolve, reject) => {
		fs.writeFile('./lgg1.html', 'utf8', (err1, result1) => {
			if(err1 != null) {
				resolve(result1)
			} else {
				reject(err1)
			}
		})
	})
}
function lgg2() {
	return new Promise((resolve, reject) => {
		fs.writeFile('./lgg2.html', 'utf8', (err2, result2) => {
			if(err2 != null) {
				resolve(result2)
			} else {
				reject(err2)
			}
		})
	})
}
function lgg3() {
	return new Promise((resolve, reject) => {
		fs.writeFile('./lgg3.html', 'utf8', (err3, result3) => {
			if(err3 != null) {
				resolve(result3)
			} else {
				reject(err3)
			}
		})
	})
}
// The lgg1 function must return to use then
lgg1().then((result1) => {
	console.log(result1);
	return lgg2()
})
.then((result2) => {
	console.log(result2);
	return lgg3()
})
.then((result3) => {
	console.log(result3);
})

3. Asynchronous function
Asynchronous function is the ultimate solution of asynchronous programming syntax. It allows us to write asynchronous code in the form of synchronization, so that the code no longer has callback function nesting, making the code clearer.
3.1 async keyword
1. Add the async keyword before the definition of ordinary functions, and ordinary functions become asynchronous functions

async function fn() {}

2. Asynchronous functions return promise objects by default instead of undefined.

async function fn() {}
console.log(fn()) // Promise { undefined }

3. Use the return keyword inside the asynchronous function to return the result. The result will be wrapped in the promise object. The return keyword replaces the resolve method.

async function fn() {
    return 'lgg';
}
console.log(fn()) // Promise { 'lgg' }
fn().then((data) => {
    console.log(data) // 'lgg'
})

// Compared with promise, simplicity
function fn() {
    return new Promise() // This step is omitted (new a promise and then return)
}

In an asynchronous function, return can replace the resolve method, but what if an error occurs? How can I return the error message to the outside?
4. Throw a program exception using the throw keyword inside an asynchronous function
Previously, reject was used, but now the throw keyword can be used instead, and catch can also be used to obtain.

async function fn() {
    throw 'An error has occurred' // After this is implemented, it will not be implemented again
    return 123; // If return precedes the throw code, throw will no longer be executed
}
fn().then((data) => {
    console.log(data) // (if return is in front of the throw code) only output 123 catch code is no longer executed.
})
.catch((err) => {
    console.log(err) // Some errors have occurred
})

5. Call the asynchronous function, and then chain call the then method to obtain the execution result of the asynchronous function.
6. Call the asynchronous function, and then chain call the catch method to obtain the error information of asynchronous function execution.
3.2 await keyword
1. The await keyword can only appear in asynchronous functions.
2. await promise. Only promise objects can be written after await. It is not allowed to write other types of API s.
3. await keyword can pause asynchronous function execution down until promise returns a result.

async function lgg1() {
    return 'lgg1'
}
async function lgg2() {
    return 'lgg2'
}
async function lgg3() {
    return 'lgg3'
}
async function lgg() {
    let lgg1 = await lgg1();
    let lgg2 = await lgg2();
    let lgg3 = await lgg3();
    console.log(lgg1, lgg2, lgg3); // lgg1 lgg2 lgg3
}

Scheme III:
The readFile method obtains the reading result of the file by returning the value, that is, it does not return the promise object, so the await keyword cannot be added. In other words, asynchronous functions cannot be used in nodejs, but nodejs provides a method to wrap the existing asynchronous API and let the method return promise objects to support asynchronous function syntax

const fs = require('fs');
const promisify = require('util').promisify;
const readFile = promisify(fs.readFile);
async function lgg() {
    let lgg1 = await readFile('./lgg1.html', 'utf8')
    let lgg2 = await readFile('./lgg1.html', 'utf8')
    let lgg3 = await readFile('./lgg1.html', 'utf8')
    console.log(lgg1, lgg2, lgg3); // lgg1 lgg2 lgg3
}
lgg();

Topics: Javascript node.js Front-end server http