Node.js - express使用详解

146 阅读8分钟

引入 express,启动服务

const express = require('express')
const app = new express()
const router = express.Router()
app.use(router)
// 启动服务,服务开在本地80端口
app.listen(80, () => {
  console.log('server running at http://127.0.0.1')
})

express 解析 post 请求体+解析 json 数据

app.use(express.urlencoded({ extended: false }))
app.use(express.json())

后端允许跨域

const cors = require('cors') //中间件解决跨域
app.use(cors())

express 静态资源托管

app.use('/upload', express.static('upload'))
// 将upload目录配置为可被外界访问的资源
// 访问地址 http://xxx.xxx.xxx.xxx/upload/xxx.jpg

nodejs path 模块

__dirname 获取当前文件所在的绝对路径,不包括文件名
__filename 获取当前文件所在的绝对路径,包括文件名
获取路径:path.dirname()
获取文件名(带有扩展名):path.basename()
获取文件名(没有扩展名):path.basename( , ".后缀")
获取文件的扩展名:path.extname()
路径连接:path.join()
转化成绝对路径:path.resolve()
解析路径  :
    path.parse(){
          root : 根路径
  	  dir :  路径名
	  base : 文件名.后缀名
	  ext : 后缀名
	  name : 文件名,不包括后缀名
   }

nodejs fs 模块

操作文件夹

创建文件夹

异步创建文件夹 mkdir(path, options,callback) path:用来设置文件夹的路径 options:用来设置文件夹的操作权限{mode: 077} callback:回调函数,当文件夹创建完毕时执行,该函数有两个参数(err,path)–err 代表错误信息,在文件夹创建失败时返回 err

// 异步创建文件夹
fs.mkdir(path.join(__dirname, 'files'), { mode: 077 }, function (err, dirPath) {
  if (err) {
    console.log('文件夹创建失败')
  }
  console.log(dirPath)
})

同步创建文件夹,即文件夹创建时后面的代码处于阻塞状态,直达文件夹创建完毕 mkdirSync(path, options) path:用来设置文件夹的路径 options:用来设置文件夹的操作权限{mode: 077} callback:回调函数,当文件夹创建完毕时执行,该函数有两个参数(err,path)–err 代表错误信息,在文件夹创建失败时返回 err

// 同步创建文件夹
fs.mkdirSync(path.join(__dirname, 'files'), { mode: 077 })

删除文件夹

异步文件夹 rmdir(path, callback) path:用来设置文件夹的路径 callback:回调函数,当文件夹删除完毕时执行,该函数有一个参数(err)–err 代表错误信息,在文件夹删除失败时返回 err

// 异步删除文件夹
fs.rmdir(path.join(__dirname, 'files'), function (err) {
  if (err) {
    console.log('文件夹删除失败')
    console.log(err)
  }
  console.log('文件夹删除成功')
})

同步删除文件夹 fs.rmSync(path) path:用来设置文件夹的路径 注意:删除文件夹时,文件夹必须为空。如果文件夹不为空,此时文件夹无法正常删除

// 同步删除文件夹
fs.rmSync(path, join(__dirname, 'files'))

查询文件夹

异步查询文件夹中的子级文件 readdir(path, callback) path:读取的文件夹路径 callback:读取之后对应的回调,err 代表读取失败信息,files 数组,存储读取成功的文件夹子级文件名称

// 异步查询文件夹
fs.readdir(path.join(__dirname, 'files'), function (err, files) {
  if (err) {
    console.log('文件夹读取失败')
  }
  console.log(files)
})

同步查询文件夹中的子级文件 readdirSync(path) path:读取的文件夹路径

var fs = fs.readdirSync(path.join(__dirname, 'files'))
console.log(fs)

非空文件夹删除

读取非空文件夹中所有的文件或者文件夹 将文件直接删除,但是文件夹需要继续读取,将子级文件夹清空以后再删除 2.1. 首先判定获取的内容是否是文件或者文件夹


//异步获取
fs.stat(path.join(__dirname,"files"),function (err,states) {
  if(err){
    console.log("读取失败");
  }
  console.log((states.isDirectory()));
})

//同步获取
var state = fs.statSync(path.join(__dirname, "files"));
console.log(state.isDirectory());
//用来返回一个文件状态,该状态对象中分别存在个方法。isDirectory()返回该文
件是否是文件夹,isFile()返回该文件是否是文件类型

如果数据是文件直接删除,如果不是继续读取该文件中的子级文件或文件夹

