nodejs express 的基本使用

news2025/7/7 10:54:31

测试需要快速过一遍express的基本使用方法

直接安装express使用

  • express和koa的区别](https://zhuanlan.zhihu.com/p/372128788)
  • egg.js企业级开发框架
npm install exress --save

可以使用express-generator生成项目框架

$ npx express-generator

  warning: the default view engine will not be jade in future releases
  warning: use `--view=jade' or `--help' for additional options


   create : public/
   create : public/javascripts/
   create : public/images/
   create : public/stylesheets/
   create : public/stylesheets/style.css
   create : routes/
   create : routes/index.js
   create : routes/users.js
   create : views/
   create : views/error.jade
   create : views/index.jade
   create : views/layout.jade
   create : app.js
   create : package.json
   create : bin/
   create : bin/www

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=expressdemo:* npm start

使用 nodeamon 监听项目文件 的变动,当代码被修改后,nodemon会自动帮我们重启项目。只需要将node更换为使用nodemon即可

npm install nodemon -g
nodemon index.js

Basic

// index.js
const express = require('express')
const app = express()
app.get('/user', (req, res) => { res.send('get user') })
app.post('/user', (req, res) => { res.send("post user") })
app.listen(8090, () => {
    console.log("Server is listening on 127.0.0.1:8090")
})

Static

// app.use(express.static('public'))
app.use('static',express.static('public'))

Router

路由指的是客户端的请求与服务器处理函数之间的映射关系。Express 中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数

每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的 URL 同时匹配成功,则 Express 会将这次请求,转交给对应的 function 函数进行处理

注意点:(1)按照定义的先后顺序进行匹配(2) 请求类型和请求的URL同时匹配成功才会调用对应的处理函数

在这里插入图片描述

Express 不建议将路由直接挂载到 app 上,而是推荐将路由抽离为单独的模块

// router.js
const express = require('express')
var router = express.Router()
// router.get('/user/list', (req, res) => { res.send('get user') })
// router.post('/user/add', (req, res) => { res.send("post user") })
router
.get('/user/list', (req, res) => { res.send('get user') })
.post('/user/add', (req, res) => { res.send("post user") })
module.exports = router
// index.js
const express = require('express')
const userRouter = require('./router')
const app = express()
// curl 127.0.0.1/static/html/test.txt
// app.use(express.static('public')) 
app.use('static',express.static('public'))
// curl 127.0.0.1/api/user/list
// app.use(userRouter)  
app.use('/api',userRouter)
app.listen(8090, () => {
    console.log("Server is listening on 127.0.0.1:8090")
})

Middleware

Express 的中间件,本质上就是一个 function 处理函数

多个中间件之间,共享同一份 req 和 res。因此可以在上游的中间件中,统一为 req 或 res 对象添加自定义的属性或方法,供下游的中间件或路由进行使用

在这里插入图片描述

// index.js
const app = express()
const mw1 = (req,res,next)=>{
    console.log('This is a simple middleware2')
    next()
}
const mw2 = (req,res,next)=>{
    console.log('This is a simple middleware1')
    next()
}
// 局部生效中间件,只在当前路由生效
app.get('/testmw', mw1, (req, res) => { 
    res.send('success') 
})
//app.get('/testmw', mw1, mw2, (req, res) => { 
//    res.send('success') 
//})
app.get('/testmw', [mw1, mw2], (req, res) => { 
    res.send('success') 
})
// 全局生效中间件
app.use(mw1)

注意:一定要在路由之前注册中间件。客户端发送过来的请求,可以连续调用多个中间件进行处理。执行完中间件的业务代码之后,需要调用 next() 函数。为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码。连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象

5种中间件分类:① 应用级别的中间件 ② 路由级别的中间件 ③ 错误级别的中间件 ④ Express 内置的中间件 ⑤ 第三方的中间件

// 应用级别
app.get('/', mw, (req, res) => { 
    res.send('success') 
})
// 路由界别
router.use((req, res) => { 
    res.send('success') 
    next()
})

错误级别,专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。注意:错误级别的中间件,必须注册在所有路由之后

app.get('/', mw, (req, res) => { 
	throw new Error('Server internal error!')
    res.send('success') 
})
app.use((err, req, res, next)=>{
	console.log('raise error:' + err.message)
	res.send('Error ' + err.message)
})

express内置中间件

  • express.static - 托管静态资源的内置中间件
  • express.json - 解析json格式的请求
  • express.urlencoded - 解析URL-encoded格式的请求体数据
app.use(express.urlencoded({extended: false}))

自定义中间件

const qs = require('querystring')
// querystring.parse("name=whitemu&sex=man&sex=women");
// return:
// { name: 'whitemu', sex: [ 'man', 'women' ] }
functiono bodyParse(req, res, next){
    req.on('end', () => {
        const body = qs.parse(str)
        req.body = body
        next()
    })
}
app.use(bodyParse)

Cors

CORS (Cross-Origin Resource Sharing)由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器是否阻止前端 JS 代码跨域获取资源。 浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了 CORS 相关的 HTTP 响应头, 就可以解除浏览器端的跨域访问限制

在这里插入图片描述

CORS 主要在服务器端进行配置。客户端浏览器无须做任何额外的配置,即可请求开启了 CORS 的接口。

解决接口跨域问题的方案主要有两种:

  • CORS(主流的解决C方案,推荐使用)
  • JSONP(有缺陷的解决方案:只支持 GET 请求)

客户端在请求 CORS 接口时,根据请求方式和请求头的不同,可以将 CORS 的请求分为两大类:简单请求和预检请求

只要符合以下任何一个条件的请求,都需要进行预检请求: ① 请求方式为 GET、POST、HEAD 之外的请求 Method 类型 ② 请求头中包含自定义头部字段 ③ 向服务器发送了 application/json 格式的数据

在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求,所以这一 次的 OPTION 请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据

使用cors中间件解决跨域问题

如果指定了 Access-Control-Allow-Origin 字段的值为通配符 *,表示允许来自任何域的请求

npm install cors

const cors = require('cors')
app.use(cors())
...
res.setHeader('Access-Control-Allow-Origin', '*')
// res.setHeader('Access-Control-Allow-Origin', 'http://example.com')

默认情况下,CORS 仅支持客户端向服务器发送如下的 9 个请求头: Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、 Content-Type (值仅限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一) 。如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过 Access-Control-Allow-Headers 对额外的请求头进行声明,否则这次请求会失败

res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Custom-Header')

默认情况下,CORS 仅支持客户端发起 GET、POST、HEAD 请求。 如果客户端希望通过 PUT、DELETE 等方式请求服务器的资源,则需要在服务器端,通过 Access-Control-Alow-Methods 来指明实际请求所允许使用的 HTTP 方法

res.setHeader('Access-Control-Alow-Methods', '*')

express也提供了项目初始化工具express-generator

Database

Mysql

以下为可用的测试数据库示例

  • https://www.sqlfather.com/

  • https://www.yiibai.com/sql/sql-sample-database.html

  • https://github.com/q343509740/Store/blob/master/classicmodels.sql

简单的增删改查

const mysql = require('mysql');
const db = mysql.createPool({
  host     : 'xxxxxxxxxxxxxx',
  user     : 'admin',
  password : 'xxxxxxxxxx',
  database : 'xxxxx'
});
 
var  sql = 'SELECT * from users';
db.query(sql,function (err, result) {
      if(err) return console.log('[SELECT ERROR] - ',err.message);
      console.log(result);
});

const user = {username:'sam',password:'sampasswd'}
var  sql = 'INSERT INTO users (username,password) VALUES (?, ?)';
db.query(sql,[user.username, user.password],(err,results)=>{
  if(err) return console.log('[INSERT ERROR] - ',err.message)
  if(results.affectedRows === 1){ console.log('Insert data successfully');}
})

const user = {id:3, username:'aaa',password:'aaaasswd'}
var  sql = 'UPDATE users SET username=?, password=? WHERE id=?';
db.query(sql,[user.username, user.password, user.id],(err,results)=>{
  if(err) return console.log('[INSERT ERROR] - ',err.message)
  if(results.affectedRows === 1){ console.log('Insert data successfully');}
})

var  sql = 'DELETE FROM user WHERE id=?';
db.query(sql,3,(err,results)=>{
  if(err) return console.log('[INSERT ERROR] - ',err.message)
  if(results.affectedRows === 1){ console.log('Update data successfully');}
})

如果数据对象的每个属性和数据表的字段一一对应,则可以通过如下方式快速插入数据

const user = {username:'sam',password:'sampasswd'}
var  sql = 'INSERT INTO users (username,password) VALUES (?, ?)';
db.query(sql2,user2,(err,results)=>{
  if(err) return console.log('[INSERT ERROR] - ',err.message)
  if(results.affectedRows === 1){ console.log('Delete data successfully');}
})

Redis

安装和启动

sudo amazon-linux-extras install redis6
#使用默认配置文件,在6379端口
redis-server 
redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> ping
PONG

默认配置文件

$ sudo cat /etc/redis/redis.conf | grep -v "#" | grep -v -e "^$"
bind 127.0.0.1 -::1
port 6379
daemonize no
loglevel notice
logfile /var/log/redis/redis.log

连接redis

注意:redis4.0和redis3.0的语法不兼容

const redis = require('redis');

(async () => {
    const redisClient = redis.createClient({ url: 'redis://127.0.0.1:6379/0' })
    redisClient.on('ready', () => {
        console.log('redis is ready...')
    }) 
    redisClient.on('error', (err) => {
        console.log(err);
    })
    await redisClient.connect();

    const status = await redisClient.set('name', 'sam', redis.print)
    console.log(status); 
    const value = await redisClient.get('name')
    console.log(value);
    await redisClient.quit()
})()

Session

Cookie 是存储在用户浏览器中的一段不超过 4 KB 的字符串。它由naem,value和其它几个用于控制 Cookie 有效期、安全性、使用范围的可选属性组成。

客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的 Cookie,客户端会自动 将 Cookie 保存在浏览器中。 随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的 Cookie,通过请求头的形式发送给 服务器,服务器即可验明客户端的身份。由于 Cookie 是存储在浏览器中的,而且浏览器也提供了读写 Cookie 的 API,因此 Cookie 很容易被伪造

session的工作原理

在这里插入图片描述

安装 express-session 中间件,在项目中使用 Session 认证

npm install express-session

注册和使用,通过 req.session 来访问和使用 session 对象,从而存储用户的关键信息

var session = require('express-session')
app.use(session({
    secret: 'salt',
    resave: false,
    saveUninitialized: true
}))

app.post('/login', (req, res) => {
    if (req.body.username !== 'admin' || req.body.password !== 'sample') {
        return res.send({ status: 1, msg: 'fail to login' })
    }
    req.session.user = req.body
    req.session.login = true
    res.send({ status: 0, msg: 'success to login' })
})
app.post('/user', (req, res) => {
    if (req.session.login) {
        return res.send({ status: 1, msg: 'fail' })
    }
    res.send({ status: 0, username: req.session.user.username })
})
app.post('/logout', (req, res) => {
    req.session.destroy()
    res.send({ status: 0, msg: 'success to logout' })
})

Session 认证机制需要配合 Cookie 才能实现。而Cookie 默认不支持跨域访问

JWT的工作原理

用户的信息通过 Token 字符串的形式,保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用户的身份
在这里插入图片描述

JWT 通常由三部分组成,分别是 Header(头部)、Payload(有效荷载)、Signature(签名)

  • Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串。
  • Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性。

客户端收到服务器返回的 JWT 之后,通常会将它储存在 localStorage 或 sessionStorage 中。此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证。推荐的做法是把 JWT 放在 HTTP 请求头的 Authorization 字段中

Authorization: Bearer <token>

安装包, jsonwebtoken 用于生成 JWT 字符串 ,express-jwt 用于将 JWT 字符串解析还原成 JSON 对象

npm install jsonwebtoken express-jwt

使用jwt认证

const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
const secretKey = 'salt'
app.post('/login', (req, res) => {
    if (req.body.username !== 'admin' || req.body.password !== 'sample') {
        return res.send({ status: 1, msg: 'fail to login' })
    }
    res.send({
        status: 0, 
        msg: 'success to login', 
        token: jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '1h' })
    })
})

