Node.js -- express 框架

发布于:2024-05-05 ⋅ 阅读:(19) ⋅ 点赞:(0)

express是一个基于Node.js平台的极简、灵活的WEB应用开发框架,官方网址:https://www.expressjs.com.cn/
简单来说,express是一个封装好的工具包,封装了很多功能,便于我们开发WEB应用(HTTP服务)

1. express 使用

// 1. 导入express 包
const express = require('express')

// 2. 创建应用对象
const app = express()

// 3. 创建路由
app.get('/home', (req, res) => {
    res.end('Hello Express')
})
// 如果请求方法为GET 且URL中路径为/home 就会执行后面的回调函数

// 4. 监听端口 启动服务器
app.listen(3000, () => {
    console.log('服务已经启动,端口3000 正在监听中...');
})

2. 路由

什么是路由?

官方定义:路由确定了应用程序如何响应客户端对特定端点的请求

2.1 路由的使用

一个路由的组成有请求方法路径回调函数组成
express中提供了一系列方法,可以很方便的使用路由,使用格式如下:

app.< method >(path,callback)

请求报文加载过来之后就开始与路由的规则开始比对;之前的判断我们是通过if / else if 来进行的,而现在express 框架对这种判断方法做了封装

app.get('/', (req, res) => {
    res.end('/')
})

app.post('/login', (req, res)=> {
    res.end('login')
})
app.post()('/login', (req, res) => {
    res.end('login')
})
// 匹配所有的方法
app.all('/test', (req, res) => {
    res.end('test')
})
// 404 响应
app.all('*', (req, res) => {
    res.end('404 not found')
})

2.2 获取请求报文参数

express框架封装了一些API来方便获取请求报文中的数据,并且兼容原生HTTP模块的获取方式

// 原生操作
console.log(req.method);
console.log(req.url);
console.log(req.httpVersion);
console.log(req.headers);
// express操作
console.log(req.path);
console.log(req.query);
// 获取ip
console.log(req.ip);
// 获取请求头
console.log(req.get('host'));

2.3 获取路由参数

路由参数指的是URL路径中的参数(数据)

app.get(‘/:id.html’,(req,res)=>{
res.send(‘商品详情,商品id为’ + req.params.id);
});

const express = require('express')

const app = express()

app.get('/:id.html', (req, res) => {
    // 获取URL 路由参数 其中路径中
    // req.params 存储了请求报文中所有路由id 
    console.log(req.params.id);
    res.setHeader('content-type','text/html;charset=utf-8')
    res.end('test')
})

app.listen(3000, () => {
    console.log('服务已经启动...');
})

2.4 路由参数练习

根据路由参数响应歌手的信息
路径结构如下
/singer/1.html
显示歌手的姓名和图片

const express = require('express')
const {singers} = require('./singers.json')

const app = express()

// 练习内容

app.get('/singer/:id.html', (req, res) => {
    // 获取路由参数
    let { id } = req.params; // 该变量是字符串形式
    // 在数组中寻找对应id的数据
    let result = singers.find(item => {
        if (item.id === Number(id)) {
            return true;
        }
    })
    console.log(result)
    // 判断:如果result 没有数据就 返回404
    if (!result) {
        res.statusCode = 404
        res.end(`<h1>404 not found</h1>`)
    }
    res.end(`
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <h1>${result.singer_name}</h1>
        <img src="${result.singer_pic}" alt="">
    </body>
    </html>
    `);
})
app.listen(3000, () => {
    console.log('服务已经启动...');
})

获取 URL 中的动态参数
通过req.params对象,可以访问到 URL 中,通过:匹配到的动态参数:

3. express 响应设置

express框架封装了一些API来方便给客户端响应数据,并目兼容原生HTTP模块的获取方式

  1. express中设置响应的方式兼容HTTP模块的方式
const express = require('express')
const app = express()
app.get('/response', (req, res) => {
    // 获取请求的路由规则
    app.get("/response", (req, res) => {
        // 1.express中设置响应的方式兼容HTTP模块的方式
        res.statusCode = 404;
        res.statusMessage = 'xxx'
        res.setHeader('abc', 'xyz');
        res.write('响应体')
        res.end('xxx')
    })

在这里插入图片描述
2. express的响应方法


    // 2.express的响应方法
    res.status(500);//设置响应状态码
    res.set('aaa', 'bbb');//设置响应头
    res.send('中文响应不乱码');//设置响应体 自动添加字符集的设置
    // 连贯操作
    res.status(404).set('xxx', 'yyy').send('你好朋友')
   
  1. express的响应方法
 // 3.其他响应
    res.redirect('http://atguigu.com')//重定向 
    res.download('./package.json');//下载响应 返回下载形式的响应
    res.json();//响应JS0N
    res.sendFile(__dirname + '/home.html')//响应文件内容
})

app.listen(3000, () => {
    console.log('服务已启动...');
})