//读取当前path对应的文件中的所有子级文件或者文件夹
var files = fs.readdirSync(pathName)
// 判断:当前读取的子级文件是否是文件类型
for (var index in files) {
  var filePath = path.join(pathName, files[index])
  console.log(filePath)
  //判断filePath对应是文件还是文件夹
  var state = fs.statSync(filePath)
  if (state.isFile()) {
    //文件此时要同步删除
    fs.unlinkSync(filePath)
  } else {
    //继续执行删除文件夹操作
    deleteDir(filePath)
  }
}

最后将当前文件夹删除

// //删除当前空文件夹
fs.rmdirSync(pathName)

完整代码

//定义一个函数完成文件夹的删除
function deleteDir(pathName) {
  //读取当前path对应的文件中的所有子级文件或者文件夹
  var files = fs.readdirSync(pathName)
  // 判断:当前读取的子级文件是否是文件类型
  for (var index in files) {
    var filePath = path.join(pathName, files[index])
    console.log(filePath)
    //判断filePath对应是文件还是文件夹
    var state = fs.statSync(filePath)
    if (state.isFile()) {
      //文件此时要同步删除
      fs.unlinkSync(filePath)
    } else {
      //继续执行删除文件夹操作
      deleteDir(filePath)
    }
  }
  // 删除当前空文件夹
  fs.rmdirSync(pathName)
}
deleteDir(path.join(__dirname, 'files'))

文件操作

从文件中读取数据

异步读取文件数据

fs.readFile(path, options, callback)–从文件中异步读取文件数据。注意 callback 存在两个参数 err,data。err 当读取失败时抛出错误信息。data 用来存储读取成功之后对应的文件数据。

fs.readFile(
  path.join(__dirname, 'a.txt'),
  { encoding: 'utf-8', flag: 'r+' },
  function (err, data) {
    if (err) {
      console.log('数据读取失败')
    }
    console.log(data)
  },
)

同步读取文件数据 fs.readFileSync(path, options)–从文件中同步读取文件数据

//同步读取文件数据
var data = fs.readFileSync(path.join(__dirname, 'b.txt'), {
  encoding: 'utf-8',
  flag: 'r',
})
console.log(data)

注意:对文件进行数据读取时,一定要保证读取的文件提前是存在的,如果被读取的文件不存在,计算机不会自动创建,会自动抛出异常。

向文件中写入数据

异步写入数据 fs.writeFile(path, data, options, callback)–向文件中异步写入数据

fs.writeFile(
  path.join(__dirname, 'a.txt'),
  '八百里分麾下炙',
  { encoding: 'utf-8', flag: 'a' },
  function () {
    console.log('数据已经写入')
  },
)

同步写入数据 fs.writeFileSync(path, data, options)–向文件中同步写入数据。

fs.writeFileSync(path.join(__dirname, 'a.txt'), '你好', {
  encoding: 'utf-8',
  flag: 'w',
})

注意:向文件中写入数据,如果不存在,此时会自动创建

删除文件

异步删除:

//文件是否存在,都会回调函数,文件删除成功
fs.unlink(path.join(__dirname), 'a.text', function () {
  console.log('文件删除成功')
})

同步删除:

fs.unlinkSync(path.join(__dirname, 'a.text'))

获取文件信息

异步获取文件信息

// 获取文件信息
fs.stat(path.join(__dirname, 'a.txt'), function (err, states) {
  if (err) {
    console.log('文件不存在')
  }
  console.log(states)
})
fs.stat(path.join(__dirname, "b.txt"), function(err, stats){
if(err){
console.log('文件不存在');
}
//stats是一个文件状态对象,可以通过该对象内置的isFile()或者isDirectory()
判断当前文件是文件夹类型还是文件类型,对应的返回值是一个布尔类型。
console.log(stats.isDirectory());
});

同步获取

var stats = fs.statSync(path.join(__dirname, 'b.txt'))
console.log(stats.isFile()) //布尔类型

重命名文件

异步:

