What is a Web server?

When an application (client) needs a resource, it can request the resource from a server through Http. The server that provides the server is a Web server;

There are many open source Web servers out there: Nginx, Apache (static), Apache Tomcat (static, dynamic), Node.js

Http template basic use

1.1. How do I create a service

1.1.1. Initial experience of Web server

A first look at creating a Web server:

const http = require('http');

const HTTP_PORT = 8000;

const server = http.createServer((req, res) = > {
  res.end("Hello World");
});

server.listen(8000.() = > {
  console.log('🚀 server is in${HTTP_PORT}Start ~ `)})Copy the code

1.1.2. Create a server

To create the server object, we do this using createServer

  • http.createServerAn object that returns to the server;
  • The underlying layer actually uses a direct New Server object.
function createServer(opts, requestListener) {
  return new Server(opts, requestListener);
}
Copy the code

So, of course, we can also create this object ourselves:

const server2 = new http.Server((req, res) = > {
  res.end("Hello Server2");
});

server2.listen(9000.() = > {
  console.log("Server started successfully ~");
})
Copy the code

As we have seen above, a callback function is passed when the Server is created. This callback function is called with two arguments:

  • Req: request Request object, which contains information about the request.
  • Res: Response The response object that contains the information we want to send to the client;

1.1.3. Listen on ports and hosts

The Server uses the Listen method to start the Server and listen for network requests on a host or port:

  • That is, when we passip:portIs sent to the Web server we are listening to;
  • We can do something about it;

The listen function takes three arguments:

  • Port: it can not be transmitted, the system will assign the end by default, we will write it into the environment variable in the subsequent project;
  • Host: you can usually pass localhost, IP address 127.0.0.1, or IP address 0.0.0.0. The default is 0.0.0.0.
    • Localhost: is essentially a domain name that is normally resolved to 127.0.0.1;
    • 127.0.0.1: Loop Back Address (127.0.0.1) : Loop Back Address (127.0.0.1)
      • Normal database packages often application layer – transport layer – network layer – data link layer – physical layer;
      • However, the loopback address is directly obtained at the network layer and is not often at the data link layer and physical layer.
      • Like we listen in127.0.0.1The IP addresses of hosts on the same network segment cannot be accessed.
    • 0.0.0.0:
      • Listen to all addresses on IPV4 and find different applications by port;
      • Like we listen in0.0.0.0Hosts on the same network segment can be accessed using IP addresses.
  • Callback function: the callback function when the server is successfully started;
server.listen(() = > {
  console.log("Server start ~🚀");
})
Copy the code

1.2. Request Request object

When sending a request to the server, we carry a lot of information, such as:

  • This request URL, the server needs to conduct different processing according to different URL;
  • The request methods, such as GET and POST, are different.
  • Headers also carries some information, such as the client information, the format of the data received, and the supported encoding format.
  • And so on…

Node will help us encapsulate this information into a request object that we can process directly:

const server = http.createServer((req, res) = > {
  / / request object
  console.log(req.url);
  console.log(req.method);
  console.log(req.headers);

  res.end("Hello World");
});
Copy the code

1.2.1. Processing of URL

When the client sends a request, it will request different data, so it will pass a different request address:

  • Such ashttp://localhost:8000/login;
  • Such ashttp://localhost:8000/products;

The server needs to make different responses according to different request addresses:

const server = http.createServer((req, res) = > {
  const url = req.url;
  console.log(url);

  if (url === '/login') {
    res.end("welcome Back~");
  } else if (url === '/products') {
    res.end("products");
  } else {
    res.end("error message"); }});Copy the code

What if the user sends the address with some extra parameters?

  • http://localhost:8000/login?name=why&password=123;

  • At this point, the url is /login, right? Name = why&password = 123;

How do we parse it?

  • Use the built-in module URL;
const url = require('url');

// Parse the request
const parseInfo = url.parse(req.url);
console.log(parseInfo);
Copy the code

Analysis results:

Url {
  protocol: null.slashes: null.auth: null.host: null.port: null.hostname: null.hash: null.search: '? name=why&password=123'.query: 'name=why&password=123'.pathname: '/login'.path: '/login? name=why&password=123'.href: '/login? name=why&password=123'
}
Copy the code

We will find that the PathName is the result we want.

But how can query information be retrieved?

  • Method 1: Intercept a string;
  • Method 2: Use the QueryString built-in module;
const { pathname, query } = url.parse(req.url);
const queryObj = qs.parse(query);
console.log(queryObj.name);
console.log(queryObj.password);
Copy the code

1.2.2. Method processing

In a Restful specification (design style), we should add, delete, modify, and review data through different requests:

  • GET: Queries data.
  • POST: Creates data.
  • PATCH: updates data.
  • DELETE: deletes data.

Therefore, we can do different processing by judging different request modes.

For example, create a user:

  • The request interface is/users;
  • Request mode:POSTRequests;
  • Carry datausernameandpassword;

How to judge and obtain the corresponding data in our program?

  • So we need to figure out what the interface is/usersAnd the request method is the POST method to get the incoming data;
  • To get the data carried by the body, we need to listen on the REQdataEvent to get;
if (req.url.indexOf('/users')! = = -1) {
  if (req.method === 'POST') {
		
    // You can set the encoding, or you can get the string format from data.toString() below
    req.setEncoding('utf-8');

    req.on('data'.(data) = > {
      const {username, password} = JSON.parse(data);
      console.log(username, password);
    });

    res.end("create user success");
  } else {
    res.end("users list"); }}else {
  res.end("error message");
}
Copy the code

Parse converts the JSON string format to an object type using the json.parse method.

1.2.3. Header properties

The header of the Request object also contains a lot of useful information:

const server = http.createServer((req, res) = > {
  console.log(req.headers);

  res.end("Hello Header");
});
Copy the code

By default, the browser passes some information:

{
  'content-type': 'application/json'.'user-agent': 'PostmanRuntime / 7.26.5'.accept: '* / *'.'postman-token': 'afe4b8fe-67e3-49cc-bd6f-f61c95c4367b'.host: 'localhost:8000'.'accept-encoding': 'gzip, deflate, br'.connection: 'keep-alive'.'content-length': ', 48 '
}
Copy the code

Content-type is the type of data carried in this request:

  • application/jsonRepresents a JSON type;
  • text/plainRepresentation is a text type;
  • application/xmlThe representation is of type XML;
  • multipart/form-dataIndicates that the file is uploaded.

The content – length:

  • The size and length of the file

Keep alive: –

  • HTTP is based on TCP, but usually interrupts immediately after a request and response.

  • In HTTP1.0, if you want to remain connected:

    • The browser needs to add in the request headerconnection: keep-alive;
    • The server needs to add in the response headerconnection:keey-alive;
    • When the client releases the request again, it will use the same connection, and directly one party breaks the connection;
  • In HTTP1.1, all connections are connection: keep-alive by default;

    • Different Web servers have different holdskeep-aliveThe time.
    • In Node, 5s is the default;

The accept – encoding:

  • Inform the server that the file compression format supported by the client, such as JS files can use GZIP encoding, corresponding.gzFile;

Accept:

  • Inform the server of the format types of files that are acceptable to the client;

The user-agent:

  • Client related information;

1.3. Response object Response

1.3.1. Return the response result

If we want to give the client the result data of the response, we can do it in two ways:

  • Write method: This method writes out data directly, but does not close the stream;
  • End method: This method writes out the last data and closes the stream.
const http = require('http');

const server = http.createServer((req, res) = > {

  // There are two ways to respond to data:
  res.write("Hello World");
  res.write("Hello Response");
  res.end("message end");
});

server.listen(8000.() = > {
  console.log("Server start 🚀~")});Copy the code

If we hadn’t called end and close, the client would have been waiting for the result, so the client would have set a timeout when sending network requests.

1.3.2. Return the status code

An Http Status Code is a numeric Code used to indicate the Status of an Http response:

  • Http status code is very many, according to different situations, to the client to return different status code;
  • Common status codes are the following (and will be used in future projects);

There are two common ways to set the status code:

res.statusCode = 400;
res.writeHead(200);
Copy the code

1.3.3. Response header files

There are two main ways to return header information:

  • res.setHeader: Writes one header at a time.
  • res.writeHead: Write both header and status;
res.setHeader("Content-Type"."application/json; charset=utf8");

res.writeHead(200, {
  "Content-Type": "application/json; charset=utf8"
})
Copy the code

So what does Header set content-type do?

  • By default, the client receives a string, and the client processes it in its default way.

For example, we return a piece of HTML, but do not specify the format:

res.end('<h2>Hello World</h2>')
Copy the code

However, if we specify the format:

res.setHeader("Content-Type"."text/html; charset=utf8");
res.end('<h2>Hello World</h2>')
Copy the code

What if we want to return a piece of JSON data?

res.writeHead(200, {
  "Content-Type": "application/json; charset=utf8"
})

const data = {
  name: "Wang Hongyuan".age: 18.height: 1.88
};

res.end(JSON.stringify(data));
Copy the code

Other Web supplements

2.1. Use of file upload

If it is a large file that needs to be uploaded to the server, how should the server save it?

const server = http.createServer((req, res) = > {
  if (req.url === '/upload') {
    if (req.method === 'POST') {
      const fileWriter = fs.createWriteStream('./foo.png');
      req.pipe(fileWriter);

      const fileSize = req.headers['content-length'];
      let curSize = 0;
      console.log(fileSize);

      req.on("data".(data) = > {
        curSize += data.length;
        console.log(curSize);
        res.write('File upload progress:${curSize/fileSize * 100}%\n`);
      });

      req.on('end'.() = > {
        res.end("File upload completed ~"); }}})else {
    res.end("error message"); }});Copy the code

The file was uploaded successfully, but the file could not be opened:

  • That’s because the data that we write, it contains some special information;
  • The software that opens up this information doesn’t parse very well;
const server = http.createServer((req, res) = > {
  if (req.url === '/upload') {
    if (req.method === 'POST') {
      // Image files must be set to binary
      req.setEncoding('binary');

      // Get the value of boundary in content-type
      var boundary = req.headers['content-type'].split('; ') [1].replace('boundary='.' ');
      
      // Record the current data
      const fileSize = req.headers['content-length'];
      let curSize = 0;
      let body = ' ';

      // Listen for current data
      req.on("data".(data) = > {
        curSize += data.length;
        res.write('File upload progress:${curSize/fileSize * 100}%\n`);
        body += data;
      });

      // Data structure
      req.on('end'.() = > {
        // Cut the data
        const payload = qs.parse(body, "\r\n".":");
        // Get the last type (image/ PNG)
        const fileType = payload["Content-Type"].substring(1);
        // Get the length to intercept
        const fileTypePosition = body.indexOf(fileType) + fileType.length;
        let binaryData = body.substring(fileTypePosition);
        binaryData = binaryData.replace(/^\s\s*/.' ');

        // binaryData = binaryData.replaceAll('\r\n', '');
        const finalData = binaryData.substring(0, binaryData.indexOf(The '-'+boundary+The '-'));

        fs.writeFile('./boo.png', finalData, 'binary'.(err) = > {
          console.log(err);
          res.end("File upload completed ~"); }}})})else {
    res.end("error message"); }});Copy the code

2.2. HTTP sends network requests

The AXIos library can be used in a browser as well as in Node:

  • In browsers, Axios uses wrapped XHR;
  • In Node, the HTTP built-in module is used;

So HTTP modules can send network requests directly from Node.

Send a GET request:

http.get("http://localhost:8000".(res) = > {
  res.on('data'.data= > {
    console.log(data.toString());
    console.log(JSON.parse(data.toString())); })});Copy the code

Send a POST request:

const req = http.request({
  method: 'POST'.hostname: "localhost".port: 8000
}, (res) = > {
  res.on('data'.data= > {
    console.log(data.toString());
    console.log(JSON.parse(data.toString()));
  })
})

req.on('error'.err= > {
  console.log(err);
})

req.end();
Copy the code

Note: All of this content will be published on our official website. We will update our tutorials on Flutter, TypeScript, React, Node, Uniapp, MPvue, data structures and algorithms, etc. We will also update some of our own learning experiences