As a self-learner on the way to learning. I sort out my understanding of knowledge by learning and sharing, and I also hope to give you a reference. I hope I can make progress together with you. If there is any mistake, I hope you can correct it. Thanks a million!


In the previous chapter, we set up a very simple “Hello World” server. In this chapter, we will continue what we learned in the previous chapter and further attempt to build a server that provides static resources.

What is a static resource server?

Static resources are files that will not be changed or generated by the dynamic running of the server. What it was originally before the server ran, and what it was when the server finished running. For example, js, CSS and HTML files written at ordinary times can be considered as static resources. It is easy to understand that the function of a static resource server is to provide static resources to clients.

Without further ado, start coding:

First we know that it is a “server “. Based on what we learned in the previous chapter, we will first create an HTTP server using HTTP modules.

var http = require('http');

var server = http.createServer(function(req, res) {
    // Business logic, write later.
});

server.listen(3000.function() {
    console.log("Static resource server running.");
    console.log("Listening on port 3000 :")})Copy the code

Url module

Url module – Documents

With an HTTP server, we can get HTTP requests from clients.

The request message contains the request URL. As mentioned earlier, the URL is used to locate resources on the network. The client uses the URL to indicate the desired resource on the server. So in order for the server to figure out what the client really wants, we need to process and parse the URL. In Node.js, we use the URL module to do this.

We know that A URL string is a structured string that contains multiple components with different meanings. With the url.parse() function, a URL string can be parsed into a URL object with properties corresponding to the components of the string. As shown in the figure below.


So back to our static file server code.

Introduce the URL module before http.createServer is called:

var url = require('url');
Copy the code

The request URL is then parsed in the HTTP server. The request URL sent by the client is stored as a property in the request object received by the callback parameter of http.createserver. The property is called URL.

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
});
Copy the code

The path module

Path module – Documentation

Then get the pathname in the request URL from the parsed URL object urlObj. The pathname is stored in the PathName property.

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
});
Copy the code

But it’s not enough to have the pathname inside the URL object. We also need to get the directory name (dirname) of the directory where the target file resides on the server.

Let’s say our project structure looks like this:

. ├ ─ ─ public │ ├ ─ ─ index. The CSS │ └ ─ ─ index. The HTML └ ─ ─ for server jsCopy the code

Our server code is written in the server.js file. The client wants to request the index. HTML file stored in the public directory. When a user enters a URL into a browser, he only knows that the file he wants is called index.html, but the “absolute location” of the file on the device where the HTTP server is located is unknown. So we need to let the HTTP server handle this part of the operation itself.

Here you need to use the path module that comes with Node.js. It provides utility functions for handling file and directory paths. It’s easy to use, starting with the path module before http.createServer is called:

var path = require('path');
Copy the code

We then use the path.join method to merge the directory name of the destination file’s directory with the pathname in the request URL. In this example, the static files accessible to the client are all in the public directory, which in turn is in the same directory as the server.js file, which holds our server code.

To get the absolute path of the server.js directory across the device, we can call the variable __dirname in the server code, which is the variable passed in when the current file is wrapped by the module wrapper and holds the directory name of the current module.

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
});
Copy the code

If you want, you can use console.log(filePathname) to see how the URL of the request received from the client will be converted after the server runs.

Fs module

Fs module – Documentation

Now comes the most important step, reading the target file and returning the file to the client.

To do this, use the fs.write method in the fs module that comes with Node.js. The first parameter of this method is the path of the target file, and the last parameter is a callback function. The callback has two parameters (err, data), The fs.write method can specify a character encoding in the second argument, or return the original buffer if it is not specified. In this case, we’re not going to consider this term.

The specific code is as follows:

First introduce fs module, I do not need to repeat, refer to the previous can be. Here is the code to read the file.

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
    
    fs.readFile(filePathname, (err, data) => {
        // Return 404 if there is a problem
        if (err) {
            res.writeHead(404);
            res.write("404 - File is not found!");
            res.end();
        // No problem
        } else {
            res.writeHead(200); res.write(data); res.end(); }})});Copy the code

We now have a basic “static file server” that allows clients to request static files stored publicly on the server. You can try to start the server, and then let the browser to http://localhost:3000/index.html. My effect is as follows:

Setting the MIME Type

MIME document -mDN Content-type Document -mDN

Multipurpose Internet Mail Extended (MIME) types are a standardized way to express the “nature” and “format” of a document. Simply put, browsers use MIME types to determine how to process documents. Therefore, it is important to set the correct MIME type in the header of the response object.

MIME consists of two strings of type and subtype separated by ‘/’, with no Spaces. MIME types are case insensitive, but are traditionally written in lower case.

Such as:

  • text/plain: is the default value for text files. Unknown text file that the browser thinks can be displayed directly.
  • text/html: All HTML content should use this type.
  • image/png: is the MIME type of a PNG image.

