最近学习了一些nodejs中的知识,整理一下,也只是一部分,后面学到用到慢慢更新
命令行窗口(小黑屏)、cmd窗口、终端
window + R --> cmd --> 回车
-- 常用的指令:
dir 列出当前目录下的所有文件
cd 目录名 进入到指定的目录
md 目录名 创建一个文件夹
rd 目录名 删除一个文件夹
cls 清空终端
-- 目录
. 表示当前目录
..表示上一级目录
node.js三大模块:内置模块,自定义模块,第三方模块
path模块
path.join(__dirname,path1,path2,...)
(路径拼接)
path. basename(path)
(从一个文件路径中获取到文件的名称)
path. extname(path)
(获取路径中的扩展名)
__dirname
(总是返回被执行的 js 所在文件夹的绝对路径)
__filename
(总是返回被执行的 js 的绝对路径)
process.cwd()
(总是返回运行 node 命令时所在的文件夹的绝对路径)
const path = require('path')
let testPath = 'a/b/c/file.txt'
/*
* basename
*/
console.log(path.basename(testPath)) // file.txt
console.log(path.basename(testPath,'.txt')) // file
/*
* extname
*/
console.log(path.extname(testPath)) // .txt
/*
* __dirname
*/
console.log(__dirname) //D:\node-test
/*
* __filename
*/
console.log(__filename) //D:\node-test\path.js
/*
* join()
*/
console.log(path.join(__dirname,'a/b/c')) //D:\node-test\a\b\c
fs模块(File System)
fs.readFile(path,'utf8',callback);
fs.readFileSync(path,'utf8')
(异步读取文件)
(同步读取文件,无callback,有返回值。返回值即读取的内容)
fs.writeFile(path,data,'utf8',callback)
fs.writeFileSync(path,data,'utf8')
(异步写入文件,第三个参数如果不写,默认为utf8)
(同步写入文件,无callback)
下面是个小练习(异步演示)。我会把txt1.txt中的内容(小红=90 小明=80 小兰=20 小红=70 小敏=50),写到text/text1.txt中
const fs = require('fs')
const path = require('path')
var fromPath = path.join(__dirname,'txt1.txt')
var toPath = path.join(__dirname,'text/text1.txt')
fs.readFile(fromPath,'utf8',(err,data) => {
if(err) {
console.log(err,'读文件错误-->>')
}else {
console.log(data,'读文件成功-->>')
handlerDataWrite(data)
}
})
function handlerDataWrite(dataStr) {
let targetDataStr = dataStr.split(' ').map(item => item.replace('=',':')).join('\r\n')
fs.writeFile(toPath,targetDataStr,(err,data) => {
if(err) {
console.log(err,'写入失败')
}else {
console.log('写入成功')
}
})
}
写完之后就是下面这个形式:
fs.readdir(path,callback)
fs.readdirSync(path)
(异步读取目录,读到一个包含该路径下的文件和文件夹的数组)
(同步读取目录,无callback,返回一个包含该路径下的文件和文件夹的数组)
fs.readdir(path.join(__dirname,'pages'),(err,files) => {
if(err) {
console.log(err,'err--->>')
}else {
console.log(files,'files--->>>') // [ 'index.html', 'login.html', 'page' ] files--->>>
}
})
let dirs = fs.readdirSync(path.join(__dirname,'pages'))
console.log(dirs,'读取的目录->') // [ 'index.html', 'login.html', 'page' ] 读取的目录->
fs.rmdir(path,{recursive:true,force:true},callback)
fs.rmdirSync(path,{recursive:true,force:true})
(异步删除目录,如果不写第二个参数,则只能删除空文件夹,有回调函数)
(同步删除目录,如果不写第二个参数,则只能删除空文件夹,无回调函数)
fs.mkdir(path,[mode],callback)
fs.mkdirSync(path,[mode])
(异步创建目录,mode为目录权限(读写权限),默认为0777)
(同步创建目录,创建目录,mode为目录权限(读写权限),默认为0777)
fs.unlink(path,callback)
fs.unlinkSync(path)
(异步删除文件,有回调函数)
(同步删除文件,无回调函数)
fs.stat(path,callback)
fs.statSync(path)
stat方法的参数是一个文件或目录,它产生一个对象,该对象包含了该文件或目录的具体信息。我们往往通过该方法,判断正在处理的到底是一个文件,还是一个目录
isDirectory()
(返回一个布尔值,判断是否为文件夹)
isFile()
(返回一个布尔值,判断是否为文件)
下面是个例子:我会把根路径下modules下的dist文件夹替换为根路径的dist文件夹
const fs = require('fs')
const path = require('path')
let modulesPath = path.join(__dirname,'modules/dist')
let distPath = path.join(__dirname,'dist')
fs.rmdirSync(modulesPath,{ //递归删除modules下的dist目录
recursive:true,
force:true
})
let dirs = [{
absolutePath:distPath,
realtivePath:''
}]
for(let dir of dirs) {
fs.mkdirSync(path.join(modulesPath,dir.realtivePath)) //在目标路径下创建目录
let names = fs.readdirSync(dir.absolutePath) //读取源目录
console.log(names)
for(let name of names) {
let stats = fs.statSync(path.join(dir.absolutePath,name)) //读取源文件(文件夹)
if(stats.isDirectory()) { //如果是文件夹,就push进去,然后会在目标路径下再次创建同样名称目录
dirs.push({
absolutePath:path.join(dir.absolutePath,name),
realtivePath:path.join(dir.realtivePath,name)
})
}else if(stats.isFile()) { //如果是文件,就直接读取该文件,然后将读取的数据写入目标文件
let readData = fs.readFileSync(path.join(dir.absolutePath,name))
fs.writeFileSync(path.join(modulesPath,dir.realtivePath,name),readData) //写入文件。注意:如果该路径没有此文件名称,则会创建该文件
}
}
}
os模块(操作系统模块)
os.cpus()
返回计算机中cpu相关信息
os.totalmem()
(返回计算机中的可用内存,返回的是B(字节);/1024=KB/1024=MB/1024=G
os.EOL
一个字符串常量,定义操作系统相关的行末标志:
- \n 在 POSIX 系统上
- \r\n 在 Windows系统上
os.arch()
返回一个字符串, 表明Node.js 二进制编译 所用的 操作系统CPU架构
现在可能的值有:
arm,
arm64
, ia32
, mips
, mipsel
, ppc
, ppc64
, s390
, s390x
, x32
, x64
, 和 x86
os.homedir()
以字符串的形式返回当前用户的home目录
os.hostname()
以字符串的形式返回操作系统的主机名
os.networkInterfaces()
返回一个对象,包含只有被赋予网络地址的网络接口;
在返回对象的每个关键词都指明了一个网络接口;
返回的值是一个对象数组, 每个都描述了赋予的网络地址;
被赋予网络地址的对象包含的属性:
- address(被赋予的 IPv4 或 IPv6 地址)
- netmask (IPv4或Ipv6子网掩码)
- family (IPv4 或 IPv6)
- mac (网络接口的MAC地址)
- internal (如果网络接口是loopback或相似的远程不能用的接口时,值为true,否则为false)
- scopeid (IPv6 数字领域识别码 (只有当family 是 IPv6时可用))
- cidr (返回当前域名及端口)
os.platform()
返回一个字符串, 指定Node.js编译时的操作系统平台 ;
可能的值有:
- aix
- darwin
- freebsd
- linux
- openbsd
- sunos
- win32
os.release()
返回一个字符串, 指定操作系统的发行版
os.tmpdir()
返回一个字符串, 表明操作系统的 默认临时文件目录
os.type()
返回一个字符串,表明操作系统的名字.举个例子 'Linux' 在 Linux系统上, 'Darwin' 在 macOS 系统上 , 'Windows_NT' 在 Windows系统上
http模块
// http 模块
const http = require('http')
const fs = require('fs')
const path = require('path')
var server = http.createServer()
var loginPath = path.join(__dirname,'pages/login.html')
server.on('request',(req,res) => {
let url = req.url,
let method = req.mathod
console.log(`请求的路径->${url},请求方式->${method}`)
if(url == '/login') {
// res.setHeader('content-type','text/plain;charset=utf-8')
fs.readFile(loginPath,(err,data) => {
if(err) {
console.log(err,'读文件错误')
}else {
console.log('读文件成功')
res.end(data)
}
})
}else if(url == '/jxhtml') { //解析html标签
res.setHeader('content-type','text/html;charset=utf-8')
res.end('<h1>解析html标签</h1>')
}
})
server.listen(3000,() => {
console.log('服务器跑起来~~~~')
})
CommonJS模块化规范
nodejs遵循了CommonJS模块化规范,CommonJS规定了模块的特性和各模块之间如何相互依赖
CommonJS规定:
① 每个模块内部,module变量代表当前模块
② module变量是一个对象,它的exports属性(即module.exports)是对外的的接口
③ 加载每个模块,其实是加载该模块的module.exports属性。require()方法用于加载模块
Express
Express是基于Node.js平台,快速开发、开放、极简的Web开发框架
Express作用和Node.js内置模块http类似,是专门用来创建Web服务器的
本质就是一个npm上的第三方包,提供了快速创建Web服务器的便捷方法
http内置模块用起来复杂,开发效率低,Express是基于内置的http模块进一步封装出来的,能够极大的提高开发效率
npm install express --save
const express = require('express')
const app = express()
app.get('/getList', (req,res) => {
// res.setHeader('Access-Control-Allow-Origin','*') //允许跨域
res.send({name:'哈哈哈',id:1,text:'上天了'})
})
app.post('/login',(req,res) => {
// res.setHeader('Access-Control-Allow-Origin','*') //允许跨域
res.send('xxxxx')
})
app.listen(3000, () => {
console.log("Server running at http://127.0.0.1")
})
Express路由
为了方便对路由进行模块化管理,Express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块,创建步骤:
① 创建路由模块对应的js文件
② 调用expross.Router()函数创建路由对象
③ 向路由对象上挂载具体的路由
④ 使用 module.exports 向外共享路由对象
⑤ 使用app.use()函数注册路由模块
中间件
(非错误中间件)
① 一定要在路由之前注册中间件
② 客户端发送过来请求,可以连续调用多个中间件进行处理
③ 执行完中间件的业务代码后,不要忘记调用next()函数
④ 为了防止代码逻辑混乱,调用next()函数后不要再写额外的代码
⑤连续调用多个中间件时,多个中间件,共享 req 和 res 对象
(错误中间件)
作用:专门用来捕获整个项目发生的异常错误,从而防止项目异常崩溃的问题
格式:错误级别中间件的function函数中,必须有四个形参,从前到后,分别是(err,req,res,next)
throw new Error("服务器内部出错") :抛出一个自定义的错误后,回去回调错误中间件,err.message就是上面("服务器内部出错")
注意:错误级别中间件,必须注册在所有路由之后
Express内置中间件
express.static
快速托管静态资源的内置中间件,例如:HTML文件、图片、css样式等(无兼容性)
express.json()
解析JSON格式请求体数据(有兼容性,仅在4.16.0+ 版本可用)
express.urlencoded()
解析URL-encoded格式的请求体数据(有兼容性,仅在4.16.0+ 版本可用)
CORS跨域资源共享
使用cors中间件解决跨域问题
cors是Express的一个第三方中间件,通过安装和配置cors中间件,可以很方便的解决跨域问题
使用步骤:
① 运行npm install cors 安装中间件
② 使用 const cors = require("cors") 导入中间件
③ 在路由之前调用 app.use(cors()) 配置中间件
注意事项:
① CORS主要在服务端配置,客户端浏览器无需做任何额外配置,即可请求
② CORS在浏览器有兼容性。只有支持XMLHttpRequest Level2的浏览器,才能正常访问开启了cors的服务端接口(例如:IE10+、Chrome4+、FireFox3.5+)
const cors = require('cors')
// 配置cors中间件(一定是在路由之前) 解决跨域问题 客户端不需要做处理
app.use(cors())
Express中使用JWT
1.安装 JWT相关的包
npm install jsonwebtoken express-jwt
其中:
jsonwebtoken 用于生成 JWT字符串
express-jwt 用于将 JWT 字符串解析还原成JSON对象
2.导入 JWT 相关的包
使用 require 函数分别导入 JWT 相关的两个包
// 1. 导入用于生成 JWT 字符串的包
const jwt = require('jsonwebtoken')
// 2. 导入用于将客户端发过来的 JWY 字符串 解析还原还原成 JSON 对象的包
const expressJWT = require('express-jwt')
3.定义 secret 密钥 (本质就是一个字符串)
为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,我们需要定义一个用于加密和解密的 secret 密钥:
① 当生成 JWT 字符串的时候,需要使用secret密钥对用户信息进行加密,最终得到加密好的字符串
② 当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行加解密
// 定义一个 secret 密钥 本质:就是一个字符串
const secretKey = 'itheima No ^_^'
4. 在登录成功后生成JWT 字符串,响应给客户端:
// 登录接口
router.post('/login', (req, res) => {
const userInfo = req.body // 用户信息(包含用户名、密码等)
// 登录失败的情况
if(userInfo.username !== 'admin' || userInfo.password !== '123456') { // 去数据库查询 有没有这个用户 或者 密码是否正确
return res.send({
status: 400,
message: '登录失败!'
})
}
/**
* 登录成功的情况
* 调用 jsonwebtoken 中的 sign() 方法生成 jwt 字符串 发送给客户端
* 参数1: 用户的信息对象 (为了保证安全,不要把密码加密到token字符串中)
* 参数2: 加密的密钥
* 参数3: 配置对象,可以配置当前token的有效期
*/
const tokenStr = jwt.sign({ username: userInfo.username }, secretKey, { expiresIn: '10h' })
res.send({
status: 200,
message: '登录成功!',
token: tokenStr
})
})
5.将 jwt 字符串还原成 JSON 对象
客户端每次在访问那些有权限接口的时候,都需要主动通过请求头中的Authorization字段,将token字符串发送到服务器进行身份认证。
此时,服务器可以通过express-jwt这个中间件,自动将客户端发送过来的Token解析还原成JSON对象:
// 使用app.use()来注册中间件
// expressJWT({ secret: secretKey, algorithms: ['HS256'] }] 用来解析token的中间件,
//其中algorithms设置jwt的算法。一般HS256为配置algorithms的默认值
//.unless({ path: ['/login'] }) 用来指定哪些接口不需要访问权限
/**
* 注册解析 jwt 字符串还原成json对象的 中间件
* 只要配置了这个中间件 就可以把解析出来的用户信息,挂载到req.user 属性上
*/
app.use(expressJWT({ secret: secretKey, algorithms: ['HS256'] }).unless({ path: ['/login'] }))
6. 使用 req.user 获取用户信息
除了login接口 其他的携带token接口的 都可以通过 req.user 访问到当前的用户
7. 捕获解析 JWT失败后 产生的错误
当使用express-jwt解析Token字符串时,如果客户端发送过来的Token 字符串 过期或者不合法,会产生一个解析失败的错误,影响项目的正常运行,我们可以通过Express的全局错误中间件来捕获这错误,并进行相关的处理,示例代码:
/**
* 全局错误中间件
* 必须写在所有接口之后
* 捕获错误
*/
app.use((err, req, res, next) => {
console.log('触发了全局错误中间件')
// token 解析失败导致的错误
if(err.name == 'UnauthorizedError') {
return res.send({
status: 401,
message: '无效的token'
})
}
// 其他错误
res.send({
status: 500,
message: err.message || '未知错误'
})
})
补充:客户端发送请求 在请求头上携带token的时候 一般要在前面加上Bearer,然后空格隔开,后面是token
在项目中操作MySQL
安装 mysql
npm install mysql
导入mysql、建立Mysql 数据库的连接关系
// 导入mysql模块
const mysql = require('mysql')
// 建立 Mysql 数据库的连接关系
const db = mysql.createPool({ //或者createConnection
host: '127.0.0.1', //数据库的 IP 地址
user: 'root', // 登陆数据的账号
password: 'admin', // 登录数据库的密码
database: 'my_db_01' // 指定要操作哪个数据库
})
测试 Mysql能否正常工作
/**
* 测试 Mysql 能否正常工作
*/
db.query('select 1', (err, results) => {
// Mysql模块工作期间报错了
if(err) return console.log(err.message)
// 能够正常的执行Mysql 语句
console.log(results)
})
插入数据(增)
/**
* 插入数据(增)
*/
// 1. 要插入到 users 表中的数据对象
const user = { username: 'xxx', password: 'xxx' }
// 2.待执行的 SQL 语句 , 其中 ? 表示占位符
const sqlStr = 'INSET INTO users (username, password) VALUES (?, ?)'
// 使用数组的形式, 依次为 ? 占位符 指定具体的值
db.query(sqlStr, [user.username, user.password], (err, results) => {
if(err) return console.log(err.message) //失败
if(results.affectedRows === 1) {
console.log('插入数据成功')
}
})
// 1. 要插入到 users 表中的数据对象(便捷方式)
const user = { username: 'xxx', password: 'xxx' }
// 2.待执行的 SQL 语句 , 其中 ? 表示占位符
const sqlStr = 'INSET INTO users SET ?'
// 使用数组的形式, 依次为 ? 占位符 指定具体的值
db.query(sqlStr, user, (err, results) => {
if(err) return console.log(err.message) //失败
if(results.affectedRows === 1) {
console.log('插入数据成功')
}
})
查询数据(查)
sql语句 select * from users order by id desc(按照id进行降序的排序 asc表示升序,默认是升序排序,多重排序用逗号隔开即可)
/**
* 查询数据 (查)
*/
// 查询users表中所有数据 (* 代表所有 想查询某些个字段 将 * 替换为对应的key 即可)
db.query('SELECT * FROM users', (err, results) => {
if(err) return console.log(err.message)
// 查询成功
console.log(results)
})
更新数据(改)
// 要更新的数据对象
const user = { id: 7, username: 'xxx', password: 'xxx'}
// 要执行的SQL语句
const sqlStr = 'UPDATE users SET username=?, password=? WHERE id=?'
db.query(sqlStr, [user.username, user.password, user.id], (err, results) => {
if(err) return console.log(err.message) //失败
if(results.affectedRows === 1) {
console.log('更新数据成功')
}
})
// 要更新的数据对象(便捷方式)
const user = { id: 7, username: 'xxx', password: 'xxx'}
// 要执行的SQL语句
const sqlStr = 'UPDATE users SET ? WHERE id=?'
db.query(sqlStr, [user, user.id], (err, results) => {
if(err) return console.log(err.message) //失败
if(results.affectedRows === 1) {
console.log('更新数据成功')
}
})
删除数据(删)
/**
* 删除数据 (删)
*/
// 要执行的 SQL 语句
const sqlStr = 'DELETE FROM users WHERE id=?'
db.query(sqlStr, 7, (err, results) => {
if(err) return console.log(err.message)
if(results.affectedRows === 1) {
console.log('删除数据成功')
}
})
/**
* 标记删除(使用 UPDATE 语句 代替 DELETE 语句; 只更新数据的状态, 并没有真正删除)
*/
db.query('UPDATE USERS SET status=1 WHERE id=?', 6, (err, results) => {
if(err) return console.log(err.message)
if(results.affectedRows === 1) console.log('删除数据成功')
})