express学习笔记
项目搭建
简单的入门步骤: hello world!
- 创建一个项目文件夹
mkdir express-demo
- 进入文件夹
cd express-demo
- 在文件夹中创建自己的模块文件夹并进入
- 生成package.json文件
npm init -y
- 安装express
npm i express
- 创建入口文件 app.js 在该文件中创建web 服务
touch app.js
- 在app.js中书写基本的web请求
const express = require('express');
const app = express();
//'/'表示根目录
//运行之后访问http://localhost:3000
app.get('/', (req, res) => {
res.send("get请求成功")
})
app.post('/', (req, res) => {
res.send("post请求")
})
//运行之后访问http://localhost:3000/user
app.put('/user', (req, res) => {
res.send('put express')
})
app.delete('/user', (req, res) => {
res.send('delete express');
})
//3000表示请求的端口号
app.listen(3000,() => {
console.log('Server running');
})
- 运行app.js(命令行要在对应文件所在目录输入);运行之后,打开对应的网页即可看到get方法中res.send中的内容;
因为浏览器地址栏只能发送get请求,如果想要测试post请求,可以借助postman工具
postman官网
node app.js
如果不想要每次修改都运行一遍node app.js 可以安装nodemon工具
npm install nodemon
运行之后每次保存都会重新刷新
nodemon app.js
因为环境的差异,有的运行上面的代码会报错.可以尝试运行
npx nodemon app.js
提示:以下是本篇文章正文内容,下面案例可供参考
一、express路由基础
一个路由由 请求方法
请求路劲
回调函数
组成
路由是指确定应用程序如何相应客户端对特定端点的请求,该特定端点是URL(或路径)和特定的HTTP请求方法(GET,POST等).
每个路由可以具有一个或多个处理程序函数,这些函数在匹配该路由时执行.
var http = require('http')
// 通过http模块 创建web应用
var app = http.createServer((req,res) => {
res.writeHead(200,{"ContentType":"text/plain"})//状态码,返回格式
res.end('Hello World');//结束响应同时 发送数据
})
// 通过监听端口启动
app.listen(3000,'localhost')
console.log('http://localhost:3000');
上面的这段代码和下面这段代码 app.get('/',function())效果相同
//req 请求对象
//res 响应对象
app.get('/', (req, res) => {
res.send("get请求成功")
})
二、请求和响应
expres使用路由回调函数的参数:request 和 response对象来处理请求和响应的数据.
express不对node.js已有的特征进行二次抽象,只是在它基础上扩展了web应用的基本功能;内部使用还是http 感兴趣可以查看nodejs官方文档中的Class: http.IncomingMessage部分
更多请求相关参数 请访问express官方文档
(一) 响应对象
res对象 表示express在收到http请求时发送的http响应.
继承于nodejs中的Class: http.ServerResponse;
原生响应
- res.statusCode = 500
- res.statusMessage = ‘mess’
- res.setHeader(‘aa’,‘bb’)
- res.write(‘hello world’)
- res.end(‘end’)
express响应
- res.status(500)
- res.set(‘aa’,‘bb’)
- res.send(‘你好 美丽的姑娘’)
跳转响应 res.redirect(‘http://baidu.com’)
下载响应 res.download(__dirname + ’ /app.json’)
json响应 res.json({ name:‘魔法’ })
响应文件内容 res.sendFile(__dirname + ’ /test.html ')
(二)状态码
- 1xx: 相关信息
- 2xx: 操作成功
- 3xx: 重定向
- 4xx:客户端错误
- 5xx:服务器错误
三、express基本用法
为方便理解,数据采用json文件保存,没有连接数据库
在app.js的同级目录下,创建一个db.json文件;
内部写入json格式的数据.示例如下:
{
"todos": [
{
"id": 1,
"title": "run"
},
{
"id": 2,
"title": "play"
},
{
"id": 3,
"title": "eat"
}
],
"user": []
}
(一)查询数据列表
在app.js中引入fs文件处理模块
const fs = require('fs')
通过**fs.readFile()**方法异步读取文件,具体步骤如下:
app.get('/todos', (req, res) => {
// 异步读取文件 (读取的文件,文件编码,回调函数)
fs.readFile('./db.json', 'utf-8', (err, data) => {
if (err) {
return res.status(500).json({
// 发送500错误代码,以json数据发送
error: err.message
})
}
const db = JSON.parse(data)
// 响应状态码
res.status(200).json(db.todos)
})
})
运行结果如下:
(二)根据id查询单个数据
通过app.get('/',()=>{})可以配置路由
/:id 表示请求参数是id
req.params.id 获取路由中的参数
find() 方法 查找文件内容
Number.parseInt() 将字符串转为int类型
具体操作步骤如下代码.返回的结果是id为1的数据
// 根据id查询单个任务
app.get('/todos/:id', (req, res) => {
fs.readFile('./db.json', 'utf-8', (err, data) => {
if (err) {
return res.status(500).json({
error: err.message
})
}
const db = JSON.parse(data)
const todo = db.todos.find(todo => todo.id === Number.parseInt(req.params.id));
if (!todo) {
res.status(404).end();//响应状态码
return
}
res.status(200).json(todo)
})
})
(三) 封装查询
为了减少代码的冗余,增加代码的可读性,可以将请求数据的方法封装到单独文件.
在app.js同级 创建一个db.js文件
将回调函数转换为异步方法
const { promisify } = require('util')
node 提供的用于处理文件和目录的路径的实用工具
const path = require('path')
path.join() 路径拼接
__dirname 用来动态获取当前文件所属目录的绝对路径
__filename 用来动态获取当前文件的绝对路径,包含当前文件
JSON.parse() 方法将数据转换为 JavaScript 对象。
具体代码实现如下
const fs = require('fs')
const { promisify } = require('util') //将callback转为promis类方法
const path = require('path')
const readFile = promisify(fs.readFile)
const dbPath = path.join(__dirname, './db.json')
//导出获取数据信息
exports.getDb = async () => {
const data = await readFile(dbPath, 'utf-8')
return JSON.parse(data)
}
封装之后需要在头部进行引用,封装的时候因为没有回调函数处理异常,所以在调用的时候需要使用try{}catch{}对异常进行捕获.
因为readFile()被转换成promise 所以需要在方法前加上async 采用异步方式调用
const { getDb } = require('./db');
//查询全部数据
app.get('/todos', async (req, res) => {
try {
const todo = await getDb()
res.status(200).json(todo.todos)
} catch (err) {
res.status(500).json({
error: err.message
})
}
})
//根据id查询
app.get('/todos/:id', async (req, res) => {
try {
const db = await getDb();
const todo = db.todos.find(todo => todo.id === Number.parseInt(req.params.id));
if (!todo) {
return res.status(404).end();
}
res.status(200).json(todo);
} catch (err) {
res.status(500).json({
error: err.message
})
}
})
(四) 数据添加
使用Postman进行数据请求;
1.获取请求体
req.body
在获取请求体之前 需要配置解析表单请求体
const express = require('express')
const app = express();
//解析:application/json
app.use(express.json());
//解析:application/x-www-form-urlencoded
app.use(express.urlencoded());
2.验证请求体
3.请求体写入json文件
push()
JSON.stringify() 将js对象转换为字符串
writeFile(file,data);
4.返回响应数据
演示代码如下:
- 写入方法封装
// 写入数据
const writeFile = promisify(fs.writeFile)
exports.saveDb = async (db) => {
const data = JSON.stringify(db);//JSON.stringify() 方法将 JavaScript 对象转换为字符串。
// const data = JSON.stringify(db,null,' ');//带格式的写入方式
await writeFile(dbPath, data);
}
- 配置解析和postman请求演示
3. 代码请求
app.post('/todos', async (req, res) => {
try {
// 1.获取客户端请求体参数
const todo = req.body
// 2.数据验证
if (!todo.title) {
return res.status(422).json({
error: 'The field title is required'
})
}
// 3.存储数据
const db = await getDb();
const lastId = db.todos[db.todos.length - 1];
todo.id = lastId ? lastId.id + 1 : 1;
db.todos.push(todo)
await saveDb(db);
// 4.成功响应
res.status(201).json(todo);
} catch (err) {
res.status(500).json({
error: err.message
})
}
})
(五)数据修改
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。
注意:如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
1.获取表单数据
2.查找到要修改的数据
3.数据组装/数据合并 将2合并到1中
Object.assign(ret1,ret2)
4.数据写入
5.返回响应数据
总体逻辑和写入相似,调用的方法也是写入的方法,代码示例如下
app.patch('/todos/:id', async (req, res) => {
try {
const ret = req.body;
const db = await getDb()
const todo_id = db.todos.find(todo => todo.id === Number.parseInt(req.params.id))
if (!todo_id) {
return res.status(404).end()
}
Object.assign(todo_id, ret)
await saveDb(db);
res.status(200).json(ret)
} catch (err) {
res.status(500).json({
error: err.message
})
}
})
(六) 数据删除
splice(index,number) 方法用于添加或删除数组中的元素。返回删除的数组
findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
findIndex() 方法为数组中的每个元素都调用一次函数执行:
当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 -1
注意: findIndex() 对于空数组,函数是不会执行的。
注意: findIndex() 并没有改变数组的原始值。
代码示例如下:
app.delete('/todos/:id', async (req, res) => {
try {
// 获取请求参数
const queryID = Number.parseInt(req.params.id);
const db = getDb();
const index = db.todos.findIndex(todo => todo.id === queryID);
if (index === -1)
return res.status(404).end();
db.todos.splice(index, 1);
await saveDb(db)
res.status(204).end();
} catch (err) {
res.status(500).json({
error: err.message
})
}
})
四、express中间件
在中间件中 包含三个参数 req res next
中间件可以执行任何代码
修改req 和 res 响应对象
结束请求响应周期
调用下一个中间件
如果当前的中间件没有结束,要用next()放权,否则程序将一直挂起。
中间件分类
应用程序级别中间件、路由级别中间件、错误处理中间件、内置中间件、第三方中间件
应用程序级别中间件
- 不关心请求路径:(在程序执行的时候会被执行到)
app.use(function (req, res, next) {
console.log('req type', req.method);
next();
})
- 限定请求路径(请求的路径要按照路由指定才能被访问)
//限定请求路径http://localhost:3001/user/1
app.use('/user/:id', function (req, res, next) {
console.log('user/req type', req.method);
next();
})
错误处理中间件
在所有的中间件之后 挂载错误处理中间件
next()中被传入除route外的任何内容,express将认为当前是错误请求,并将跳过所有剩余的无错误处理的路由和中间件函数。
router.get('/', async (req, res) => {
try {
const todo = await getDb()
res.status(200).json(todo.todos)
} catch (err) {
next(err)
}
}
//错误处理代码
// 错误处理中间件 --四个参数 缺一不可
app.use((err, req, res, next) => {
console.log('err', err);
res.status(500).json({
error: err.message
})
})
错误输出
内置中间件
express.json() 解析Content-Type为application/json格式的请求体
express.urlencoded() 解析Content-Type为application/x-www-form-urlencoded格式的请求体
express.raw() 解析Conten-Type为 application/octest-stream格式的请求体
express.text() 解析Content-Type为text/plain 格式的请求体
express.static() 托管静态资源文件
第三方中间件
express路由
express路由中的路径有多种形式可以被匹配访问。
- 字符串匹配
当访问pp.text的时候被匹配
app.post('/pp.text', (req, res) => {
res.send('string type')
})
- 字符串模式匹配
?表示可选 ab?cd 可以访问 abcd 也可以访问acd
*是任意字符
app.post('/ab?cd', (req, res) => {
res.send('abcd | acd');
})
app.post('/ab*cd', (req, res) => {
res.send('ab*cd');
})
app.post('/ab(cd)?ef', (req, res) => {
res.send('ab(cd)?ef')
})
- 正则表达式模式匹配
app.post(/b/, (req, res) => {
res.send('正则表达式')
})
路由参数
当想在路由中传递多个参数的时候,可以使用连字符-
或点 .
也可以在路由上进行参数校验
//路由校验 转义字符需要\\ http://localhost:3000/student/36
app.get('/student/:num(\\d+)', (req, res) => {
res.send('/student/:num(\\d+)')
})
总结
命令行语句
递归复制文件01 到 02
cp -r 01 02
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。