大前端 - nodejs - 框架 - express - 基础使用

Express介绍
  • express是什么?
    express是一个简单,快速的,及简的nodejs web应用开发框架,通过它,可以很轻松的构建各种web应用。
    例如:1.接口服务
    2.传统的web网站
    3.开发工具的集成

  • express本身是极简的,仅仅提供了web的基础能力,但是它通过中间件的方式继承了许许多多的外部插件来处理http请求。
    body-parser:解析http请求体
    compression:压缩http相应。
    cookie-parser:解析cookie数据
    cors:处理跨域资源请求
    morgan:http请求日志记录。

  • express中间件是一把双刃剑。
    1.它让express本身变的更加灵活和简单。
    缺点在于虽然有一些中间可以解决几乎所有的问题或需求,但是挑选合适的包有时也是一种挑战。
    2.express不对nodejs已有的特性进行二次封装,只是在它之上扩展了web应用所需的基本功能。
    3.内部使用的还是http模块。请求对象继承自:http.IncomingMessage。响应对象继承自:http.ServerResponse

  • 有很多流行框架基于express。例如:nestjs, sails

  • express特性:
    1.简单易学,
    2.丰富的基础api支持,以及常见的http辅助程序,例如:重定向,缓存
    3.,提供了强大的路由功能
    4.灵活的中间件
    5.高性能
    6.非常稳定。

  • express应用场景:
    1.传统的web网站
    2.接口服务
    3.服务端渲染中间层
    4.开发工具 json server, webpack-dev-server

路由基础

查看官方文档:https://www.expressjs.com.cn/4x/api.html

路由指的是:确定应用程序如何响应客户端对特定端点的请求,该特定端点是url和特定的http请求方法(get,post等。)
每个路由可以具有一个或多个处理程序函数,这些函数在匹配该路由时执行。路由定义采用一下结构:
1.app时express实例
2.METHOD时小写的http请求方法
3.PATH时服务器上的路径。
4.HANDLER是当路由匹配时执行的功能。

const express = require('express')

const app = express()
app.get('/', (req, res) => {
	res.send('get /')
})

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

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

请求和响应

express应用使用路由回调函数的参数,request和response对象来处理请求和响应的数据。

app.get('/', (req, res) => {
	req.url // 请求地址
	req.method // 请求方式
	req.headers // 请求头

	res.cookie('foo', 'bar')
})

express不对nodejs已有的特性进行二次抽象,只是在他的接触上扩展了web应用所需的基本功能。

路由:

需求:
实现查询任务列表: get /todos
根据id查询单个任务: get todos/:id
添加任务:post /todos
修改任务: patch /todos
删除任务. delete /todos/:id

index.js

const express = require('express')
const fs = require('fs')
const { getDb, saveDb } = require('./db')

const app = express()

// 根据不同的content-type格式解析数据
// 解析表单请求体:application/json
app.use(express.json())
// 解析表单请求体:application/x-www-form-urlencoded
app.use(express.urlencoded())


// 获取列表
app.get('/todos', async(req, res) => {
  // fs.readFile('index.json', 'utf-8', (err, data) => {
  //   if (err) {
  //     // json:只能发送json格式的数据
  //     // 返回给客户端的响应
  //     return res.status(500).json({error: err.message})
  //   }
  //   const db = JSON.parse(data)
  //   // 返回给客户端
  //   res.status(200).json(db.todos)
  // })

  try {
    const db = await getDb()
    res.status(200).json(db.todos)
  } catch (error) {
    return res.status(500).json({error: err.message})
  }
})
// 获取详情
app.get('/todos/:id', async(req, res) => {
  // fs.readFile('index.json', 'utf-8', (err, data) => {
  //   if (err) {
  //     // json:只能发送json格式的数据
  //     // 返回给客户端的响应
  //     return res.status(500).json({error: err.message})
  //   }
  //   const db = JSON.parse(data)
  //   const todo = db.todos.find(todo => todo.id == req.params.id)
  //  if (!todo) {
  //   return res.status(404).json({code: '0', data: {}})
  //  } else {
  //   return res.status(200).json({ code: '200', data: todo })
  //  }
  // })

  try {
    const db = await getDb()
    const todo = db.todos.find(todo => todo.id == req.params.id)
    return res.status(200).json({ code: '200', data: todo })
  } catch (error) {
    return res.status(404).json({code: '0', data: {}})
  }
})
// 添加
app.post('/todos', async(req, res) => {
  // 1.获取客户端请求体参数:req.body
  // 2.数据验证
  // 3.数据验证通过么,把数据存储到index.json中

  try {
    const todo = req.body
    if (!todo.title) {
      return res.status(422).json({ code: '0', error: 'title is request' })
    } else {
      const db = await getDb()
      const lastTodo = db.todos[db.todos.listen - 1]
      todo.id = lastTodo ? lastTodo.id + 1 : 1
      db.todos.push(todo)
      await saveDb(db)
      // 发送响应
      return res.status(200).json({ code: '200', message:'插入成功' })
    }
  } catch (error) {
    return res.status(500).json({ code: '0', error: 'title is request' })
  }
})
// 修改
app.patch('/todos/:id', async(req, res) => {
  try {
    // 1.获取表单数据
    // 2。查找要修改的任务项目

    const todo = req.body
    const db = await getDb()
    const ret = db.todos.find(todo => todo.id == req.params.id)
    if (!ret) {
      return res.status(0).json({ code: '0', error: '数据不存在' })
    } 
    Object.assign(ret, todo)
    await saveDb()
    return res.status(200).json({ code: 200, data: ret })
  } catch (error) {
    return res.status(500).json({ code: '0', error: '错误啦' })
  }
})