4. 中间件

中间件(Middleware)本质是一个回调函数
中间件函数可以像路由回调一样访问请求对象(request),响应对象(response)

中间件的作用就是使用函数封装公共操作,简化代码

中间件类型:

  • 全局中间件
  • 路由中间件

4.1 全局中间件

每一个请求到达服务端之后都会执行全局中间件函数
(路由函数只有在满足请求方法和请求路径一致时才会执行回调函数)

全局中间件实践:

const express = require('express')
const fs = require('fs')
const path = require('path')
const app = express()
app.get('/home', (req, res) => {
    // 获取url ip
    let { url, ip } = req;
    // 将信息保存在record.log 中
    fs.readFileSync(path.resolve(__dirname + 'record.log'),`${url} ${ip}\r\n`)
    res.end('home')
})
app.all('*', (req, res) => {
    // 获取url ip
    let { url, ip } = req;
    // 将信息保存在record.log 中
    fs.readFileSync(path.resolve(__dirname + 'record.log'),`${url} ${ip}\r\n`)
    res.end('all')
})
app.listen(3000,()=> {
    console.log('服务启动...')
})

如果我们每创建一个路由就在其中定义url和ip 再存入到文件中,那么将会产生很多重复的代码,这时候我们可以让重复的代码部分写入全局中间件中

function recordMiddleware(req,res,next) {
    // 获取url ip
    let { url, ip } = req;
    // 将信息保存在record.log 中
    fs.readFileSync(path.resolve(__dirname + 'record.log'), `${url} ${ip}\r\n`)
    next()// 表示执行完全局中间件函数之后继续执行下面的路由中间件或者其他的全局中间件
}
// 使用全局中间件函数
app.use(recordMiddleware)
app.get('/home', (req, res) => {
    res.end('home')
})
app.all('*', (req, res) => {
    res.end('all')
})

4.2 路由中间件

// 针对/admin/setting的请求,要求URL携带code=521参数,如末携带提示『暗号错误」
// const { log } = require('console')
const express = require('express')
const path = require('path')
const app = express()
let CheckCodeMiddleware = (req, res, next) => {
    // 其中路由中间件函数 必须要加上 表示在满足条件时就可以继续执行next()
    //判断code 参数是否为666
    if (req.query.code === '666') {
        next()
    }
    else {
        res.send('不能调用')
    }
}
app.get('/home', CheckCodeMiddleware, (req, res) => {
    res.send('首页')
});
app.get('/admin', CheckCodeMiddleware, (req, res) => {
    res.send('后台')
});
app.all('*', (req, res) => {
    res.send('all')
});
app.listen(3000, () => {
    console.log('go');
})

4.3 静态资源中间件

静态资源:长时间不发生改变的资源,例如:css,js,图片…

express.static(文件夹路径) --> 返回文件中的全局中间件

app.use(express.static(__dirname+'/public'))

之前我们通过读取文件url ,拼接文件路径,读取文件,响应文件,设置meta 类型等多项操作完成了以上操作,但是在这里一行代码就搞定了!

注意事项:

  1. index.html 为默认打开的资源,也就是可以作为网站首页,因为我们在不添加路径的时候访问网站,返回的会是index.html ,但是这也反映出一个问题(也就是第二个注意事项)!
  2. 如果静态资源与路由规则同时匹配,谁先匹配谁就像响应
  3. 路由响应动态资源,静态资源中间件响应静态资源

练习:
要求:局域网内可以访问小米商城的网页

const express = require('express')
const app = express()
// 静态资源中间件
app.use(express.static(__dirname + '/小米商城'))

app.listen(3000, () => {
    console.log('服务启动');
})

在电脑上win + R 打开命令行,输入ipconfig 查询本机所在局域网,在链接了同一个局域网的手机上可以通过ip:端口号查看同一个网站(比如自己写好的小米商城网页)

5. 获取请求体数据 body-parser

express可以使用body-parser包处理请求体
第一步:安装

npm i body-parser

第二步:导入body-parser包

const bodyParser = require(‘body-parser’);

第三步:获取中间件函数

// 处理querystring格式的请求体
let urlParser = bodyParser.urlencoded({extended:false});
// 处理JSON格式的请求体
let jsonParser = bodyParser.json();

第四步:设置路由中间件,然后使用request.body来获取请求体数据

app.post('/login',urlParser,(request,response)=>{
//获取请求体数据
//console.log(request.body);
//用户名
console.log(request.body.username);
//密码
console.log(request.body.userpass);
response.send('获取请求体数据')
})

6. 防盗链

有些时候我们在网上存了某张图片的网址链接,但是并不能在自己的html 中打开,这是因为这个网站设置了防盗链(禁止该域名之外的其他网站来对该图片网站访问)

防盗链实践:
在设置防盗链之前,访问127.0.0.1:3000 和访问localhost:3000 效果是一样的,可以得到同一个网页;

