# 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

****
## 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基本上是「同步」的。++

首先,載入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供我們使用:

> 圖片參考:[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發現他在運作。

2. 之後我們到browser輸入"localhost:3001",雖然browser看不到任何動作(畢竟我們還沒return任何html頁面)

3. 不過回到terminal看到一大串東西,那些東西就是我們程式碼console.log(req)的結果,所以那一大陀東西就是我們(使用者)輸入網址後,所傳送到server端的request。

****
## 3-27. The Node Lifecycle & Event Loop

****
## 3-29. Understanding Requests

```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

```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>:

3. <font style="color: yellow;">res.end</font>:
每個請求都必須要呼叫的一個方法res.end();。結束回應(請求)告訴伺服器該回應的封包頭、封包檔案等等全部已經回應完畢了,可以考慮本回應結束。res.end()要回應資料的話,資料必須是String類型或是Buffer類型。
****
## 3-32. Routing Requests

```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

▲ 原先:尚未提交表單



▲ 重新導向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來監聽這些事件。

所以當我們使用req.on(eventName, listener)時,.on在這裡可以用來監聽request object所發出的事件(把它放到eventName的位置),而listener則是在eventName被觸發時將會執行的函式。

先來看看什麼情況會發出事件,首先是**當eventName為"data"時**。
這裡用以下作code做為舉例,我們目前有一個form,並且輸入"abcd":

```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);`執行完畢,程式才會繼續往下執行。

但假如這行程式遇到了問題,我們的程式就會卡在那邊不動了,因為他是同步的,我們得等這行程式結束後才能執行,所以我們不應該用這樣的語法來寫。
取而代之的,我們應該利用這行`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函式
```

**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

**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/