# Node开发框架——Express 注意项: * 引入自定义模块要用相对路径, 如`./` ## 简介 官网: https://www.expressjs.com.cn/ > Express是一个Node.js Web 应用程序开发框架 > 最肤浅理解: 路由框架 核心特性: * **路由**: 定义了路由表用于执行不同的 HTTP 请求动作。 * **中间件**: 本质是一个函数, 在收到请求和返回响应的过程中进行处理 * **模版引擎**: ...... ## 类似的框架 fastify koa koa2 ## 使用 ```js //初始化, 创建package.json文件 npm init -y //安装express到项目 npm i express -S ``` ```js //引入Express框架 const express = require('express') //创建一个web服务器 const app = express() //给app对象注册一个中间器,use是一个内置中间件,可以用来监听get和post请求 app.use((req, res) => res.send('Hello World!')) //开启服务器并监听端口 app.listen(3000, ()=>{ console.log('http://127.0.0.1:300') }) ``` ## 使用express-generator生成项目骨架 ...... ## 中间件 中间件功能可以执行以下任务: * 执行任何代码。 * 更改请求和响应对象。 * 结束请求-响应周期。 * 调用堆栈中的下一个中间件函数。 中间件的分类: * 应用层中间件 * 路由器级中间件 * 错误处理中间件 * 内置中间件 * 第三方中间件 ### 应用层中间件 使用`use()`或者`METHOD()`绑定函数到应用程序实例app上, METHOD 包括get和post * ==中间件是有顺序的== * next() 放行, 触发后续的中间件. 不放行, 后面的中间件都不执行. * next() 不能在响应处理完毕后调用. 举例: res.send() 后不能再接 next() ```js const app = express() //每个请求都会执行 app.use(function (req, res, next) { console.log('Time:', Date.now()) next() }) //访问根目录时会执行 app.use('/', (req, res, next) { console.log('Time:', Date.now()) next() }) ``` ### 路由器中间件 使用`router.use()`和`router.METHOD()`函数加载路由器级中间件。 ```js // 创建路由对象 const router = express.Router() router.get('/', function (req, res) { res.send('hello, user!') }) //安装路由 app.use(router) ``` ### 错误处理中间件 ```js //错误处理中间件始终带有四个参数, 不能省略 app.use(function (err, req, res, next) { console.error(err.stack) res.status(500).send('Something broke!') }) //要写在路由安装之后 ``` ### 内置中间件 Express具有以下内置的中间件功能: * express.static : 提供静态资产,例如HTML文件,图像等。 ```js // 中间件有顺序, 哪个在前, 哪个先生效 //实现静态页面的自动访问,它会默认打开views下面的index.html app.use('/', express.static(path.join(__dirname, 'views'))) // 响应静态资源 app.use(express.static('public')) ``` * express.json: 。它使用JSON有效负载分析传入请求,并基于body-parser ```js app.use(bodyParser.json()) //接受一个对象或数组,并在发送之前将其转换为JSON res.json({ code: 204, msg: '注册失败' }) ``` ### 第三方中间件 通过使用第三方中间件从而为Express应用增加更加功能 安装所需要功能的node模块,并在应用中加载,可以在应用级加载,也可以在路由级加载。 * bodyParse: 处理程序之前,在中间件中对传入的请求体进行解析 ```js //先安装再使用 npm i body-parser -S ``` ```js const bodyPsrse = require('body-parser') ... //bodyParser.json是用来解析json数据格式的 app.use(bodyParser.json()) //bodyParser.urlencoded则用来解析我们通常的form表单提交的数据 app.use(bodyParser.urlencoded({ extended: false //设置为false采用querystring库,如果设置为true那么采用qs库, 官方建议false })) //body-parse不支持FormData, 但post的其他数据都可以用它来接收 //get参数用express自带的req.query接收 ``` ## 路由 参数: * 请求方式: ```js app.get() app.post() app.put() app.delete() app.all() ``` * 请求路径: 只匹配对应路径的请求进行处理 ```js app.get('/', function (req, res) { res.send('只响应根路由/') }) app.get('/random.text', function (req, res) { res.send('只响应/random.text') }) ``` * 处理方法: ```js //单个回调函数 app.get('/example/a', function (req, res) { res.send('Hello from A!') }) //多个回调函数可以处理一条路由, 使用next() app.get('/example/b', function (req, res, next) { console.log('the response will be sent by the next function ...') next() }, function (req, res) { res.send('Hello from B!') }) ``` ## 项目 ``` //1.规划项目文件夹 Demo -data 数据 -node_modules 第三方模块 -public 公共资源 -css -images -js -views 静态页面资源 -index.html 首页 -register -app.js 程序入口 -handler.js 业务处理模块 -router.js 路由表 -package.json -package-lock.json //2. 引入内置模块 //3. 创建服务器, 监听端口 //4. 响应页面资源 //5. 静态资源托管 //6. 添加路由表 router.js //7. 业务功能处理 handle.js //8. 安装路由 ``` ### 程序入口 `app.js` ```js const express = require('express') const path = require('node:path') const router = require('./router') const bodyParser = require('body-parser') let app = express() app.listen(3002, () => { console.log('127.0.0.1:3002') }) // 添加body-parse解析器,使用它进行处理post方式所传递的参数 app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: false })) //static中间函数实现静态页面的自动访问 app.use('/', express.static(path.join(__dirname, 'views'))) //响应静态资源 app.use(express.static('public')) //安装路由功能 app.use(router) // 路由可以安装多个, 中间件都是有顺序的 // app.use('url1', router1) // 都在url1下, 路径为url1/xxx // app.use('url2', router1) ``` ### 路由表`router.js` ```js // 引入express const express = require('express') const handler = require('./handler') // 创建路由对象 const router = express.Router() // 添加(挂载)路由配置 router.post('/register', (req, res) => { handler.userRegister(req, res) }) // 暴露 module.exports = router ``` ### 业务处理 `handler.js` ```js const fs = require('node:fs/promises') const path = require('node:path') // 专门进行业务处理 module.exports = { userRegister: function (req, res) { // express封装了get请求参数的接收 // req.query 是express自带的 // 接收用户参数 以前req.on('data') req.on('end') > 参数的类型转换 // body:就是post方式所传递的参数,它是一个对象(参数的接收和参数的转换) // console.log(req.body) // 存储到指定的数据文件中,并响应结果 getRegister: (req,res) => { console.log('req.body', req.body) fs.readFile(path.resolve(__dirname, './data/users.json'),'utf-8') .then(data=>{ let arr = JSON.parse(data) arr.push(req.body) return fs.writeFile(path.resolve(__dirname,'./data/users.json'),JSON.stringify(arr, null, ' ')) }) .then(result => { res.json({code:200,msg:'注册成功'}) }) .catch(err=>{ console.log('err', err) res.json({code:204,msg:'注册失败'}) }) } } ``` * 重定向: res.redirect(path) * 设置响应状态码: `res.status(404).send('404 not found')` ## 用户认证 另建笔记 ## 模板引擎(了解) 服务端渲染: | | 优势 | 劣势 | | ------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | 服务端 | 1. 前端耗时少
2. 有利SEO
3. 客户端速度更快, 耗电更少 |1. 不利于前后端分离, 开发效率低
2. 请求多时, 对服务器造成访问压力| 折中方案: "首屏渲染", 首屏加载用服务器渲染,其他的用客户端渲染 模板引擎有很多: [Template Engines](https://expressjs.com/en/resources/template-engines.html) 其中: 前台模板引擎 express-art-template [介绍 - art-template](https://aui.github.io/art-template/zh-cn/docs/) **使用步骤:** * 下载安装 * 先下载art-template:`npm i art-template -S` * 再下载express-art-template: `npm i express-art-template -S` * 给express框架设置默认的模板引擎 ```js // 注册模板引擎 app.engine('html', require('express-art-template')); //第一个参数是渲染文件的格式 app.set('view options', { debug: process.env.NODE_ENV !== 'production' //设置模板引擎的使用环境, 开发和上线都要使用 }); ``` * 设置模板引擎文件的默认路径 ```js // 如果不设置,默认也是views app.set('views',path.join(__dirname, './views') app.set('view engine', 'html') //设置模板文件的类型 ``` 示例如下: ```js // 1. 引入框架模块 var express = require('express') // 2. 创建服务器 var app = express(); // 注册模板引擎 //engine就是个中间件 app.engine('html', require('express-art-template')); app.set('view options', { debug: process.env.NODE_ENV !== 'production' }); // 如果不设置,默认也是views //app.set('views','./views') // 3. 开启服务器并监听端口 app.listen(3000,()=>{ console.log('http://127.0.0.1:3000') }) app.get('/login',function(req,res){ // 引入express-art-template之前,会在Res上挂载一个render方法,可以实现页面的渲染 // render实现的功能有: 读取文件,动态渲染,响应, 所以不用fs去读文件 // res.render(要渲染的文件路径,[数据]) res.render('login.html', { list: [{ name: 'jack', age: 20 }, { name: 'rose', age: 18 }] }) }) ``` `/views/login.html` ```html Document
用户名:
密码
``` ## node操作mysql ```js const express = require('express') // 导入MySQL模块 const mysql = require('mysql'); const app = express() app.listen('3008', () => { console.log('http://127.0.0.1:3008') }) // 创建一个数据库连接, 用createPool不用createConnection var connection = mysql.createPool({ host: '127.0.0.1', // 数据库服务器的地址 user: 'root', // 数据库的账号 password: 'root', // 数据库的密码 database: 'mybase58' // 数据库名称, 不是表的名称 }); // 连接数据库或打开数据库 //connection.connect(); //不用手动打开链接 app.get('/', (req, res) => { //查 // let sql = `select * from users` //增 // let obj = { // name: '赵六', // age: '26', // address: '赵家村' // } // let sql = `insert into users (name, age, address) value ('${obj.name}', '${obj.age}', '${obj.address}')` //注意: value里面的字符串要主动加引号 //除了查询, 增删改的fields没有定义 //查询返回的是所有的数据, 增删改返回的是okPacket //改 // let obj = { // id: 8, // age: 25 // } // let sql = `update users set age = '${obj.age}' where id = '${obj.id}'` //删 let obj = { name: '赵六', } let sql = `delete from users where name = '${obj.name}'` connection.query(sql, (err, results, fields) => { if (err) { console.log(err) throw err } else { console.log(results) console.log(typeof results) console.log('-----------------') console.log(fields) } }) }) ``` 简洁写法 ```js //参数查询 let sql = 'select * from users where name = ?' connection.query(sql, ['张三'], (err, results, fields) => { if (err) { console.log(err) throw err } else { console.log(results) } }) //增 let obj = { name: '赵六1212', age: '26', address: '赵家村' } let sql = `insert into users set ?` connection.query(sql, obj, (err, results, fields) => { if (err) { console.log(err) throw err } else { console.log(results) } }) //改 let obj = { id: 5, name: '赵六1212', age: '27', address: '赵家村' } let sql = `update users set ? where id = ?` connection.query(sql, [obj, obj.id], (err, results, fields) => { //第二个参数, 如果只有一个是对象, 直接写, 如果是多个参数, 加上方括号成数组 if (err) { console.log(err) throw err } else { console.log(results) } }) //删除 let sql = 'delete from users where id = ?' connection.query(sql, [2], (err, results, fields) => { if (err) { console.log(err) throw err } else { console.log(results) } }) let sql = 'delete from users where name = ?' connection.query(sql, ['李八'], (err, results, fields) => { if (err) { console.log(err) throw err } else { console.log(results) } }) ```