一、读取文件 
const fs = require('node:fs')
fs.readFile('1.txt', 'utf8', (err, data) => {
  console.log('data: ', data) // 失败为undefined
  console.log('err: ', err) // 成功为null,否则为错误对象
  if (err)
    return console.log(`读取失败${err.message}`)
  console.log(`读取成功${data}`)
})问题
- 相对路径在上一层读取不了文件
- 绝对路径不利于维护移植
解决
__dirname代表当前文件所处目录, 解决文件路径动态拼接问题
坑点:__dirname输出是路径是**反斜杠(\),拼接后面的路径需要写正斜杠(/)**
//__dirname:  D:\PracticeProject\Node\readfile
fs.readFile(__dirname + "/3.txt", "utf8", function (err, data) {
  __dirname + "\\\\3.txt",
    __dirname + "\\\3.txt",
    __dirname + "\\\3.txt",
    __dirname + "/\\3.txt",
    console.log("__dirname: ", __dirname + "\\3.txt"); //这四个都可以成功(?)
  console.log("__dirname: ", __dirname);
  console.log("data: ", data);
  if (err) {
    return console.log("读取失败" + err.message);
  }
});path 路径模块 
const pathStr = path.join('/a', '/b/c', '../', '/d') //  a\b\d 抵消路劲(../)获取文件扩展名
const fext = path.extname(fpath)二、写入文件 
const fs = require('node:fs')
fs.writeFile('F:/12.txt', '888888', (err) => {
  if (err)
    console.log('写入失败 ', err.message)
  // 写入成功,错误为null
  console.log('写入成功')
})fs.writeFile 只能创建文件,不能创建路径
重复调用 fs.writeFile 写入同一个文件,新写入的会覆盖之前内容
三、服务器操作 
服务器与普通电脑:服务器上安装了 web 服务器软件
1.创建 web 服务器步骤 
- 导入 http 模块
- 创建 web 服务器实例
- 为服务器实例绑定 request 事件,监听客户端请求
- 启动服务器
const http = require('node:http')
const server = http.createServer()
server.on('request', (req, res) => {
  // 返回数据给客户端(解决中文乱码两种方法)
  res.writeHead(200, { 'Content-Type': 'text/plain;charset=utf-8' })
  // res.setHeader('Content-Type','text/html; charset=utf-8')
  res.end(`请求url:${req.url},请求方法:${req.method}`)
  console.log('服务器监听到请求')
})
server.listen(80, () => {
  console.log('服务器运行在 127.0.0.1')
})2.动态响应内容 
const http = require('node:http')
const server = http.createServer()
server.on('request', (req, res) => {
  const url = req.url
  let content = `<h1>Not Found 404</h1>`
  if (url === '/' || url === '/index.html')
    content = `<h1>Home</h1>`
  else if (url === '/about.html')
    content = `<h1>About</h1>`
  res.setHeader('Content-Type', 'text/html; charset=utf-8')
  res.end(content)
})
server.listen(80, () => {
  console.log('运行在localhost:8080')
})3.响应文件内容 
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')
const server = http.createServer()
server.on('request', (req, res) => {
  console.log('请求路径和方式 ', req.url, req.method)
  const fpath = req.url
  // 映射服务器文件路径
  fs.readFile(path.join(__dirname, fpath), 'utf-8', (err, data) => {
    if (err) {
      res.end('<h1>Not Found 404</h1>')
      return console.log('读取失败')
    }
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
    res.end(data)
  })
})
server.listen(8888, () => {
  console.log('服务器运行在localhost:8888')
})优化访问方式
const http = require('node:http')
const path = require('node:path')
const fs = require('node:fs')
const server = http.createServer()
server.on('request', (req, res) => {
  console.log('请求路径和方式 ', req.url, req.method)
  const url = req.url
  let fpath = ''
  // 让访问(/,/index.html,/stock-roolup/index.html)都可以得到响应
  if (url === '/')
    fpath = path.join(__dirname, '/stock-rollup/index.html')
  else if (url === '/index.html')
    fpath = path.join(__dirname, '/stock-rollup', url)
  else if (url === '/stock-rollup/index.html')
    fpath = path.join(__dirname, url)
  console.log('fpath: ', fpath)
  fs.readFile(fpath, 'utf-8', (err, data) => {
    if (err) {
      res.end('<h1>Not Found 404</h1>')
      return console.log('读取失败')
    }
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
    res.end(data)
  })
})
server.listen(8888, () => {
  console.log('服务器运行在localhost:8888')
})四、Node 模块化 
模块分为:内置模块、自定义模块、第三方模块
1.module 对象 
在自定义模块中,module.exports模式是空对象,使用module.exports将模块内部成员共享

