nodejs项目实战教程12——Nodejs封装Express
- 1. Express简介
- 2. 封装get请求
- 2.1 模拟express的get方法
- 2.2 封装get方法
- 2.3 在web服务器中使用封装的get方法
- 3. 封装post请求
- 3.1 使用闭包,避免注册方法变量暴露
- 3.2 封装post方法
- 3.3 在web服务器中使用封装的post方法
- 3.4 扩展res.send方法
- 4. 封装web静态服务
- 4.1 封装静态web服务
- 4.2 在web服务中使用封装的静态服务
1. Express简介
查看express的npmjs文档介绍,简单用法如下:
const express = require('express')
const app = express()
app.get('/', function (req, res) {
res.send('Hello World')
})
app.listen(3000)
显然,express是对我们之前路由学习更加高效简便的一种路由封装方式,接下来我们将尝试完成一个简单的express。
2. 封装get请求
2.1 模拟express的get方法
/**
* 最终目标是以这样的方式配置路由
*
* app.get('/',(req,res)=>{
* res.send('hello world!')
* })
*/
let app = ()=>{
console.log('调用app方法')
}
app.get = ()=>{
console.log('get方法')
}
app.post = ()=>{
console.log('post方法')
}
app()
app.get()
2.2 封装get方法
/**
* 最终目标是以这样的方式配置路由
*
* app.get('/',(req,res)=>{
* res.send('hello world!')
* })
*/
let G = {}
let app = (req,res)=>{
if(G['/login']){
// 执行方法
G['/login'](req,res)
}
}
app.get = (string,cb)=>{
// 注册方法
G[string] = cb
/**
* G['/login'] = (req,res)=>{
* res.send('hello world!')
* }
*/
}
app.post = ()=>{
console.log('post方法')
}
app.get('/login',(req,res)=>{
console.log('执行了login方法')
})
setTimeout(()=>{
app('req','res')
},1000)
这段封装方法中,使用了一个公共变量G作为存储方法的对象,app.get方法用于往G中注册方法,app方法则是调用G中的方法。
2.3 在web服务器中使用封装的get方法
(1) 进一步封装,创建module/route.js,引入url,并且最终暴露app:
let G = {}
let app = (req,res)=>{
const {url} = req
const {host} = req.headers
const myURL = new URL(url,`http://${host}`)
pathname = myURL.pathname
pathname = pathname === '/index.html'?'/':pathname
if(G[pathname]){
// 执行方法
G[pathname](req,res)
}
else{
res.writeHead(404, {'Content-Type': 'text/html;charset="utf-8"'});
res.end('当前页面不存在');
}
}
app.get = (string,cb)=>{
// 注册方法
G[string] = cb
}
module.exports = app
(2)在web服务器中使用封装的app模块和get方法:
var http = require('http');
const app = require('./module/route');
http.createServer(app).listen(8080);
app.get('/',(req,res)=>{
res.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
res.end('首页');
})
app.get('/login',(req,res)=>{
res.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
res.end('执行登录操作');
})
app.get('/news',(req,res)=>{
res.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
res.end('新闻页面');
})
console.log('Server running at http://127.0.0.1:8080/');
3. 封装post请求
3.1 使用闭包,避免注册方法变量暴露
let server = ()=>{
let G = {}
let app = (req,res)=>{
const {url} = req
const {host} = req.headers
const myURL = new URL(url,`http://${host}`)
pathname = myURL.pathname
pathname = pathname === '/index.html'?'/':pathname
if(G[pathname]){
// 执行方法
G[pathname](req,res)
}
else{
res.writeHead(404, {'Content-Type': 'text/html;charset="utf-8"'});
res.end('当前页面不存在');
}
}
app.get = (string,cb)=>{
// 注册方法
G[string] = cb
}
return app
}
module.exports = server()
3.2 封装post方法
let server = ()=>{
let G = {}
G._get = {}
G._post = {}
let app = (req,res)=>{
const {url} = req
const {host} = req.headers
const myURL = new URL(url,`http://${host}`)
pathname = myURL.pathname
pathname = pathname === '/index.html'?'/':pathname
// 获取请求类型
let method = req.method.toLowerCase()
if(G['_'+method][pathname]){
if(method === 'get'){
G['_'+method][pathname](req,res)
}
else{
let postData = ''
req.on('data',(chunk)=>{
postData += chunk
})
req.on('end',()=>{
res.body = postData
G['_'+method][pathname](req,res)
})
}
}
else{
res.writeHead(404, {'Content-Type': 'text/html;charset="utf-8"'});
res.end('当前页面不存在');
}
}
app.get = (string,cb)=>{
// 注册方法
G._get[string] = cb
}
app.post = (string,cb)=>{
// 注册方法
G._post[string] = cb
}
return app
}
module.exports = server()
3.3 在web服务器中使用封装的post方法
(1)在此之前,先引入需要的文件和ejs:
(2)在app.js中使用post方法:
const http = require('http');
const app = require('./module/route');
const ejs = require('ejs')
http.createServer(app).listen(8080);
app.get('/',(req,res)=>{
res.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
res.end('首页');
})
app.get('/login',(req,res)=>{
ejs.renderFile('./views/form.ejs',{},(err,data)=>{
res.writeHead(404, {'Content-Type': 'text/html;charset="utf-8"'});
res.end(data);
})
})
app.post('/doLogin',(req,res)=>{
console.log(res.body)
res.writeHead(404, {'Content-Type': 'text/html;charset="utf-8"'});
res.end(res.body);
})
console.log('Server running at http://127.0.0.1:8080/');
3.4 扩展res.send方法
在web服务器上我们经常使用到以下两句:
res.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
res.end(data);
可以一并在route.js中对res进行扩展:
function changeRes(res){
res.send = (data)=>{
res.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
res.end(data);
}
}
4. 封装web静态服务
复制demo15的代码到demo16中,并加入需要的资源文件:
(1)文件类型json
(2)css文件
4.1 封装静态web服务
完整route.js:
const fs = require('fs')
const path = require('path')
// 扩展res
function changeRes(res){
res.send = (data)=>{
res.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
res.end(data);
}
}
// 根据后缀名获取文件类型
function getFileMine(extname){
// 同步获取数据
let data = fs.readFileSync('./data/mime.json')
let mimeObj = JSON.parse(data.toString())
// 变量名属性只能通过数组的形式进行访问
console.log(mimeObj[extname])
return mimeObj[extname]
}
// 静态web服务的方法
function initStatic(req,res,staticPath){
const {url} = req
const {host} = req.headers
const myURL = new URL(url,`http://${host}`)
pathname = myURL.pathname
// 默认加载页面
pathname = pathname === '/'?'/index.html':pathname
// 获取文件后缀
let extname = path.extname(pathname)
// 2.通过fs模块读取文件
try {
let data = fs.readFileSync('./' + staticPath + pathname)
if(data){
let mime = getFileMine(extname);
res.writeHead(200, {'Content-Type': ''+ mime +';charset="utf-8"'});
res.end(data);
return true
}
return false
} catch (error) {
return false
}
}
let server = ()=>{
let G = {
_get: {},
_post: {},
staticPath:'static' // 静态web目录
}
let app = (req,res)=>{
// 扩展res方法
changeRes(res)
// 配置静态服务
let flag = initStatic(req,res,G.staticPath)
console.log('flag',flag)
if(flag){
return
}
const {url} = req
const {host} = req.headers
const myURL = new URL(url,`http://${host}`)
pathname = myURL.pathname
pathname = pathname === '/index.html'?'/':pathname
// 获取请求类型
let method = req.method.toLowerCase()
if(G['_'+method][pathname]){
if(method === 'get'){
G['_'+method][pathname](req,res)
}
else{
let postData = ''
req.on('data',(chunk)=>{
postData += chunk
})
req.on('end',()=>{
res.body = postData
G['_'+method][pathname](req,res)
})
}
}
else{
res.writeHead(404, {'Content-Type': 'text/html;charset="utf-8"'});
res.end('当前页面不存在');
}
}
// get请求
app.get = (string,cb)=>{
// 注册方法
G._get[string] = cb
}
// post请求
app.post = (string,cb)=>{
// 注册方法
G._post[string] = cb
}
// 配置静态web服务目录
app.static = (staticPath)=>{
G.staticPath = staticPath
}
return app
}
module.exports = server()
(1)首先在app中加入配置静态资源目录(即css、js等文件所在目录)的方法:
// 配置静态web服务目录
app.static = (staticPath)=>{
G.staticPath = staticPath
}
之后可以在app.js中调用
(2)加入initStatic方法,给对应的静态资源文件配上对应的响应头:
// 根据后缀名获取文件类型
function getFileMine(extname){
// 同步获取数据
let data = fs.readFileSync('./data/mime.json')
let mimeObj = JSON.parse(data.toString())
// 变量名属性只能通过数组的形式进行访问
console.log(mimeObj[extname])
return mimeObj[extname]
}
// 静态web服务的方法
function initStatic(req,res,staticPath){
const {url} = req
const {host} = req.headers
const myURL = new URL(url,`http://${host}`)
pathname = myURL.pathname
// 默认加载页面
pathname = pathname === '/'?'/index.html':pathname
// 获取文件后缀
let extname = path.extname(pathname)
// 2.通过fs模块读取文件
try {
let data = fs.readFileSync('./' + staticPath + pathname)
if(data){
let mime = getFileMine(extname);
res.writeHead(200, {'Content-Type': ''+ mime +';charset="utf-8"'});
res.end(data);
return true
}
return false
} catch (error) {
return false
}
}
(3)调用时,注意添加对应
// 配置静态服务
let flag = initStatic(req,res,G.staticPath)
if(flag){
return
}
4.2 在web服务中使用封装的静态服务
(1)app.js中app.static('public')
指定静态资源目录位置。完整app.js:
const http = require('http');
const app = require('./module/route');
const ejs = require('ejs')
http.createServer(app).listen(8080);
app.static('public')
app.get('/',(req,res)=>{
res.send('首页');
})
app.get('/login',(req,res)=>{
ejs.renderFile('./views/form.ejs',{},(err,data)=>{
res.send(data);
})
})
app.post('/doLogin',(req,res)=>{
console.log(res.body)
res.send(res.body);
})
console.log('Server running at http://127.0.0.1:8080/');
(2)form.ejs 中 <link rel="stylesheet" href="./css/style.css">
会被initStatic
方法自动解析对应资源。完整form.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 这个地址是相对app.js -->
<link rel="stylesheet" href="./css/style.css">
</head>
<body>
<h2>登录页面</h2>
<form action="/doLogin" method="post">
用户名:<input type="text" name="username">
<br>
<br>
密码:<input type="password" name="password">
<br>
<br>
<input type="submit" value="提交">
</form>
</body>
</html>