// 文件重命名
fs.rename(path.join(__dirname, "b.txt"), path.join(__dirname,
"block.txt"), function(){
console.log('文件重命名成功');

同步:

fs.renameSync(path.join(__dirname, 'b.txt'), path.join(__dirname, 'block.txt')) //如果不存在会抛出异常

综上:文件的操作分为同步和异步操作,异步操作均对异常做出了处理,但是同步操作没有对异常做出处理,因此使用时最好选用异步操作。

常用的第三方模块

# i是install的缩写,删除时可以使用un代替i,un既是uninstall的缩写
npm i express-generator -g # 安装全局express脚手架
express xxx -e # 创建基于.ejs的项目

npm i -g nodemon            # 这个安装为全局,用来解决实时刷新页面,实现node服务器与数据库服务器的重启

npm i multer                #  安装编写上传文件接口需要的模块

npm i bcryptjs              #  安装密码加密需要的模块

npm i jsonwebtoken          #  安装服务器端传送token需要的模块

npm i express-jwt           #  安装验证token存不存在需要的模块

npm i cors                  #  安装服务器端解决跨域需要的模块

npm i moment                #  安装日期格式化需要的模块

npm i mongoose              #  安装使用mongoDB数据库需要的模块

npm i body-parser           #  安装解决上传文件大小受限需要的模块

npm i cheerio               #  实现服务器端操作DOM

# 非常常用,内部封装了ajax,通常也是用来发送四种方式的请求,
# get,post,put,delete(这种方式没有数据,只有一个参数url,传参时要在url后面进行拼接)
npm i axios

jwt token 校验

// token认证
// 安装导入JWT相关的包
const jwt = require('jsonwebtoken')
const expressJwt = require('express-jwt')
const secret = 'guoyanchao No1 ^_^' // 定义密钥
app.use(function (req, res, next) {
  const { path, method } = req
  const token = req.headers.authorization
  const pathUnless = [
    //白名单,除了这里写的地址,其他的URL都需要验证token
    '/',
    '/register',
    '/login',
    {
      url: '/articles',
      methods: ['GET'],
    },
    {
      url: /^\/articles\/.*/,
      urlReg: true, //url是正则
      methods: ['GET'],
    },
  ]
  //验证请求的地址和方法是否在白名单里
  let credentialsRequired = true
  pathUnless.forEach((item) => {
    if (item === path) {
      credentialsRequired = false
    }
    if (item.url && item.methods) {
      //判断url是否正则形式,如果是判断请求的地址是否符合正则
      let regPass = item.urlReg && new RegExp(item.url).test(path)
      if ((item.url === path || regPass) && item.methods.indexOf(method) > -1) {
        credentialsRequired = false
      }
    }
  })
  if (!credentialsRequired) {
    //不需要进行验证
    return next()
  } else {
    jwt.verify(token, secret, (err, decoded) => {
      if (err) {
        console.log(err)
        return res.json({
          msg: 'token无效,请重新登录',
          tokenOut: true,
          code: 400,
        })
        // return next()   // err放行,会进入错误处理中间件
      } else {
        console.log('token认证通过,解析结果')
        console.log(decoded)
        return next() // success放行,会进入router中间件
      }
    })
  }
})

错误中间件

app.use(function (err, req, res, next) {
  //判断token是否无效并返回前台
  console.log('handle err------------error-----------')
  console.log(err)
  if (err.name == 'TokenExpiredError') {
    //token过期
    return res.json({
      msg: 'token过期,请重新登录',
      tokenOut: true,
      code: 400,
    })
  } else if (err.name == 'JsonWebTokenError') {
    //无效的token
    return res.json({
      msg: 'token无效,请重新登录',
      tokenOut: true,
      code: 400,
    })
  }
  res.status(err.status || 500)
  res.render('error')
})

express 解析并接收 FormData 表单数据

安装 FormData 解析中间件

npm -i express-formidable
const formidable = require('express-formidable') //引入中间件
app.use(formidable()) // 挂载中间件

router.post('/xx', (req, res, next) => {
  // 接收formData中的参数
  console.log(req.fields)
  // 接收formData中的文件
  console.log(req.files)
})

前后端交互

express 接收 GET 请求参数

query 参数

前端传 query 参数的两种方式: 1.在请求 url 中直接拼接 ?name=zhangsan 最为参数 2.组装 query 对象

const queryParams = { name: 'zhangsan' }
this.axios.get('/userInfo', queryParams).then((res) => {
  console.log(res)
})
this.$axios({
  method: 'get',
  url: '/userInfo',
  params: {
    name: 'zhangsan',
  },
})

后端接收 query 参数

app.get('/userInfo', (req, res) => {
  // http://127.0.0.1/userInfo?name=zhangsan
  res.send(req.query)
})

params 参数

前端传 params 参数

this.axios.get('/userInfo/${userId}').then((res) => {
  console.log(res)
})

后端接收 params 参数

app.get('/user/:id', (req, res) => {
  // http://127.0.0.1/userInfo/5
  res.send(req.params)
})

express 接收 post 请求参数

前端传递 data 参数

const data = { name: 'zhangsan' }
this.$axios.post('/user', data).then((res) => {
  console.log(res)
})
this.$axios({
  method: 'post',
  url: '/user',
  data: {
    name: 'zhangsan',
  },
})

后端接收 data 参数

app.post('/form', (req, res) => {
  res.send(req.body)
})