2.模块共享 
m1.js
const name = 'zhangsan'
module.exports.name = 'lisi'
console.log('module-m1: ', module)m2.js
const m1 = require('./m1')
console.log(m1.name)运行: node m2.js

导出的对象以module.expports为准, 使用require()得到的永远是module.expports指向的对象
// m1.js
exports.name = 'zhangsan'
exports.age = 18
module.exports = {
  name: 'lisi',
  age: 20,
}
// m2.js
const m1 = require('./m1')
console.log(m1.name) // lisi
console.log(m1.age) // 20
五、模块加载机制 
- 优先从缓存中加载,模块多次 require()只会执行一次 
- 内置模块的加载优先级是最高的 
- 自定义模块 require 时,必须在路径指定 - ./或../开头的路径标识符
 4. 第三方模块记载机制
4. 第三方模块记载机制

- 目录作为模块  
六、Express 模块 
前端两种服务器:
Web 网站服务器:专门对外提供 Web 网页资源服务器 API 接口服务器:专门对外提供 API 接口服务器
1.Express 创建服务器 
const express = require('express')
const app = express()
app.get('/zhj', (req, res) => {
  console.log('zhj被访问')
  res.send('zhj被访问哈哈哈')
})
app.listen(80, () => {
  console.log('run 80')
})2.Express 中间件 
- 概念

- 定义与使用全局中间件 - 可以调用多个中间件对请求进行**预处理**  
const express = require('express')
const app = express()
function mw(req, res, next) {
  console.log('全局中间件被执行了')
  req.name = 'zhangsan'
  next()
}
app.use(mw)
app.get('/zjj', (req, res) => {
  console.log('req.name: ', req.name) // zhangsan
  console.log('zjj被访问')
  res.send('zjj被访问哈哈哈')
})
app.listen(80, () => {
  console.log('run 80')
})- 中间件本质 - next 函数可以流转关系转交给下一个中间件或路由 

- 局部中间件
// ...
function mw1(req, res, next) {
  console.log('中间件mw1被执行了')
  req.age = 18
  next()
}
// 在路由加一个参数,
app.get('/mw1', mw1, (req, res) => {
  console.log('req.age: ', req.age)
  console.log('mw1被访问')
  res.send('mw1被访问哈哈哈')
})
// ...- 多个局部中间件
const express = require('express')
const app = express()
function mw(req, res, next) {
  console.log('全局中间件被执行了')
  req.name = 'zhangsan'
  next()
}
function mw1(req, res, next) {
  console.log('中间件mw1被执行了')
  req.age = 18
  next()
}
function mw2(req, res, next) {
  console.log('中间件mw2被执行了')
  req.age = 20
  next()
}
app.use(mw)
app.get('/zhj', (req, res) => {
  console.log('req.name: ', req.name)
  console.log('zhj被访问')
  res.send('zhj被访问哈哈哈')
})
// 两种传参方式
app.get('/mw1', [mw1, mw2], (req, res) => {
  // app.get('/mw1',mw1,mw2,(req,res)=>{
  console.log('req.age: ', req.age) // 20
  res.send('mw1被访问哈哈哈')
})
app.listen(80, () => {
  console.log('run 80')
})
- 中间件注意事项
- 一定要在路由之前注册中间件
- 可以连续调用多个中间件对请求进行处理
- 中间件函数必须有next()函数
- next()后不要写额外代码
- 连续调用的多个中间件共享 req 和 res 对象
- 中间件分类
- 应用级别中间件:绑定到 app 实例上的中间件
- 路由级别中间件:绑定到 router 实例上的中间件
- 错误级别中间件:捕获项目异常错误的中间件
七、编写接口 
// index.js
const express = require('express')
const router = require('./router')
const app = express()
// 配置解析urlencoded请求体中间件(在路由中间件前使用)
app.use(express.urlencoded({ extended: false }))
// CORS解决跨域
const cors = require('cors')
app.use(cors)
app.use('/api', router)
app.listen(80, () => {
  console.log('server running 127.0.0.1:80')
})// router.js
const express = require('express')
const apiRouter = express.Router()
apiRouter.get('/get', (req, res) => {
  console.log('req.query: ', req.query)
  res.send({
    status: 200,
    message: 'GET 请求成功',
    success: 1,
    data: {
      name: 'zhangsan',
      age: 18,
    },
  })
})
apiRouter.post('/post', (req, res) => {
  console.log('req.body', req.body)
  res.send({
    status: 200,
    message: 'POST 请求成功',
    success: 1,
    data: req.body,
  })
})
module.exports = apiRouter1. CORS 跨域资源共享 

