---
# System prepended metadata

title: NodeJS(Max)第 3 節：Understanding the basics
tags: [Node.js, Udemy]

---

# NodeJS(Max)第 3 節：Understanding the basics

> Udemy課程：[NodeJS - The Complete Guide (MVC, REST APIs, GraphQL, Deno)
](https://www.udemy.com/course/nodejs-the-complete-guide/)

`20231122Wed.~20231203Sun.`

## 3-25. How The Web Works
![image](https://hackmd.io/_uploads/BJZx5QoN6.png)


****

## 3-26. Creating a Node Server
* node.js的檔案進入點為"app.js"
* node.js的core module有以下幾種
    * http：Launch a server, send requests
    * https：Launch a SSL server
    * fs
    * path
    * os
    
:::success
一個node.js文件，就是一個單獨的moduel。
:::
* node server實作：
    * ==require()==：require method提供可以載入module方法（例如上面提到的core module們都可以利用require()載入。）
        ++require加載module 流程++：
        1. 找到需要加載的module
        2. 判斷是否緩存過，若沒有則讀取module
        -->避免多次引入相同module
        3. 把讀取到的內容放到一個自執行函數中執行
        
        ++require()加載module基本上是「同步」的。++

        ![image](https://hackmd.io/_uploads/BkVZpQsEa.png)
        
        首先，載入http module，載入之後我們就可以使用"http object"，http object提供很多fields、methods給我們使用。
        ```javascript!
        const http = require("http");
        ```
    * ==http.createServer()==：
        1. run your computer into sever
        2. create a Http sever object
        * 寫法：
            1. 基本函式寫法
            ```javascript!
            finction reListener(req, res){
                ...
            }
            http.createServer(reListener());
            ```
            2. 匿名函式寫法
            ```javascript!
            http.createServer(function(req, res){
                ...
            });
            ```
            3. 箭頭函式寫法
            ```javascript!
            http.createServer((req, res) => {
                ...
            })
            ```
            
        * 用以上任意方法建立好server之後，賦予他一個變數（此處叫做server）：
            ```javascript!
            const http = require("http");
            const server = http.createServer((req, res) => {
                console.log(req);
            })
            ```
            如此一來，現在這個server變數便是一個Http sever object，理所當然的也會有一些methods供我們使用：
            ![2023-11-22 15-43-14 的螢幕擷圖](https://hackmd.io/_uploads/ryed7bEi4T.png)
            > 圖片參考：[w3schools-Node.js HTTP Server Object](https://www.w3schools.com/nodejs/obj_http_server.asp)
            
    * ==listen()==：承上面提到的Http sever object，listen()是該物件的其中一個method。
        1. listen()可以接收參數port，這裡給予3001（若沒給則會跑預設的port，通常是8080），我們跑這段程式"node app.js"，可以從terminal發現他在運作。
        ![2023-11-22 15-47-20 的螢幕擷圖](https://hackmd.io/_uploads/SJZDmEjEp.png)
        
        2. 之後我們到browser輸入"localhost:3001"，雖然browser看不到任何動作（畢竟我們還沒return任何html頁面）
        ![2023-11-22 15-50-06 的螢幕擷圖](https://hackmd.io/_uploads/ByV0mEjE6.png)

        3. 不過回到terminal看到一大串東西，那些東西就是我們程式碼console.log(req)的結果，所以那一大陀東西就是我們（使用者）輸入網址後，所傳送到server端的request。
        ![2023-11-22 15-55-20 的螢幕擷圖](https://hackmd.io/_uploads/HJFg4EsNT.png)

****

## 3-27. The Node Lifecycle & Event Loop
![image](https://hackmd.io/_uploads/r12Mv4iV6.png)

****

## 3-29. Understanding Requests
![image](https://hackmd.io/_uploads/HyVQwSXSp.png)

```javascript!
const http = require("http");
const server = http.createServer((req, res) => {
    console.log(req.url, req.method, req.headers);
})

server.listen(3000);
```

req為request物件
1. <font style="color: red;">req.url</font>：
目前位址是localhost:3000的根目錄，所以只會得到一個斜槓
2. <font style="color: orange;">req.method</font>：
目前的方法為GET
3. <font style="color: yellow;">req.headers</font>：
下面一大陀都是headers

****

## 3-30. Sending Responses
![image0](https://hackmd.io/_uploads/Bkl6urEHT.jpg)

```javascript!
const http = require("http");
const server = http.createServer((req, res) => {
    console.log(req.url, req.method, req.headers);
    
    res.setHeader("content-type", "text/html");
    res.write("<html>");
    res.write("<head><title>My First Page</title></head>");
    res.write("<body><h1>Hello from my Node.js Server!</h1></body>");
    res.write("</html>");
    res.end();
})

server.listen(3000);
```

> 參考資料：
> 1. [res.setHeader 方法和res.writeHead 方法|學習筆記](https://developer.aliyun.com/article/1060024)
> 2. [Express框架中res.write、res.end及res.send 、res.json方法之間的區別？](https://blog.csdn.net/sunyctf/article/details/124616674)

res為response物件。

1. <font style="color: red;">res.setHeader</font>：
Header告訴瀏覽器我發送的資料是什麼類型的，你應該用什麼格式來編碼顯示。如果不設定，會自動產生一個回應頭，但中文的話瀏覽器會亂碼。
在http 協定中， Content-type 是用來告訴對方我寄給你的資料內容是什麼類型。
2. <font style="color: orange;">res.write</font>：
![2023-11-29 13-18-48 的螢幕擷圖](https://hackmd.io/_uploads/HkcCFB4S6.png)
3. <font style="color: yellow;">res.end</font>：
每個請求都必須要呼叫的一個方法res.end();。結束回應(請求）告訴伺服器該回應的封包頭、封包檔案等等全部已經回應完畢了，可以考慮本回應結束。res.end()要回應資料的話，資料必須是String類型或是Buffer類型。

****

## 3-32. Routing Requests
![image](https://hackmd.io/_uploads/S1OQhIVHa.png)

```javascript!
const http = require("http");
const server = http.createServer((req, res) => {
    const url = req.url;
    if(url === "/"){
        res.setHeader("content-type", "text/html");
        res.write("<html>");
        res.write("<head><title>Enter Message</title></head>");
        res.write("<body><form action='/message' method='POST'><input type='text'><button type='submit'>send</button></form></body>");
        res.write("</html>");
        return res.end();
    }
    
    res.setHeader("content-type", "text/html");
    res.write("<html>");
    res.write("<head><title>My First Page</title></head>");
    res.write("<body><h1>Hello from my Node.js Server!</h1></body>");
    res.write("</html>");
    res.end();
})

server.listen(3000);
```

1. action：當提交表單時，action屬性代表著「從form收集來的data」會被送往某處。像上方例子便是送往`localhost/message`。
2. method：HTTP傳送data的方法可以是GET或是POST
**GET request** is automatically send when you click a link or enter a url.
**POST request** has to be set up by you, by creatingsuch a form etc.

****

## 3-33. Redirecting Requests
![2023-11-29 15-02-41 的螢幕擷圖](https://hackmd.io/_uploads/rkD7zwNHT.png)
▲ 原先：尚未提交表單

![image](https://hackmd.io/_uploads/rJLd4PEBT.png)
![image](https://hackmd.io/_uploads/B18tNv4Sa.png)
![image](https://hackmd.io/_uploads/rkg5NvVrT.png)
▲ 重新導向request：提交表單之後

```javascript!
const http = require("http");
const fs = require("fs");

const server = http.createServer((req, res) => {
    const url = req.url;
    const method = req.method;
    if(url === "/"){
        res.setHeader("content-type", "text/html");
        res.write("<html>");
        res.write("<head><title>Enter Message</title></head>");
        res.write("<body><form action='/message' method='POST'><input type='text'><button type='submit'>send</button></form></body>");
        res.write("</html>");
        return res.end();
    }
    if(url === "/message" && method === "POST"){
        fs.writeFileSync("message.txt", "DUMMY");
        res.statusCode = 302;
        res.setHeader("Location", "/");
        return res.end();
    }
    
    res.setHeader("content-type", "text/html");
    res.write("<html>");
    res.write("<head><title>My First Page</title></head>");
    res.write("<body><h1>Hello from my Node.js Server!</h1></body>");
    res.write("</html>");
    res.end();
})

server.listen(3000);
```

****

## 3-34. Parsing Request Bodies
:::success
擷取至[【stack overflow】In node.js "request.on" what is it this ".on"](https://stackoverflow.com/questions/12892717/in-node-js-request-on-what-is-it-this-on)

<p>The <strong>on</strong> method <strong>binds</strong> an event to a object. </p>

<p>It is a way to express your intent <strong>if</strong> there is something happening (data sent or error in your case) , <strong>then</strong> execute the function added as a parameter. This style of programming is called <strong>Event-driven programming</strong>. 
You might want to look it up in the <a href="http://en.wikipedia.org/wiki/Event-driven_programming" rel="noreferrer">Wikipedia</a></p>

<p>In node.js, there is a class called <strong>EventEmitter</strong> that provides you with all the code that you need for basic events if you decide to use them in your own code (which I would strongly recommend in the case of node.js). Docs for node.js <strong>EventEmitter</strong> are <a href="http://nodejs.org/api/events.html" rel="noreferrer">here</a></p>
:::
> 參考資料：
> 1. [Node.js 中的缓冲区（Buffer）究竟是什么？](https://mp.weixin.qq.com/s/UU-Gug_Dx-OmXVL-99rWRg)
> 2. [In node.js "request.on" what is it this ".on"](https://stackoverflow.com/questions/12892717/in-node-js-request-on-what-is-it-this-on)
> 3. [Node.js Streams: Everything you need to know](https://www.freecodecamp.org/news/node-js-streams-everything-you-need-to-know-c9141306be93)

****
request object是一種EventEmitter object，代表可以發出(emit)事件(event)，那request object可以發出哪些事件呢？

這裡先把request object全部印出來，可以從中找到`_events`，底下的所有event即request object會發出的事件，我們可以新增listener來監聽這些事件。
![image](https://hackmd.io/_uploads/rJwwt_rra.png)

所以當我們使用req.on(eventName, listener)時，.on在這裡可以用來監聽request object所發出的事件(把它放到eventName的位置)，而listener則是在eventName被觸發時將會執行的函式。
![image](https://hackmd.io/_uploads/S1nL9uBrp.png)

先來看看什麼情況會發出事件，首先是**當eventName為"data"時**。

這裡用以下作code做為舉例，我們目前有一個form，並且輸入"abcd"：
![2023-11-30 10-59-46 的螢幕擷圖](https://hackmd.io/_uploads/rk4GsdSBT.png)

```javascript!
const server = http.createServer((req, res) => {
        ...
        res.write("<html>");
        res.write("<head><title>Enter Message</title></head>");
        res.write("<body><form action='/message' method='POST'><input type='text' name='message'><button type='submit'>send</button></form></body>");
    
    ...
    
    req.on("data", (chunk) => {
            console.log(chunk);
        })
    
    ...
}
```
接著當我們按下send按鈕之後，這個"abcd"就會被作為data POST到我們上方設置的`/message`中，當我們傳送了data，data送達了目的，便會觸發listener。

另外一個例子是**當eventName為"end"時**。end event的觸發條件是在所有的data皆傳送完畢，不再有data傳送時便會發出end event。

另外，要記得`<input>`中必須要有"name"屬性，這樣當使用者輸入的內容作為data放進request body時，以上例來說就是message才可以作為一組鍵值對(key-value-pair)的keys，而使用者輸入的"abcd"才可以作為該組鍵值對(key-value-pair)的value。
****

> 參考資料：
> 1. [Node.js官方文件：emitter.on(eventName, listener)](https://nodejs.org/api/events.html#emitteroneventname-listener)
> 2. [When does the NodeJS request object emit events?](https://stackoverflow.com/questions/57350966/when-does-the-nodejs-request-object-emit-events)

```!
[註] const

用const宣告變數之後，就不能再次宣告或是重複指定值。

例如上方例子中的const body = [];，宣告之後我們不能再次body=某某東西，因為這樣的行為就是在對已宣告的body重複assign值了。

不過我們可以對body做push，body.push(某某東西)是沒問題的，因為我們這時改變的是body object（或body element）後面的data，而非value本身。
```

****

## 3-35. Understanding Event Driven Code Execution

先前提到的req.on(eventName, listener)，其中的listener屬於callback function，只有在我們呼叫時他才會作用。

****

## 3-36. Blocking and Non-Blocking Code

在3-34中的程式碼，有一行叫作`fs.writeFileSync("message.txt", message);`，他有個關鍵字sync，是synchronize的縮寫，即同步的意思，這在3-25的圖中解釋過，也就是說經過這行程式時，他會把程式的運行給block住，得等這行`fs.writeFileSync("message.txt", message);`執行完畢，程式才會繼續往下執行。
![image](https://hackmd.io/_uploads/BkVZpQsEa.png)

但假如這行程式遇到了問題，我們的程式就會卡在那邊不動了，因為他是同步的，我們得等這行程式結束後才能執行，所以我們不應該用這樣的語法來寫。

取而代之的，我們應該利用這行`fs.writeFile("message.txt", message);`作為取代。

他可以在第三個參數放上一個function，這個function仍然是個callback function，而該function可以接受error object，所以當沒有error時將會得到null，也就是說當有error時即為true，該function便會被執行。

```javascript!
fs.writeFile("message.txt", message, err => {
    res.statusCode = 302;
    res.setHeader("Location", "/");
    return res.end();
});
```

## 3-38. Using the Node Modules System
**▊ 原先**
接著要把先前的程式碼給模組化，所以先來看看原先的程式碼，我們把所有的邏輯都放在同一個檔案`app.js`中。
**app.js**
```javascript!
const http = require("http");
const fs = require("fs");

const server = http.createServer((req, res) => {
    const url = req.url;
    const method = req.method;
    if(url === "/"){
        res.setHeader("content-type", "text/html");
        res.write("<html>");
        res.write("<head><title>Enter Message</title></head>");
        res.write("<body><form action='/message' method='POST'><input type='text' name='message'><button type='submit'>send</button></form></body>");
        res.write("</html>");
        return res.end();
    }
    if(url === "/message" && method === "POST"){
        const body = [];
        req.on("data", (chunk) => {
            console.log(chunk);
            body.push(chunk);
        })
        return req.on("end", () => {
            const parsedBody = Buffer.concat(body).toString();
            const message = parsedBody.split("=")[1];
            fs.writeFile("message.txt", message,(err) => {
                res.statusCode = 302;
                res.setHeader("Location", "/");
                return res.end();
            });
        })
    }
    
    res.setHeader("content-type", "text/html");
    res.write("<html>");
    res.write("<head><title>My First Page</title></head>");
    res.write("<body><h1>Hello from my Node.js Server!</h1></body>");
    res.write("</html>");
    res.end();
})

server.listen(3000);
```

**▊ 模組化**
所以現在我們要把跟route相關的邏輯全部拉出來獨立成一個檔案。

這裡會運用到module.expoorts的語法。例如下方例子，我們若是在其他檔案中import 某個模組，那麼nodejs便會去尋找module.exports，並從中尋找是否有東西在某個檔案中register了。
```javascript!
const requestHandler = (req, res) => {
    ...
}
module.exports = requestHandler;
//註冊（register）requestHandler函式
```
![image](https://hackmd.io/_uploads/ByE1dvuH6.png)


**routes.js**
```javascript!
const fs = require("fs");

const requestHandler = (req, res) => {
    const url = req.url;
    const method = req.method;

    if(url === "/"){
        res.setHeader("content-type", "text/html");
        res.write("<html>");
        res.write("<head><title>Enter Message</title></head>");
        res.write("<body><form action='/message' method='POST'><input type='text' name='message'><button type='submit'>send</button></form></body>");
        res.write("</html>");
        return res.end();
    }
    if(url === "/message" && method === "POST"){
        const body = [];
        req.on("data", (chunk) => {
            console.log(chunk);
            body.push(chunk);
        })
        return req.on("end", () => {
            const parsedBody = Buffer.concat(body).toString();
            const message = parsedBody.split("=")[1];
            fs.writeFile("message.txt", message,(err) => {
                res.statusCode = 302;
                res.setHeader("Location", "/");
                return res.end();
            });
        })
    }
    res.setHeader("content-type", "text/html");
    res.write("<html>");
    res.write("<head><title>My First Page</title></head>");
    res.write("<body><h1>Hello from my Node.js Server!</h1></body>");
    res.write("</html>");
    res.end();
}

module.exports = requestHandler;
```

**app.js**
```javascript!
const http = require("http");
// routes is a customer file
const routes = require("./routes")

const server = http.createServer(routes);

server.listen(3000);
```

而nodejs提供了一種shortcut寫法，不必寫出`module.exports = requestHandler;`，只須寫出`exports = requestHandler;`即可。

****

## Assignment
![2023-12-02 16-35-02 的螢幕擷圖](https://hackmd.io/_uploads/HkaQpw_Sa.png)

**Answer**
```javascript!
const http = require("http");

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

    if(url === "/"){
        res.setHeader("content-type", "text/html");
        res.write("<html>");
        res.write("<head><title>Welcome!</title></head>");
        res.write(`
            <body>
                <form action='/create-user' method='POST'>
                    <input type='text' name='username'>
                    <button type='submit'>send</button>
                </form>
            </body>
        `);
        res.write("</html>");
        return res.end();
    }
    if(url === "/users"){
        res.setHeader("content-type", "text/html");
        res.write("<html>");
        res.write("<head><title>Enter Message</title></head>");
        res.write("<body><ul><li>user 1</li><li>user 2</li></ul></body>");
        res.write("</html>");
        return res.end();
    }
    if(url === "/create-user"){
        const body = [];
        req.on("data",(chunk) => {
            body.push(chunk);
        });
        req.on("end", () => {
            const parsedBody = Buffer.concat(body).toString();
            //username=使用者輸入的任何內容
            console.log(parsedBody.split("=")[1]);
        });
    }
    res.statusCode = 302;
    res.setHeader("Location", "/");
    res.end();
});

server.listen("3000");
```

****

## 其他參考資料
1. Official Node.js Docs: https://nodejs.org/en/docs/guides/

2. Full Node.js Reference (for all core modules): https://nodejs.org/dist/latest/docs/api/

3. More about the Node.js Event Loop: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

4. Blocking and Non-Blocking Code: https://nodejs.org/en/docs/guides/dont-block-the-event-loop/