---
# System prepended metadata

title: 後端basic - Express

---

# 後端basic - Express
[Node.js Express 結構分析](https://ithelp.ithome.com.tw/articles/10222345)

![](https://i.imgur.com/QreZl15.png)
開始使用Express in project: 在目標資料夾使用`npm init`並用`npm install express`安裝
* Tools:
Templating
URL Mapping/Routing
User Input Processing

[Installation guides for common application development tools](https://treehouse.github.io/installation-guides/) | [解決: 因為這個系統上已停用指令碼執行，所以無法載入...](https://israynotarray.com/other/20200510/1067127387/)
#### Server
A **program** runs on a remote computer, wait for **HTTP request** from client(使用者輸入網址), then bring action to putting together a **response**.
通常會先在local端建server測試app,測試完成後再把app [Deploy](https://teamtreehouse.com/library/deploy-a-node-application-to-heroku) 到remote server.
#### Route (end point)
對client端來說: Route是URLs
對server端來說: Route是**command**,會執行特定**function** which sends **response** back to client
## 開始使用express
[Express doc - API reference](https://expressjs.com/en/4x/api.html) | 若code有更改,server必須重開才會更新,此時可用[nodemon](https://www.npmjs.com/package/nodemon)來自動化此步驟
```
const express = require('express');
const app = express();
app.get('/', (request, response) => {...});    //參數1-PATH:'/'是root route, 參數2-callback
```
* `app.listen(port, callback)`method: 參數1 tell server which port to serve the app on 
## Pug Templating
#### template (views)
![](https://i.imgur.com/WPyXYFJ.png)
templates are a special type of file that have their own syntax and language, **stored on server**, and act as some kind of **form letter** for your HTML.
The result is a **full HTML page** sent to the client.
* Rendering the template: provides the **basic HTML** for app and serves it to the users.
* vary output to provide customized **responses**.
* JS templatimg engine languages: **[Pug](https://pugjs.org/api/getting-started.html)**(formerly Jade), **[Handlebars](https://handlebarsjs.com/)**, **[EJS](https://ejs.co/)**
![](https://i.imgur.com/BdPOYvD.png)
[pug加attrubutes](https://pugjs.org/language/attributes.html)
#### start to use pug
1. Downloading Pug with npm
2. Update code with `app.set()` method in app to use Pug
```
app.set('view engine', 'pug'); //參數2:要用哪個engine
```
3. Create templates and folder for templates
By default, Express會指向root裡面的'views'資料夾,我們會把Template檔案(e.g. `index.pug`)放在裡面。若要更改路徑參考[app.set()](https://expressjs.com/en/guide/using-template-engines.html)
4. Render templates with response.render()
```
res.render('index');    //Pug會自己抓資料夾裡面的檔案名來render
```
![](https://i.imgur.com/gGA6fEC.png)
可用dev tool來查看template的內容,並整理排版![](https://i.imgur.com/bCkgtjL.png)
5. templates.pug檔案常用開頭:
```
doctype html
html(lang="en")
  head
    title Your Web Page
  body
```
* [Using Logic in Pug](https://orandigo.github.io/blog/2020/12/27/20201227-pug-note/)
可做if...else判斷,loop等
#### interpolation (template literals of Pug)
```
h1 My name is #{name}    //<h1>My name is Andrew</h1>
```
attributes(title, id, class...)不接受interpolation,所以要這樣寫:
```
h1(title='My name is '+ name) #{name}    //    <h1 title='My name is Andrew'>Andrew</h1>
```
#### [Template Inheritance](https://pugjs.org/language/inheritance.html)
可以寫一個layout.pug包含所有template會共用的部分,並讓child.pug繼承parent.pug來共用layout
* 方法1:block content
```
//parent template: layout.pug
block content

------

//child template
extends layout.pug    //延伸的template路徑,parent.pug

block content    //讓child.pug輸入內容到content block
    section#content    //這些內容會被放入parent.pug的content block

```
*方法2:[Include](https://pugjs.org/language/includes.html) 常用在header跟footer
```
//parent template: layout.pug
include includes/header.pug    //views裡創建includes資料夾,並連結child檔案路徑

------

//child template
header    //直接寫內容
    h1 Assignment 2
```
#### [HTML to Pug](https://html2jade.org/)
## Request object & Response object in Pug
### Request object
Gives us access to data the client has sent to the server, such as values of fields from a form submission.
#### making a [POST](https://expressjs.com/en/4x/api.html#app.post.method) request using `form` tag: send data to the server
```
form(action='/url', method='post')    //參數1:提供data到哪個path(空白會提供給當前頁面), 參數2:request method
    label Please enter a Number:
        input(type='number', name='inputValue')    //給user的輸入欄位
    button(type='submit') Submit    //submit按鈕
```
#### [Request](https://expressjs.com/en/4x/api.html#req) Object in Pug
* `req.body`: 注意body property, this is where our ***form response* will end up**. By default, it is **undefined**
* **Middleware**: some **code fits in the middle** of a *request* or *response*, it dose something with the *incoming data*(HTTP request), and hands the result of to the app.
We use middleware **to put the form response into the body**. (e.g.`express.json()` or `express.urlencoded()`)
```
app.use(express.urlencoded({ extended: false}));    //不要問為甚麼,之後user輸入的資料就會進到req.body了
```
![](https://i.imgur.com/ahywpw4.png)
* `express.urlencoded()`在做這件事:
![](https://i.imgur.com/s8sQ25y.png)
* 三個獲取參數的method: `req.params` `req.body` `req.query`
### [Response](https://expressjs.com/en/4x/api.html#res) object
Packaging a response to be sent back to the client.
#### res.send()
```
res.send('<h1>Hello World!</h1>')
```
#### [res.render()](https://expressjs.com/en/4x/api.html#res.render) Method:  Turn templates into HTML
```
res.render(view [, locals] [, callback])    //[]代表optional
```
參數1:view檔案名(不用加附檔名.pug)
參數2:Locals-{var:value} for view to have access to when being rendered.
### Locals
Locals是一個object(e.g.`{prompt: 'Who are you?'}`,`{num: request.body.num}`)
也可使用`res.locals`,以下兩段程式碼等效
```
res.render('card', {prompt:'Who is buried in Grant's tomg?});    //在.pug檔裡使用prompt會輸出值'Who is buried in Grant's tomg?
```
![](https://i.imgur.com/Dn346qX.png)
* 兩種把res.locals傳入template.pug的方法
```
h1= prompt    //若var沒定義,則為undefined, h1值為空白
h1 The hint is #{name}    //類似template literals的用法
```
![](https://i.imgur.com/P4Tl1L8.png)
#### GET POST cycle
express裡設定GET跟POST request(num的值由POST form裡的.inputValue定義)
![](https://i.imgur.com/CcNBgeT.png)
Pug裡寫if num有被定義則GET,else則render POST(屬性name:inputValut)
![](https://i.imgur.com/G6asYRV.png)
#### res.json()
For client who don't want all the persentation of HTML CSS, they just want data. (e.g. build REST API)
```
res.json(req.body);    //return {"username":"Andrew"}
```
## Modular Routes
### Cookies
Cookie store **state** data on the client.
(Further Resources for Storing State: keywords - Local Storage, SQL and Node.js with Sequelize, Mongo)
1. [`res.cookie()`](https://expressjs.com/en/4x/api.html#res.cookie): Send a cookie to the broswer after *submit the form*, then the browser will automatically send this cookie with *every request it makes*.
```
//res.cookie(name, value [, options])
res.cookie('username', req.body.username);    //參數1:name, 參數2:從form取出的值
```
但此時server isn't reading回傳的cookie, to read the cookie from the request, 在request object裡需要有 cookie's property, 此時就需要cookie-parser這個Middleware.

2. [`cookie-parser`](https://github.com/expressjs/cookie-parser): 可以讓我們讀取form裡的data
* npm install cookie parser
* config and use cookieParser in your app
```
const cookieParser = require('body-parser');

app.use(cookieParser());
```
3. [`res.cookies()`](https://expressjs.com/en/4x/api.html#req.cookies): 讀取步驟1 form submit的值. When using *cookie-parser* middleware, this property is an **object** that contains *cookies sent by the request*. If the request contains no cookies, it defaults to `{}`.
![](https://i.imgur.com/nuPYG13.png)
### redirect
當Client端request一個 *受保護的*(需login) 或*輸入錯誤的*(Google買下的類似domain) 或*被移動的* URL, Server通常會回覆一個包含redirect URL的response, Client收到後會立刻make a 2nd request去這個new URL.
HTTP Redirects require a browser or client to make a second request and **don't require action from the user** to follow the new URL being redirected to.
* `res.redirect([status,] path)`
利用cookie state來redirect: 若cookie state裡有username,首頁會歡迎{[name](https://ui.dev/shorthand-properties)}; 若無,redirect到inputForm讓使用者輸入username
![](https://i.imgur.com/MrrnDjA.png)
* `res.clearCookie()`
首頁清除cookie後redirect到inputForm
![](https://i.imgur.com/nKZXMlK.png)
![](https://i.imgur.com/5ZmaihV.png)
## [Middleware](https://expressjs.com/en/guide/writing-middleware.html)
讀取req跟res的需求,處理成回覆給使用者的res
* Basic form of Express middleware
```
(req, res, next) => {
  // do something
  next();
}
```
* express使用middleware
```
app.use((req, res, next) => {});    //for every request

app.use('/users', (req, res, next) => {});    //run for特定route
```
`.use`可以換成`.get`,單純處理get request
* Sequence: 會按順序由上往下執行,也可用`,`增加多個function
```
app.use((req, res, next) => {    //func1
    console.log('one');
    next();
},
(req, res, next) => {    //func2
    console.log('two');
    next();
});

app.use((req, res, next) => {    //func3
    console.log('three');
    next();
});

//one two three
```
* pass info
```
app.use((req, res, next) => {
    req.message = 'I made it!';
    next();
});

app.use((req, res, next) => {
    console.log(req.message);
    next();
});

//I made it!
```
#### The `next` function
* `next()`;宣告一個middleware-function結束了,可以執行下一個
* 要結束一個middleware function,要馬`next()`,要馬完成一個response (e.g.`.render()`,`.send()`),不然app會一直loading
* third party middleware: these func are being called as Express sets up the server and returns middleware functions.([Closures的概念](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures))
![](https://i.imgur.com/vBxPimZ.png)
### Handling Errors in an Express App
By Passing an object as a parameter to `next()`來抓app裡的錯誤
```
app.use((req, res, next)=>{
    const err = new Error('Oh noes!');    //製作一個方便查找的簡短Error message
    err.status = 500;    //這裡的.status是普通的object property, 500代表general error
    next(err);
});
```
![](https://i.imgur.com/cxP54V3.png)
This output comes from Express's built in error handler.紅線為錯誤發生位置
#### Error Handling Middleware
* Error Middleware
```
(err, req, res, next) => {}    //Express會把Error object pass給第一個有4個parameter的Middleware(類似request.on("error", () =>{})的概念)
```
```
app.use((err, req, res, next)=>{
    res.local.error = err;    //把local.error property設成err
    res.status(err.status);    //抓取status code給browser
    res.render('error', err);    //參數1:error template, 參數2:error object
});
```
* `res.status()`method takes the number of the code.
![](https://i.imgur.com/P5bSBls.png)
#### Handling 404 Errors
A 404 signals the requested route dosen't exists. Express's native handler會傳送預設的404 plain text給client端

如何catch the request before他跑到最後,並傳送客製化404頁面
![](https://i.imgur.com/eTO7enC.png)
```
//做一個'error製造Middleware'放在Error Middleware之前,Express跑到這,會使用這個製造機
app.use((req, res, next)=>{
    const err = new Error('Not Found');
    err.status = 404;
    next(err);
});
```
## Parameters, Query Strings, and Modularizing Routes
### Modular Routes
![](https://i.imgur.com/ewaJxLa.png)
通常業界會把route依功能模組拆分成數個檔案,放在route資料夾
![](https://i.imgur.com/smqhgFe.png)
```
//index.js裡的config
const express = require('express');
const router = express.Router();

======
router.get('/'......);
router.post('/inputForm'......);
======

module.exports = router;
```
```
//cards.js裡的config
const express = require('express');
const router = express.Router();

======
router.get('/'......);    //因為在app.js裡app.use('/cards', cardRoutes); 所以在cards.js裡的route都會多一個/cards
======

module.exports = router;
```
### Using Data and Route Parameters
以下程式碼使用{[ES6 Shorthand Properties](https://ui.dev/shorthand-properties)} + 解構賦值
![](https://i.imgur.com/HgmLbNb.png)
* Route Parameters `/:`
```
// '/:'的':'後面的值會被當成param的name,以id示範
router.get('/:id',(req, res)=>{});
```
* [`req.params`](https://expressjs.com/en/api.html#req.params) object 網頁網址會變`/url/id`
* [`req.query`](https://expressjs.com/en/api.html#req.query) object 用query string的方法取值 網頁網址會變`/url?key1=value1&key2=value2`
### 隨機card的.pug
![](https://i.imgur.com/PFe72dm.png)
## Serving Static Assets
Static Assets: 靜態網頁內容(CSS, client-side JS),存放在web server資料夾內的完整檔案,會直接交由browser(Client端)執行.(Express無法讀取Client端的js)
#### Setup
1.先在project裡建立一個public(業界慣用)資料夾,裡面存放.css檔案
#### `express.static()` method
2.設一個route連到public資料夾
```
app.use('/static', express.static('public'));
```
![](https://i.imgur.com/MlK355W.png)
3.若希望所有頁面都使用此stylesheet, 在layout.pug裡連結此檔案(css會放在`<head>`tag裡)
![](https://i.imgur.com/uvYXGPw.png)
4.調整template
