Table of Contents
1. 建立 TypeScript 的環境
首先要先建立 TypeScript 的環境,請參考:如何在 node.js 中建立 TypeScript 的環境。
2. 使用 http module create http server
建立完 TypeScript 的環境後,引入 Node.js 的核心套件之一:http module。 http module 能讓你建立一個 http server,當你的 code 在執行的時候,可以隨時監聽網路上發送到你 url 的所有 request,並將你寫的程式邏輯 response 回去。
Import http module 其實跟 JavaScript 的寫法差不多,只是在 request 和 response 加上對應的型別:IncomingMessage, ServerResponse。
import { createServer, IncomingMessage, ServerResponse } from 'http';
const port = 5000;
const server = createServer((request: IncomingMessage, response: ServerResponse) => {
response.on('error', (err) => {
console.error(err);
});
response.writeHead(200, {"Content-Type": "text/plain"});
response.end('Hello world!');
});
server.listen(port);
console.log(`server is running on http://localhost:5000`)
如此一來每當有任何 client 對 http://localhost:5000 這個 url 發送 request 時,我們的 server 都會回應 ‘Hello world!’ 給該 client。
3. 取得 request 中的資料
3.1 取得 request 中的 body
大部分的時候,如果我們需要傳資料到 server ,會發送 POST request ,並且將資料存在 body 當中,要在 Node.js 取得 request 中的 body data 方法如下:
let body = [];
request
.on('error', (err) => {
console.error(err);
})
.on('data', (chunk) => {
body.push(chunk);
})
.on('end', () => {
body = Buffer.concat(body).toString();
console.log(body);
})
如果直接將 chunk console.log 出來會發現是一段看不懂的資料:`<Buffer 7b 0d 0a 20 20 20 20 22 74 65 73 74 22 3a 20 22 35 22 0d 0a 7d>`,這是因為 http request 在發送給 server 的時候是按照順序一個 byte 一個 byte 以資料流的方式發送的,因此需要用 Buffer.toString() 轉換回原本的資料。
當我使用 POST method 發送 request 時,我的 server 端就會印出 request 中 body 的資料。
3.2 取得 request 中的 header
直接用 request.headers 取得 request 中的基本資料:
request
.on('error', (err) => {
console.error(err);
})
.on('end', () => {
console.log(request.headers);
})
3.3 取得 request 中的 authorization
authorization 是我們習慣放置 token 的欄位,雖然在 postman 中是獨立的一欄,但事實上是存在 request 的 headers 裡的。
request
.on('error', (err) => {
console.error(err);
})
.on('end', () => {
console.log(request.headers);
console.log(request.headers.authorization);
})
完整程式碼:
import { createServer, IncomingMessage, ServerResponse } from 'http';
const port = 5000;
const server = createServer((request: IncomingMessage, response: ServerResponse) => {
const { headers, method, url } = request;
let body:any = [];
request
.on('error', (err) => {
console.error(err);
})
.on('data', (chunk) => {
body.push(chunk);
})
.on('end', () => {
body = Buffer.concat(body).toString();
console.log(body);
console.log(request.headers);
console.log(request.headers.authorization);
})
response.on('error', (err) => {
console.error(err);
});
response.writeHead(200, {"Content-Type": "text/plain"});
response.end('Hello world!');
});
server.listen(port);
console.log(`server is running on http://localhost:5000`)
4. 新增 routes
大多時候,我們希望 client 對不同的 url 發送 request 時可以獲得不同的資料或頁面,這時候就需要新增 router 來管理。
import { createServer, IncomingMessage, ServerResponse } from 'http';
const port = 5000;
const server = createServer((request: IncomingMessage, response: ServerResponse) => {
const { url } = request;
switch(url) {
case '/':
response.writeHead(200, {"Content-Type": "text/plain"});
response.write('Homepage');
response.end();
break
case '/articles':
response.writeHead(200, {"Content-Type": "text/plain"});
response.write('All articles are here!');
response.end();
break
case '/about-me':
response.writeHead(200, {"Content-Type": "text/plain"});
response.write('My name is Jimmy.');
response.end();
break
default:
response.writeHead(404, {"Content-Type": "text/plain"});
response.write('Page not found.');
response.end();
}
});
使用 switch case 來根據不同 request 的 url 來回應相對應的 response。
5. 使用 Express 來改寫
import express, { Express, Request, Response } from 'express';
const port = 5000;
const app: Express = express();
app.get('/', (request: Request, response: Response) => {
response.type('text/plain');
response.send('Homepage');
})
app.get('/articles', (request: Request, response: Response) => {
response.type('text/plain');
response.send('All articles are here!');
})
app.get('/about-me', (request: Request, response: Response) => {
response.type('text/plain');
response.send('My name is Jimmy.');
})
app.use((request: Request, response: Response) => {
response.type('text/plain');
response.status(404)
response.send('Page is not found.');
})
app.listen(port, () => {
console.log(`server is running on http://localhost:5000`)}
);
6. 將 routes 和 controllers 獨立出來
當專案越長越大後,將所有 routes 和邏輯都寫在 server.ts 是個很難維護的做法,因此大多數的專案都會將 routes 和 controllers 獨立出來,把商業邏輯寫在 controllers 中。
// server.ts
import express, { Express, Request, Response, NextFunction } from 'express';
import { ApiRouter } from './routes/api-routes';
const port = 5000;
const app: Express = express();
const apiRouter = new ApiRouter;
app.use((request: Request, response: Response, next: NextFunction) => {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE");
next();
});
app.use('', apiRouter.router)
app.use((request: Request, response: Response) => {
response.type('text/plain');
response.status(404)
response.send('Page is not found.');
})
app.listen(port, () => {
console.log(`server is running on http://localhost:5000`)}
);
// routes/api-routes.ts
import express, { Router, Request, Response, NextFunction } from 'express';
import { ApiControllers } from '../controllers/api-controllers';
const apiControllers = new ApiControllers;
export class ApiRouter {
router: Router;
constructor() {
this.router = express.Router();
this.initializeRoutes();
}
initializeRoutes() {
this.router.get('/', apiControllers.getHomePage);
this.router.get('/articles', apiControllers.getArticlesPage);
this.router.get('/about-me', apiControllers.getAboutPage)
}
}
// controllers/api-controllers.ts
import { Request, Response, NextFunction } from 'express';
export class ApiControllers {
getHomePage(request: Request, response: Response, next: NextFunction) {
response.type('text/plain');
response.send('Homepage');
}
getArticlesPage(request: Request, response: Response, next: NextFunction) {
response.type('text/plain');
response.send('All articles are here!');
}
getAboutPage(request: Request, response: Response, next: NextFunction) {
response.type('text/plain');
response.send('My name is Jimmy.');
}
}
檔案結構:
7. 參考資料
文章:
TypeScript Express tutorial #1. Routing, controller, middleware
Node.js TypeScript #7. Creating a server and receiving requests
Node.js and TypeScript Tutorial: Build a CRUD API
Node.js 官方文件
書籍:
用Node.js一統JavaScript前後端:強勢Web開發親手作
網頁應用程式設計:使用 Node 和 Express(第二版)
如果覺得我的文章有幫助的話,歡迎幫我的粉專按讚哦~謝謝你!
將 routes 和 controllers 獨立出來的第一段程式码档案名称错误了
srver.ts => server.ts