2.CORS 头部 





八、Node 连接数据库 
1.配置 mysql 模块 
- 安装 mysql 模块:`npm i mysql 
- 连接数据库并测试 mysql 是否连接成功 
const mysql = require('mysql')
const db = mysql.createPool({
  host: '127.0.0.1',
  user: 'root',
  password: 'root',
  database: 'mydb01',
})
// 测试
db.query('select 1', (err, res) => {
  if (err)
    return console.log(err.message)
  console.log('res: ', res) // res:  [ RowDataPacket { '1': 1 } ]
})2.查询数据 
// 查询语句
const qstr = 'select * from users'
db.query(qstr, (err, res) => {
  if (err)
    return console.log(err.message)
  console.log('res: ', res)
})
/* 查询结果:
res:  [
  RowDataPacket { id: 1, name: '张三', age: 19 },
  RowDataPacket { id: 2, name: '李四', age: 22 }
]
*/3.插入数据 
//方式一:
let zl = {
  id: 6,
  name: "zhaoliu",
  age: 21,
};
//占位符的数量和表的列数需要一致,
//否则报错ER_WRONG_VALUE_COUNT_ON_ROW: Column count doesn't match value count at row 1
const istr = `insert into users values(?,?,?)`;
db.query(istr, [zl.id, zl.name, zl.age], (err, res) => {
  if (err) {
    return console.log(err.message);
  }
  console.log("res:", res); //res.affectedRows大于0表示插入成功
});
/* 返回结果:
res: OkPacket {
  fieldCount: 0,
  affectedRows: 1,
  insertId: 3,
  serverStatus: 2,
  warningCount: 0,
  message: '',
  protocol41: true,
  changedRows: 0
}
*/
//方式二:(便捷形式)
const istr = `insert into users set ?`;
db.query(istr, zl, (err, res) => {
  if (err) {
    return console.log(err.message);
  }
  if (res.affectedRows > 0) {
    console.log("插入成功");
  }
});4.更新数据 
//方式一:
let user = {
  id: 6,
  name: "laoqi",
  age: 30,
};
const ustr = `update  users set name=?,age=? where id=?`;
db.query(ustr, [user.name, user.age, user.id], (err, res) => {
  if (err) {
    return console.log(err.message);
  }
  console.log("res: ", res);
  if (res.affectedRows > 0) {
    console.log("更新成功");
  }
});
//方式二:(便捷形式)
const ustr = `update  users set ? where id=?`;
db.query(ustr, [user, user.id], (err, res) => {
  if (err) {
    return console.log(err.message);
  }
  if (res.affectedRows > 0) {
    console.log("更新成功");
  }
});5.删除数据 
- 硬删除
const dstr = 'delete from users where id=?'
// 只有一个占位符可省略数组括号
db.query(dstr, 6, (err, res) => {
  if (err)
    return console.log(err.message)
  if (res.affectedRows > 0)
    console.log('硬删除成功')
})- 软删除
const user = {
  id: 2,
  deleted: 1,
}
const dstr = 'update users set ? where id = ?'
db.query(dstr, [user, user.id], (err, res) => {
  if (err)
    return console.log(err.message)
  if (res.affectedRows > 0)
    console.log('软删除成功')
})九、身份认证 
1. http 协议的特性 
- 无状态性:多个请求之间相互独立,服务器不会保留每次 HTTP 请求的状态
- 无连接:服务器挨个处理访问队列里的访问,处理完一个就关闭连接
2.Cookie 特性 
- 自动发送
- 域名独立
- 过期时限
- 4KB 限制
注意:Cookie 不具有安全性,可以伪造,所以不要用 Cookie 保存隐私数据。
3.Session 认证 

const express = require('express')
const app = express()
// 配置 Session 中间件
const session = require('express-session')
app.use(
  session({
    secret: 'usersession',
    resave: false,
    saveUninitialized: true,
  })
)
// 托管静态页面
app.use(express.static('./pages'))
// 解析 POST 提交过来的表单数据
app.use(express.urlencoded({ extended: false }))
// 登录的 API 接口
app.post('/api/login', (req, res) => {
  // 判断用户提交的登录信息是否正确
  if (req.body.username !== 'admin' || req.body.password !== '000000')
    return res.send({ status: 1, msg: '登录失败' })
  // 将登录成功后的用户信息,保存到 Session 中(express-session配置后才有)
  req.session.user = req.body // 用户的信息
  req.session.islogin = true // 用户的登录状态
  res.send({ status: 0, msg: '登录成功' })
})
// 获取用户姓名的接口
app.get('/api/username', (req, res) => {
  // 从 Session 中获取用户的名称,响应给客户端
  if (!req.session.islogin)
    return res.send({ status: 1, msg: 'fail' })
  res.send({
    status: 0,
    msg: 'success',
    username: req.session.user.username,
  })
})
// 退出登录的接口
app.post('/api/logout', (req, res) => {
  // 清空 Session 信息
  req.session.destroy()
  res.send({
    status: 0,
    msg: '退出登录成功',
  })
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, () => {
  console.log('Express server running at http://127.0.0.1:80')
})Session 认证弊端:需要配合 Cookie,Cookie 不支持跨域,需要做额外配置,才能实现跨域 Session 认证。
4.JWT 认证 
原理:服务端通多用户信息生成 Token,发送并保存在客户端,服务器通过还原 Token 来认证用户身份。

JWT 组成部分:Header(头部)、Payload(有效载荷)、Signature(签名)。形式:Header.Payload.Signature

JWT 使用
安装jsonwebtoken、express-jwt
const express = require('express')
const app = express()
// 01:安装并导入 JWT 相关的两个包,分别是 jsonwebtoken 和 express-jwt
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 允许跨域资源共享
const cors = require('cors')
app.use(cors())
// 解析 post 表单数据的中间件
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
// 02:定义 secret 密钥,建议将密钥命名为 secretKey
const secretKey = 'coder8888'
// 04:注册将 JWT 字符串解析还原成 JSON 对象的中间件
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂载到 req.user 属性上
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))
// 登录接口
app.post('/api/login', (req, res) => {
  // 将 req.body 请求体中的数据,转存为 userinfo 常量
  const userinfo = req.body
  // 登录失败
  if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
    return res.send({
      status: 400,
      message: '登录失败!',
    })
  }
  // 登录成功
  /* 03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
   参数1:用户的信息对象
   参数2:加密的秘钥
   参数3:配置对象,可以配置当前 token 的有效期
   记住:千万不要把密码加密到 token 字符中 */
  const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, {
    expiresIn: '30s',
  })
  res.send({
    status: 200,
    message: '登录成功!',
    token: tokenStr, // 要发送给客户端的 token 字符串
  })
})
// 这是一个有权限的 API 接口
app.get('/admin/getinfo', (req, res) => {
  // 05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
  console.log(req.user)
  res.send({
    status: 200,
    message: '获取用户信息成功!',
    data: req.user, // 要发送给客户端的用户信息
  })
})
// 06:使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err, req, res, next) => {
  // 这次错误是由 token 解析失败导致的
  if (err.name === 'UnauthorizedError') {
    return res.send({
      status: 401,
      message: '无效的token',
    })
  }
  res.send({
    status: 500,
    message: '未知的错误',
  })
})
app.listen(8888, () => {
  console.log('Express server running at http://127.0.0.1:8888')
})