// 删除任务
app.delete('/todos/:id', (req, res) => {
  try {
    const todoId = Number.parseInt(req.params.id)
    const db = await getDb()
    const index = db.todos.findIndex(todo => todo.id === todoId)
    if (index === -1) {
      return res.status(404).end()
    }
    db.todos.splice(index, 1)
    await saveDb()
  } catch (error) {
    return res.status(500).json({ code: '0', error: error.message })
  }
})
app.listen(3000, () => {
  console.log('server is strat')
})

db.js

const fs = require('fs')
const path = require('path')
const { promisify } = require('util')

const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)

const dbPath = path.join(__dirname, './index.json')
exports.getDb = async () => {
  const data = await readFile(dbPath, 'utf-8')
  return JSON.parse(data)
}

// 向todo中存储数据
exports.saveDb = async (db) => {
  const data = JSON.stringify(db)
  await writeFile(dbPath, data)
}

index.json

{"todos":[{"id":1,"title":"睡觉"},{"id":2,"title":"吃饭"},{"id":3,"title":"code"},{"id":4,"title":"上课"},{"title":"22222","id":1},{"title":"2222222222","id":1},{"title":"2222222222","id":1}]}
中间件

不需要修改原有的代码,增加代码的功能。

// 案例:中间件的配置
app.use(express.json())
// 自己封装的函数
consyt myLogger = (req) => {
	console.log(req.method, req.url, Date.now())
}

app.get('/', (req, res) => {
	myLogger()
})

中间件的顺序很重要。代码的执行顺序从上到下。

// req:请求对象
// res: 响应对象
// next: 进入下一个中间件
// 当前实现的功能:每次进入一个请求地址,都会输出console里面的内容。
app.use((req, res, next) => {
	console.log(req.method, req.url, Date.now())
	// 交出执行权,往后继续匹配执行
	next()
})
app.get('/', (req, res) => {
	
})
中间件概念

请添加图片描述
express中间价和AOP(面向切面编程)是一个意思,都是需要经过一些的步骤,不去修改自己的代码啊,以此来扩展或者处理一些功能。

什么是AOP(面向切面编程)?
将日志记录,性能统计,安全控制,事物处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而该拜年这些行为的时候不影响业务逻辑的代码。

利用aop可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑部分之间的耦合度降低,提供程序的可重用性,同时提高可开发的效率和可维护性。
请添加图片描述

总结:就是在现有的代码中,程序的生命周期或者横向流程中,加入/减去一个或多个功能,不影响原有的逻辑。

express中间件函数

在express中,中间件就是一个可以访问的请求对象,响应对象和调用next方法的函数。
请添加图片描述

中间件-分类-应用程序级别中间件

express中的中间件分类:
1.应用程序级别中间件
2.路由级别中间件
3.错误处理中间件
4.内置中间件
5.第三方中间件

  • 1.应用程序级别中间件
    都是使用 app.use挂载的

不关心请求路径是什么,哪个请求路径都会指向的。

 var express = require('express')
 var app = express()
app.use(function (req, res, next) {
	console.log(Date.now())
	next()
})

限制请求路径的中间件:

// 只有匹配到/user/:id这个路径才会执行的中间件。
app.use('/user/:id', (req, res, next) => {
	console.log(Date.now())
	next()
})

限定请求路径 + 请求方法

// 会有匹配到get请求,请求路径为/users/:id才会执行函数中的方法
app.get('/users/:id', (req, res, next) => {})

多个处理函数的中间件

app.use('/user/:id', (req, res, next) => {console.log('1')next()}, (req, res, next) => {console.log('2'); next()})

给一个路径定义多个处理中间件

app.get('/user/:id', () =>{}, () =>{},)

中间件也可以在数组中声明使用。
如下:创建了一个带有中间件的子堆栈的数组,该子堆栈处理对user/:id路径的get请求。

function logOriginUrl(req, res, next) {
	console.log(req.originalUrl)
	next()
}

function logMethod(req, res, next) {
	console.log(req.method)
	next()
}

