nodejs项目实战教程12——Nodejs封装Express

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>

在这里插入图片描述

下一章 nodejs项目实战教程13——MongoDB

猜你喜欢

转载自blog.csdn.net/qq_39055970/article/details/121919876