On the server, we indicate the MIME Type of the resource to be responded to by setting the value of the response header, Content-Type. In Node.js, you can easily set the response status code and the response header using the writeHead method of the response object.

If we want to respond to the client with an HTML file, we should use this code:

res.writeHead(200, {"Content-Type":"text/html"});
Copy the code

You will notice that I did not set the MIME type of the response resource in the static resource server code above. But if you try running the server, you’ll see that static resources are also presented in the browser in the right way.

The reason for this is that when a MIME type is missing or the client thinks the file has the wrong MIME type, the browser may guess the MIME type by looking at the resource, called “MIME sniffing.” Different browsers may perform different actions in different situations. So to ensure that the resource behaves consistently across browsers, you need to manually set the MIME type.

So first we need to get the suffix of the file to prepare the response to the client.

To do this we need to use the parse method of the PATH module. This method parses a path into an object whose properties correspond to each part of the path.

Continue with the static file server example and add:

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
    
    // The ext attribute of the parsed object holds the suffix of the target file
    var ext = path.parse(urlPathname).ext;
    
    // Read the file code...
});
Copy the code

Now that we have the file suffix, we need to look up the CORRESPONDING MIME type. This step can be easily implemented using the third-party module MIME. You can go to NPM and check out its documentation.

For our current requirements, just use the getType() method of the MIME module. This method takes a string argument (suffix) and returns its MIME type, or null if none exists.

To do this, first install the MIME module using NPM (NPM init if you haven’t already created package.json).

npm install mime --save
Copy the code

Once installed, introduce the module into the server code, and then go straight to the MIME type with the suffix we just obtained

var mime = require('mime');

var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
    
    // The ext attribute of the parsed object holds the suffix of the target file
    var ext = path.parse(urlPathname).ext;
    // Get the MIME type of the suffix
    var mimeType = mime.getType(ext);
    
    // Read the file code...
});
Copy the code

Ok, now the most important thing is that we’ve got the MIME type. The next step is to set the Content-Type in the writeHead method of the response object.

var server = http.createServer(function(req, res) {
    // code omitted...
    var mimeType = mime.getType(ext);

    fs.readFile(filePathname, (err, data) => {
        // Return 404 if there is a problem
        if (err) {
            res.writeHead(404, { "Content-Type": "text/plain" });
            res.write("404 - File is not found!");
            res.end();
            // No problem
        } else {
            // Set the response header
            res.writeHead(200, { "Content-Type": mimeType }); res.write(data); res.end(); }})});Copy the code

Stage victory ✌ ️ now run the server, in the browser to access the localhost: 3000 / index. HTML try!!!!

You can see that the Content-Type is now set correctly!

Refactor the code

Now look at your code and see if it’s starting to feel a bit messy. I think you’re smart enough to know that the entire static file server code does one thing: it responds to the client’s request for a static file. This code has a single responsibility and a high frequency of reuse. It makes sense to encapsulate it as a module.

I won’t go into the details of the process. Here is my module code:

// readStaticFile.js

// Import dependent modules
var path = require('path');
var fs = require('fs');
var mime = require('mime');

function readStaticFile(res, filePathname) {

    var ext = path.parse(filePathname).ext;
    var mimeType = mime.getType(ext);
    
    // Check whether the path has a suffix, if so, the client is requesting a file
    if (ext) {
        // Read the corresponding file according to the target file path passed in
        fs.readFile(filePathname, (err, data) => {
            // Error handling
            if (err) {
                res.writeHead(404, { "Content-Type": "text/plain" });
                res.write("404 - NOT FOUND");
                res.end();
            } else {
                res.writeHead(200, { "Content-Type": mimeType }); res.write(data); res.end(); }});// Return false to indicate that the client wants a static file
        return true;
    } else {
        // Returning false indicates that the client does not want a static file
        return false; }}// Export the function
module.exports = readStaticFile;
Copy the code

The module readStaticFile for reading static files is wrapped. We can create a modules directory in the project directory to store modules. Here is my current project structure.

Once the module is wrapped, we can delete the server code that reads the file and reference the module directly. Here is my modified server.js code:

// server.js 

// Introduce related modules
var http = require('http');
var url = require('url');
var path = require('path');
var readStaticFile = require('./modules/readStaticFile');

// Set up the HTTP server
var server = http.createServer(function(req, res) {
    var urlObj = url.parse(req.url);
    var urlPathname = urlObj.pathname;
    var filePathname = path.join(__dirname, "/public", urlPathname);
    
    // Read static files
    readStaticFile(res, filePathname);
});

// Listen for requests on port 3000
server.listen(3000.function() {
    console.log("Server running.");
    console.log("Listening on port 3000 :")})Copy the code

😆 Well, that’s the end of today’s sharing. In the next article, I’ll cover “How to set up server routing” and “Handling browser form submissions.”

Portal – Node.js series – Build routes & handle form submission

If you like it, just click on it! O(∩_∩)O Thanks for your support ❗️