服务器可以通过 express-jwt 这个中间件,自动将客户端发送过来的 Token 解析还原成 JSON 对象

//.unless indicate which api dont't need auth
app.use(expressJWT({secret: secretKey}).unless({path:[/^\/api\//]}))

Other

pm2

PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务,如性能监控、自动重启、负载均衡等

npm install pm2 -g
pm2 start app
pm2 stop app
pm2 stop all
pm2 reload
ENV_VARIABLE=DEV pm2 restart app --update-env --log-date-format "YYYY-MM-DD HH:mm"
pm2 log app

multer

npm install multer

解析表单数据

upload.single() 是一个局部生效的中间件,用来解析 FormData 格式的表单数据

  • 将文件类型的数据,解析并挂载到 req.file 属性中
  • 将文本类型的数据,解析并挂载到 req.body 属性中
const multer = require('multer')
const path = require('path')
// init
const upload = multer({ dest: path.join(__dirname, '../uploads') })
router.post('/add', upload.single('cover_img'), addArticle)

addArticle = (req, res) => {
    console.log(req.body)
    console.log(req.file)
    res.send('ok')
}

bcryptjs

加密用户数据等

npm i bcryptjs

// 同步
const bcrypt = require('bcryptjs')
const salt = bcrypt.genSaltSync(10)
var pwd = bcrypt.hashSync('123456', salt)
var isOk = bcrypt.compareSync('123456', pwd) 
// 异步
bcrypt.genSalt(10, (err, salt) => {
  bcrypt.hash('123456', salt, (err, pwd) => {
    bcrypt.compare('123456', pwd, (err, isOk) => {
      console.log(isOk)
    })
  })
})

Promisify

将函数promise

const fs = require('fs')
const readFile = promisify(fs.readFile)
let content = await readFile('./index.html','utf8')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/19705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

call()、apply()、bind() 区别、使用场景、实现方式

目录 1. call()、apply()、bind() 三者区别 1.1 作用 1.2 参数 1.3 执行时机 2. call()、apply() 使用场景 2.1 使用 Array.prototype.push.apply(arr1, arr2) 合并两个数组 2.1.1 原理&#xff08;看了手写方法&#xff0c;或许会更有助于理解&#xff09; 2.1.2 如何…

微电网两阶段鲁棒优化(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

LeetCode刷题复盘笔记—一文搞懂62. 不同路径 63. 不同路径 II(动态规划系列第三篇)

今日主要总结一下动态规划的两道题目&#xff0c;62. 不同路径 && 63. 不同路径 II 题目一&#xff1a;62. 不同路径 题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或…

HTML CSS 网页设计作业「体育小站」(梅西足球 6页 )

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 校园篮球网页设计 | 足球体育运动 | 体育游泳运动 | 兵乓球 | 网球 | 等网站的设计与制作| HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HT…

JavaScript之PC端网页特效(55th)

在前面学习了JS基础、DOM 和 BOM 的基本操作后&#xff0c;这部分主要学习这些知识的拓展应用 1、元素偏移量 offset 系列 1、offset 概述 offset 翻译过来就是偏移量&#xff0c;我们使用 offset 系列相关属性可以动态的得到该元素的位置&#xff08;偏移&#xff09;、大小…

SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.7 SpringBoot 操作 Redis 客户端实现技术切换【jedis】

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇4 数据层解决方案4.7 SpringBoot 操作 Redis 客户端实现技术切换【je…

数据结构实验教程-第二套

5.一棵左子树为空的二叉树在先序线索化后&#xff0c;其中空的链域的个数是 a&#xff0e;不确定 b.0 c.1 d.2在先序线索化之后&#xff0c;相当于只有开始节点没有前驱&#xff0c;最后的节点没有后继&#xff0c;因此空链域只有2个&#xff0c;分别是开始节点的左孩子和最后节…

【笔试题】【day23】

文章目录第一题&#xff08;二叉树度结点的计算&#xff09;第二题&#xff08;平衡查找二叉树&#xff09;第三题&#xff08;堆的插入&#xff09;第四题&#xff08;哈希表的查找&#xff09;第五题&#xff08;大数排序&#xff09;第一题&#xff08;二叉树度结点的计算&a…

功能测试

功能测试 按照是否覆盖源代码 黑盒测试&#xff08;输入和输出&#xff09; 白盒测试&#xff08;代码内部实现逻辑&#xff09; 灰盒测试&#xff08;输入输出和代码逻辑&#xff09; 介于白盒测试和黑盒测试之间的测试&#xff0c;多用于集成阶段&#xff0c;不仅关注输入输…

智能制造与数字化工厂

智能制造是基于新一代信息技术&#xff0c;贯穿设计、生产、管理、服务等制造活动各个环节&#xff0c;具有信息深度自感知、智慧优化自决策、精准控制自执行等功能的先进制造过程、系统与模式的总称。具有以智能工厂为载体&#xff0c;以关键制造环节智能化为核心&#xff0c;…

通信用多模光纤主要有哪些类型?OM1~OM5有什么区别

1 前言 根据光纤内光信号传输模式的不同&#xff0c;光纤可分为单模光纤和多模光纤&#xff0c;见《常用通信光纤是如何分类的》一文。 在传送网和有线接入网中&#xff0c;我们接触到的光纤类型主要有&#xff1a;G.652、G.654和G.657&#xff0c;这些都是单模光纤。多模光纤…

Linux 系统启动过程

linux启动时我们会看到许多启动信息。 Linux系统的启动过程并不是大家想象中的那么复杂&#xff0c;其过程可以分为5个阶段&#xff1a; 内核的引导。运行 init。系统初始化。建立终端 。用户登录系统。init程序的类型&#xff1a; SysV: init, CentOS 5之前, 配置文件&#…

通关算法题之 ⌈二叉树⌋ 上

二叉树深度 104、求二叉树最大深度 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数&#xff0c;叶子节点是指没有子节点的节点。 示例&#xff1a; 给定二叉树[3,9,20,null,null,15,7]&#xff0c; 3/ \9 20/ \15 7返回它的最大深度 3。 解法一&#xff1a;递…

Linux进阶-Shell编程

目录 定义变量&#xff1a; 使用变量&#xff1a; 将命令的结果赋值给变量&#xff1a; 删除变量&#xff1a;unset 退出当前进程&#xff1a;exit 读取从键盘输入的数据 &#xff1a;read 对整数进行数字运算&#xff1a;(()) 逻辑与或&#xff1a; 检测某个条件是否成…

Qt QSqlQueryModel详解

1.功能概述 QSqlQueryModel是QSqlTableModel的父类。QSqlQueryModel封装了执行SELECT语句从数据库查询数据的功能&#xff0c;但是QSqlQueryModel只能作为只读数据源使用&#xff0c;不可以编辑数据。 2.常用API void clear() //清除数据模型&#xff0c;释放所有获得的数据…

投资有风险,入市需谨慎

投资有风险&#xff0c;入市需谨慎投资有风险&#xff0c;入市需谨慎股票的分类股票的分时图股票K线图股票交易规则股票趋势股票买卖机制投资有风险&#xff0c;入市需谨慎 感谢平台和大家支持&#xff0c;今天不聊技术&#xff0c;了解了解其他方面&#xff0c;比如股市&…

编程思维是一种什么思维?

hello wordl&#xff01;    keep coding&#xff01;&#x1f3c3; 学编程不是将来要当程序猿&#xff0c;而是在学习编程思维。比尔盖茨、扎克伯格、乔布斯用经验告诉我们&#xff0c;拥有编程思维的人&#xff0c;就相当于成功了一半——不但逻辑清晰心思缜密&#xff0c;…

vue + el-checkbox 单选功能

需求: 用 el-checkboc 实现单选功能并且当选中某一项时则回填到input框里,当点击 enter 键或者是 按下搜索图标按键,来实现页面搜索内容的同步展示;如图: <el-checkbox-group placeholder"请选择"size"small"v-model"checkedCols"clearablefi…

Android BLE HIDS Data ,从问询DB 到写入Android 节点的flow之二

问题点4&#xff1a;Android BLE具体连接flow 并问询DB的API flow 之第一阶段问询&#xff1b; 当前确认原生BT当作为GATT Client 连接上GATT Server时&#xff0c;在连接上后会有自动启动问询的动作(以下Tracing 基于Android 9(P), 测试 8.1的代码和Android 8.0有差异&#x…

Web大学生网页作业成品——抗击疫情网站设计与实现(HTML+CSS)实训素材

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 击疫情致敬逆行者感人类题材 | 致敬逆行者网页设计作品 | 大学生抗疫感动专题网页设计作业模板 | 等网站的设计与制作 | HTML期末大学生网页设计作业 …