var logStuff = [logOriginUrl, logMethod]
app.get('/user/:id', logStuff, function (req, res, next) {
	res.send('user info')	
})
路由器 - 级别中间件

router.js

const express = require('express')

// 1.创建路由实例
路由实例就相当于一个mini express实例
const router = express.router()

app.get
app.post

// 2.配置路由
router.get('/foo', (req, res) => {})
router.post('/about', (req, res) => {})

// 3.导出路由实例
module.exports = router
// 4.将路由挂载继承到express应用当中

在index.js文件当中使用router

const exporess = require('express')
// 1.引入路由
const router = require('./router.js')
app.use('/user/:id', (req, res, next) => {})

// 2.直接挂载路由,路由的访问路径为:/foo
app.use(router) 
// 给路由设置访问前缀, 路由的访问路径为:/provider/foo
app.use('/provider',router)
错误处理 - 中间件

以与其他中间件函数相同的方式定义错误处理中间件函数,出了使用4个参数而不是参数之后,错误处理中间件始终带有4个参数,必须提供4个参数已将其标识为错误处理中间件函数。即使不需要使用该next对象,也必须制定它以维护签名。否则,该next对象将被解释为常规中间件。

如果将任何内容传递给next()函数(字符串‘router’除外)express都会将当前请求视为错误,并且将跳过所有剩余的无错处理路由和中间件函数。

app.get('/users', (req, res, next) => {
	try{
	}catch(err) {
		next('err')
	}
})


// 在所有的中间件之后挂载处理中间件
// 必须是4个参数,缺一不可。
app.use((err, req, res, next) => {
	console.log('err', err)
	res.status(500).send('出错啦')
)

next(‘router’)往后匹配当前中间件堆栈中的下一个。
next(err)跳过所有剩余的无错误处理路由和中间件函数。

处理404中间件

通常会在所有的路由之后配置处理404的内容。
请求进来依次从上到下。

app.use('req', 'res', 'next', () => {
	res.status(404).send('404  not fond')
})
内置中间件

内置中间件:就是express本身自己存在的。
express有5个内置中间件。
express.json()
express.urlencoded()
express.raw()
express.text()
express.static()
请添加图片描述

第三方中间件

常用官方维护的第三方中间件 https://expressjs.com/en/resources/middleware.html

express路由

express支持错有http请求方法相对应的方法:get,post。。。
有一种特殊的方法:app.all()

app.all('/serect', (req, res, next) => {})

查询字符串不是路由路径的一部分。

  • 此理由路径会将请求匹配到/random.tex
app.get('/random.text', () => {})
  • 以下是一些基于字符串模式的路由路径。
    此路径将与acd和匹配abcd
app.get('/ab?cd', () => {})
  • 这条路线的路径将会匹配abcd,abbcd,adddcd等等
app.get('/ab+cd', () => {})
  • 这条路线的路径将会匹配abcd,abxcd,abRAndomcd, ab1234cd等等
* : 表示随意的。
app.get('/ab*cd', () => {})
  • 这条路线的路径将会匹配/abe, /abcde
app.get('/ab(cd)e', () => {})

  • 这条路线的路径将会匹配带有 “a”的任何内容。
app.get(/a/, () => {})
  • 这条路线的路径将会匹配butterfly和dragonfly,但不带butterflyman,和dragonflyman等等。
app.get(/.*fly$/, () => {})
路径参数

路由参数被命名为url段,用于捕获url中在其位置处制定的值。捕获的值将其填充到req.params对象中,并将路径指定的route参数的名称作为其各自的键。

app.get('/users/:userId/books/:bookid', function (req, res, next) {})
  • 多个回调函数处理一个路由:
app.get('/example/b', (req, res, next) => {next()}, (req, res, next) => {next()})
  • 回调函数数组处理路由:
var cb0 = function (req, res, next) {next()}
var cb1 = function (req, res, next) {next()}
var cb2 = function (req, res, next) {next()}

app.get('example/c', [cb0, cb1, cb2])
  • 独立功能和功能数组的组合可以处理路由:
var cb0 = function (req, res, next) {next()}
var cb1 = function (req, res, next) {next()}


app.get('example/c', [cb0, cb1], (req, res, next) => {
	next()
})
响应方法:
res.download() // 提示要下载
res.end() //  结束响应
res.json() // 发送json格式的数据
res.jsonp() // 发送带有jsonp支持的json响应。
res.redirect() // 重定向请求。
res.render() // 渲染视图模版
res.send() // 发送各种类型的响应。
res.sendFile() // 将文件作为八位字节流发送
res.sendStatus() // 设置响应状态码。
app.route()

定义链式路由处理程序。

app.route('/book')
	.get()
	.post()
	.put()
快速路由器:

express.Router该累创建模块化的,可安装的路由。

请添加图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值