// 防盗链
const express = require('express')

const app = express()

// 声明中间件
app.use((req, res, next) => {
    // 获取referer
    let referer = req.get('referer')
    if (referer) {
        // 实例化
        let url = new URL(referer)
        let hostname = url.hostname
        if (hostname !== '127.0.0.1') {
            res.status(404).send('<h1>404 Not Found</h1>')
            return
        }
    }
    next()
})

// 静态资源中间件设置
app.use(express.static(__dirname + '/public'))

app.listen(3000, () => {
    console.log('启动!');
})

7. 路由模块化

homeRouter.js:

const express = require('express')
// 创建路由对象
const router = express.Router()

// 创建路由规则
router.get('/settings',  (req, res) => {
    res.send('设置')
});
router.get('/search',  (req, res) => {
    res.send('搜索')
});
router.get('/home', (req, res) => {
    res.send('首页')
});
router.get('/admin', (req, res) => {
    res.send('后台')
});

// 暴露router
module.exports = router

模块化.js:

const express = require('express')

const homeRouter = require('./routes/homeRouter')
const app = express()
// 设置
app.use(homeRouter)

// app.all('*', (req, res) => {
//     res.send('all')
// });
app.listen(3000, () => {
    console.log('go');
})

8. 模板引擎

模板引擎是分离用户界面和业务数据的一种技术
模板引擎也是一种通用技术,多种语言都有。

8.1 了解EJS

EJS是一个高效的Javascript的模板引擎。
官网:https://ejs.co/
中文站:https://ejs.bootcss…com/

EJS 初体验:
简单理解模板引擎就是把html和js 分离开,但这里的js 指的是服务器相关的js 代码。

下面是之前的一个案例,从中我们可以发现html 和js 是混合到了一起的,而ejs 可以将他们分离开。
在这里插入图片描述

const ejs = require('ejs')
const fs = require('fs')

let china = '中国' // 1处
let weather = 'sunny'

// 声明变量
let str = fs.readFileSync('./10-test.html').toString()

// 使用ejs 渲染
let result = ejs.render(str, { china: china, weather })

console.log(result);

render(str,{}) 第二个属性是对象的形式,属性名对应str中的变量名 属性值对应1处的变量名
在这里插入图片描述

8.2 列表渲染

// 列表渲染
const { log } = require('console');
const ejs = require('ejs')
const fs = require('fs')

const xiyouji = ['冒菜','酸甜麻辣烫','凉菜','菠萝']

// 原生js 
// let str = '<ul>'
// xiyouji.forEach(item => {
//     str += `<li>${item}</li>`
// })
// str += '</ul>'

// ejs 渲染
let str = fs.readFileSync('./11-test.html').toString()
let result = ejs.render(str, { xiyouji: xiyouji })

console.log(result);

8.3 条件渲染

通过变量isLogin 决定最终的输出内容
true --> 输出< span>欢迎回来</ span>
false --> 输出< button>登录</button < button >注册</ button>

// 条件渲染
const ejs = require('ejs')
const fs = require('fs')

let isLogin = true
// 原生js
// if (isLogin) {
//     console.log('<span>come back</span>');
// } else {
//     console.log(`<button>登录</button>
//     <button>注册</button>`);
// }

//ejs
let html = fs.readFileSync('./12-test.html').toString()
let result = ejs.render(html, { isLogin: isLogin })
console.log(result);

8.4 在express 中使用ejs

const express = require('express')
const path = require('path')

const app = express()

// 1. 设置模板引擎
app.set('view engine', 'ejs')
// 2. 设置模板文件存放位置 模板文件:具有模板语法内容的文件
app.set('views',path.resolve(__dirname,'./views'))

app.get('/home', (req, res) => {
    // 3. render 响应
    // res.render('模板文件名', '数据')
    let title='在思考中学习!'
    res.render('view', {title})
})

app.listen(3000, () => {
    console.log('go!');
})

9. express-generator

express-generator 是Express 应用程序生成器,帮助我们生成express 文件骨架。

  1. 安装

npm i -g express-generator

  1. express - h/v --> 查看相关帮助
  2. express - e 文件路径 --> 添加ejs 的模板引擎支持
  3. 添加依赖

npm i

10. 文件上传

文件上传场景:
更换头像;网盘上传文件;短视频平台上传视频…

注:
enctype=“multipart/form-data” 是上传表单时表单必要的属性

处理文件上传 – 安装formidable 包

// 显示网页表单
router.get('/portrait', (req, res) => {
  res.render('portrait')
})

// 处理文件上传
router.post('/portrait', (req, res) => {
  // 创建form 对象
  const form = formidable({
    multiples: true,
    // 设置上传文件的保存目录 一般都保存在用户很容易获取到的地方
    uploadDir: __dirname + '/../public/images',
    // 保持文件后缀
    keepExtensions:true

  });