node 第二课,制作一个自己的 node mvc 框架。
Github 源码仓库地址: https://github.com/MiYogurt/hah 免费视频地址:https://nodelover.me/course/nodejs2
net 模块
http 与 https 模块
搭建一个 web server
import * as http from "http";
const server = http.createServer((req, res) => {
res.end('hello world!')
})
server.listen(3000)
路由与控制器
npm i path-to-regexp
router.ts
import * as pathToRegexp from 'path-to-regexp';
const methodBuilder = (name) => function (url, handle) {
let keys = []
this.methods[name].push({
url: pathToRegexp(url, keys),
keys,
handle
})
}
// method url
class Router {
methods = {
get: [],
delete: [],
option : [],
put: [],
post: []
}
get = methodBuilder('get')
post = methodBuilder('post')
put = methodBuilder('put')
option = methodBuilder('option')
delete = methodBuilder('delete')
handleParams(req, res){
const methods = this.methods[(req.method as string).toLocaleLowerCase()];
for(let method of methods){
const {
url,
keys,
handle
} = method;
const params = url.exec(req.url)
if(params){
req.params = keys.reduce((prev, current, index, array) => {
prev[current.name] = params[index + 1] ? params[index + 1] : undefined;
return prev;
}, {});
(async() => {
await handle(req, res)
})()
break;
}
}
}
}
export default Router;
index.ts
import Router from './router';
const router: any = new Router();
router.get('/', (req, res) => {
res.end('home')
})
router.get('/day/haha', (req, res) => {
console.log(req.params)
res.end("haha")
})
router.get('/day/:count', async (req, res) => {
console.log(req.params)
res.end("day")
})
const server = http.createServer((req, res) => {
router.handleParams(req, res)
})
server.listen(3000)
数据库
npm i lokijs
database/index.ts
import * as loki from 'lokijs';
const db = new loki('db.json')
const posts = db.addCollection('posts');
var resultObj = posts.insert({
username: "yugo",
title: 'day by day',
body: 'awesome day!'
});
export {
db,
posts
}
index.ts
import { posts } from './database/index';
router.get('/posts/:username', async (req, res) => {
const postsList = posts.find({ 'username': { '$eq': req.params.username } })
const p = Promise.resolve(postsList)
res.end(JSON.stringify(await p))
});
视图
npm i pug
npm i @types/pug -D
view.ts
import * as pug from 'pug';
import * as path from 'path';
function installViewEngine(res, basePath = path.resolve(__dirname, './views')){
res.render = (filename, context) => {
try {
const prod = process.env['NODE_ENV'] == 'prod' ? true: false;
const fn = pug.compileFile(basePath + filename, {
debug: !prod,
cache: prod,
basedir: basePath
});
const html = fn(context)
res.end(html);
}catch(e) {
console.log(e);
res.end(e)
}
}
}
export { installViewEngine }
index.ts
import { installViewEngine } from './view'
router.get('/', (req, res) => {
res.render('/home.pug', {
hello : 'world'
})
})
const server = http.createServer((req, res) => {
installViewEngine(res);
router.handleParams(req, res)
})
views/home.pug
<!DOCTYPE html>
html(lang="en")
head
meta(charset="UTF-8")
title Document
body
=hello
not fond
npm i finalhandler
import * as finalhandler from 'finalhandler';
const server = http.createServer((req, res) => {
res['done'] = finalhandler(req, res)
installViewEngine(res);
router.handleParams(req, res)
res['done']();
})
const server = http.createServer((req, res: http.ServerResponse & { done: Function }) => {
res.done = finalhandler(req, res)
installViewEngine(res);
router.handleParams(req, res)
res.done();
})
解析 post 文本
npm i raw-body
views/home.pug
<!DOCTYPE html>
html(lang="en")
head
meta(charset="UTF-8")
title Document
body
=hello
form(action='post', method='post')
input(name='username')
button(type='submit') 提交
import * as getRawBody from 'raw-body';
import * as querystring from 'querystring'
router.post('/post', async(req, res) => {
res.end(JSON.stringify(req.text))
})
const server = http.createServer((req: any, res: http.ServerResponse & { done: Function }) => {
res.done = finalhandler(req, res)
getRawBody(req, {
length: req.headers['content-length'],
limit: '1mb'
}, (err, string) => {
if (err) return res.done(err)
req.text = querystring.parse(string.toString())
installViewEngine(res);
(async () => {
await router.handleParams(req, res)
res.done();
})()
})
})
添加浏览器不支持的请求
home.pug
<!DOCTYPE html>
html(lang="en")
head
meta(charset="UTF-8")
title Document
body
=hello
form(action='post', method='post')
input(name='username')
input(name='_method', type='hidden', value='delete')
button(type='submit') 提交
router['delete']('/post', async(req, res) => {
res.end('delete')
})
router.ts
async handleParams(req, res){
let methods = this.methods[(req.method as string).toLocaleLowerCase()];
if ('_method' in req.text) {
methods = this.methods[(req.text['_method'] as string).toLocaleLowerCase()];
}
for(let method of methods){
const {
url,
keys,
handle
} = method;
const params = url.exec(req.url)
if(params){
req.params = keys.reduce((prev, current, index, array) => {
prev[current.name] = params[index + 1] ? params[index + 1] : undefined;
return prev;
}, {});
await handle(req, res)
break;
}
}
}