MongoDB+Express

竺捷
2023-12-01

数据库概念

  • 一个数据库软件中可以包含多个数据仓库,在每个数据仓库中,可以包含多个数据集合,每个数据集合中可以包含多条文档(具体的数据)
术语解释说明
database数据库,mongoDB数据库软件中可以建立多个数据库
collection集合,一组数据的集合,可以理解为JavaScript中的数组
document文档,一条数据的数据,可以理解为JavaScript中的对象
field字段,文档中的属性名称,可以理解为JavaScript中的对象属性

MongoDB

  • 在node.js中操作MongoDB数据库需要依赖node.js的第三方包mongoose
  • 使用npm install mongoose命令下载
    window下启动MongoDB
  • 在命令行工具中运行 net start mongoDB即可启动mongoDB,否则mongoDB无法连接
    数据库连接
const mongoose = require('mongoose')
mongoose.connect('moongodb://localhost/playground')
.then(()=>{console.log('success')})
.catch(err=>console.log('fail', err))

数据库创建

  • 在mongodb中不需要显示创建数据库,如果正在使用的数据库不存在,mongodb会自动创建

MongoDB操作(增删改查)

创建集合

  • 创建集合分为两步,以视对集合设定规则,二十创建集合,创建mongoose.Schema构造函数的实例即可创建集合
// 设定集合规则
const courseSchema = new mongoose.Schema({
	name: String,
	author: String,
	isPublish: Boolean
})
//创建集合并应用规则
const Course = mongoose.model('Course', courseSchema)  // courses

创建文档

  • 创建文档实际上就是向集合中插入数据
    • 创建集合实例
    • 调用实例对象下的save方法将数据保存到数据库中
      方法一
// 创建集合实例
const course = new Course({
	name:'Node.js course',
	author: 'Billy',
	isPublish: true
})
// 将数据库保存到数据库中
course.save()

方法二

Course.create({
	name:'JavaScript基础',
	author:'Billy',
	isPublish: true
},(err, doc)=>{
	// 错误对象
	console.log(err)
	// 当前插入的文档
	console.log(doc)
})
---------------------------
Course.create({
	name:'JavaScript基础',
	author:'Billy',
	isPublish: true
})
.then(doc=>console.log(doc))
.catch(err=>console.log(err))

mongoDB数据库导入数据

  • mongoimport -d 数据库名称 -c 集合名称 --file 要导入的数据文件

找到mongodb数据库的安装目录,将安装目录下的bin目录放置在环境变量中

查询文档

查找所有文档

// 根据条件查找文档
Course.find().then(result=>console.log(result))
// 返回文档集合
[{},{}]

查找一条文档

  • 只返回符合要求的第一条文档
// 根据条件查找文档
Course.findOne({name:'JavaScript基础'})
.then(res=>console.log(res))
// 返回文档
{}

模糊查找

// 匹配大于 小于
User.find({age:{$gt:20, $lt:50}}).then(res=>console.log(res))

// 匹配包含
User.find({hobbies: {$in: ['敲代码']}}).then(res=>console.log(res))

// 选择要查询的字段
User.find().select('name email').then(res=>console.log(res))

// 将数据库按照年龄进行排序
User.find().sort('age').then(res=>console.log(res))

// skip跳过多少数据  limit限制查询数量
User.find().skip(2).limit(2).then(res=>console.log(res))

删除文档

// 删除单个
Course.findOneAndDelete({}).then(res=>console.log(res))

// 删除多个
Course.deleteMany({}).then(res=>console.log(res))

更新文档

// 更新单个
User.updateOne({查询条件}, {要修改的值}).then(res=>console.log(res))
// 更新多个
User.updateMany({查询条件},{要更改的值}).then(res=>console.log(res))

mongoose验证

  • 创建集合规则时,可以设置当前字段的验证规则,验证失败就输入插入失败

  • required:true 必传字段

  • minlength:3 字符串最大长度为2

  • maxlength:20 字符串最大长度为20

  • min:2 数值最小为2

  • max:100 数值最大为100

  • enum:['html','css','javascript','node.js'] 枚举(只能输入列举的数据)

  • trim:true去除字符串两边的空格

  • validate:自定义验证器

  • default:默认值
    获取错误信息:error.errors[‘字段名称’].message

集合关联

  • 通常不同集合的数据之间是有关联的,例如文章信息和用户信息存储在不同的集合中,但文章是某个用户发表的,要查询文章的所有信息包括发表用户,就需要用到集合关联
    • 使用id对集合进行关联
    • 使用populate方法进行关联集合查询
// 用户集合
const User = mongoose.model('User', new mongoose.Schema({
	name: {
		type: String
	}
}))
// 文章集合
const Post = mongoose.model('Post', new mongoose.Schema({
	title: {
		type: String
	}
	// 使用id将文章集合和作者集合进行关联
	author:{type:mongoose.Schema.Types.ObjectId, ref:'User'}
}))
// 联合查询
Post.find().populate('author').then((err, result)=>console.log(result))

模板引擎

  • 在命令行工具中使用npm install art-template命令进行下载
  • 使用const template = require('art-template')引入模板引擎
  • 告诉模板引擎要凭借的数据和模板在哪const html = template(模板路径', 数据')
  • 使用模板语法告诉模板引擎,模板与数据应该如何进行拼接
    代码示例
// 导入模板引擎模块
const template = require('art-template')
// 将特定模板与特定数据进行拼接
const html = template('./views/index.art', {
	data: {
		name:'张三',
		age: 20
	}
})
<div>
	<span>{{ data.name }}</span>
	<span>{{ data.age }}</span>
</div>

模板语法

<!--标准语法-->
<h2>{{value}}</h2>
<h2>{{a?b:c}}</h2>
<h2>{{ a + b}}</h2>
<!--原始语法-->
<h2><%= value %></h2>
<h2><%= a ? b : c %></h2>
<h2><%= a + b %></h2>

原文输出

<!--标准语法-->
<h2>{{ @ value }}</h2>
<!--原始语法-->
<h2><%- value></h2>

条件判断

<!--标准语法-->
{{if 条件}}.......{{/if}}
{{if v1}}....{{else if v2}}....{{/if}}
<!--原始语法-->
<% if (value) { %>  ...  <% } %>
<% if (v1) { %> ... <% } else if (v2) { %> ... <%}  %>

循环

<!--标准语法-->
{{ each target }}
	{{ $index }} {{$value}}
{{ /each }}
<!--原始语法-->
<% for(var i = 0; i < target.length; i++){ %>
	<%= i%> <%= target[i] %>
<% } %>

子模板

<!--标准语法-->
{{ include './header.art' }}
<!--原始语法-->
<% include('./header.art') %>

模板继承

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    {{blcok 'head'}}
    {{/blcok}}
</head>
<body>
    {{block 'content'}}
    {{/block}}
</body>
</html>


<!-- 首页 -->
{{extend './layout.art'}}
{{blcok 'head'}}
<link rel="stylesheet" href="index.css">
{{/blcok}}
{{blcok 'content'}}
<p>this is the content</p>
{{/blcok}}

模板配置

  • 向模板中导入变量template.defaults.imports.变量名 = 变量值
  • 设置模板根目录template.defaults.root = 模板目录
  • 设置模板默认后缀template.defaults.extname= '.art'

Express

  • express是基于node平台的web应用开发框架,它提供了一系列的强大特性,帮助你创建各种Web应用
    • 使用npm install express命令进行下载
      express框架特性
  • 提供了方便简介的路由定义方式
  • 对获取HTTP请求参数进行了简化处理
  • 对模板引擎支持程度高,方便渲染动态HTML页面
  • 提供了中间件机制有效控制HTTP请求
  • 拥有大量第三方中间件对功能进行扩展
// 引入express框架
const express = require('express')
// 使用框架创建web服务器
const app = express();
// 当客户端以get方式访问路由时
app.get('/', (req, res)=>{
	// 对客户端做出响应
	res.send('Hello Express')
})
// 监听3000端口
app.listen(3000)


-------------------------
// 引入express框架
const express = require("express");

// 创建网站服务器
const app = express();

app.get("/", (req, res) => {
  // send方法内部会检测响应内容的类型
  // send方法会自动设置http状态码;
  // send方法会自动设置响应的内容类型及编码;
  res.send({ name: "Billy", age: 19 });
});

// 监听端口
app.listen(3000);

console.log("网站服务器启动成功");

中间件

  • 中间件就是一堆方法,可以接收客户端发来的请求、可以对请求做出响应,也可以将请求继续交给下一个中间件继续处理
    • 中间件主要由两部分组成,中间件方法以及请求处理函数
    • 中间价方法由express提供,负责拦截请求,请求处理函数由开发人员提供,负责处理请求
app.get('请求路径', '处理函数')   // 接收并处理get请求
app.post('请求路径', '处理函数')   // 接收并处理post请求

  • 可以针对同一个请求设置多个中间件,对同一个请求进行多次处理
  • 默认情况下,请求从上到下依次匹配中间件,一旦匹配成功,终止匹配
  • 可以调用next方法将请求的控制权交给下一个中间件,直接遇到结束请求的中间件
app.get('/request', (req, res, next)=>{
	req.name - 'zhangsan',
	next()
})
app.get('/request', (req, res)=>{
	res.send(req.name)
})

app.use中间件的用法

  • app.use匹配所有的请求方式,可以直接传入请求处理函数,代表接收所有的请求
app.use((req, res, next)=>{
	console.log(req.url)
	next()
})
  • app.use第一个参数也可以传入请求地址,代表不论什么请求方法,只要是这个请求地址就接收这个请求
app.use('/admin', (req, res, next)=>{
	console.log(req.url)
	next()
})
// 引入express框架
const express = require("express");

// 创建网站服务器
const app = express();

app.use((req, res, next) => {
  console.log("请求走了app.use中间件");
  next();
});
app.use("/request", (req, res, next) => {
  console.log("请求走了app.use   /request中间件");
  next();
});

app.get("/list", (req, res, next) => {
  req.name = "张三";
  next();
});
app.get("/request", (req, res) => {
  res.send(req.name);
});
// 监听端口
app.listen(3000);

console.log("网站服务器启动成功");

中间件的应用

  • 路由保护,客户端在访问需要登录的页面时,可以先使用中间件判断用户登录状态,用户如果未登录,则拦截请求,直接响应,禁止用户进入需要登录的界面
  • 网站维护公告,在所有的路由的最上面定义接收所有请求的中间件,直接为客户端做出响应,网站正在维护中
  • 自定义404页面
    错误处理中间件
  • 在程序执行的过程中,不可避免的会出现一些无法预料的错误,比如文件读取失败,数据库连接失败
  • 错误处理中间件是一个集中处理错误的地方
app.use((err, req, res, next)=>{
	res.status(500).send('服务器发生未知错误')
})
-----------------
// 引入express框架
const express = require("express");

// 创建网站服务器
const app = express();

// app.use((req, res, next) => {
//   res.send("网站正在维护");
// });

app.use("/admin", (req, res, next) => {
  let isLOgin = true;
  if (isLOgin) {
    next();
  } else {
    res.send("您还没登录");
  }
});

app.get("/admin", (req, res) => {
  res.send("您已经登录");
});

app.use((req, res, next) => {
//   res.status(404);
  res.status(404).send("404");
});
// 监听端口
app.listen(3000);

console.log("网站服务器启动成功");

  • 当程序出现错误时,调用next()方法,并且将错误信息通过参数的形式传递给next()方法,即可触发错误处理中间件
app.get('/', (req, res, next)=>{
	fs.readFile('/file-docs-not-exist', (err, data)=>{
		if(err){
			next(err)
		}
	})
})

捕获错误

  • 在node.js中,异步API的错误信息都是通过回调函数获取的,支持Promise对象的异步API发生错误可以通过catch方法捕获
  • try catch 可以捕获异步函数以及其他同步代码在执行过程中发生的错误,但是不能捕获其它类型的API发生的错误
app.get('/', async (req, res, next)=>{
	try {
		await User.find({name:'张三'})
	}catch(ex){
		next(ex)
	}
})

模块化路由

// home.js
const home = express.Router()
home.get('/index', ()=>{
	res.send('welcome')
})
module.exports = home

// app.js
const home = require('./route/home')
app.use('/home', home)

GET参数的获取

  • Express框架中使用req.query即可获取GET参数,框架内部会将GET参数转换为对象并返回
// http://localhost:3000/?name=zhangsan
app.get('/', (req, res)=>{
	console.log(req.query)  //{"name":"zhangsan"}
})

POST参数的获取

// 引入body-parse模块
const bodyParse = require('body-parse')
// 配置body-parse模块
app.use(bodyParse.urlencoded({extended: false}))
// 接收请求
app.post('/add', (req, res)=>{
	// 接收请求参数
	console.log(req.body)
})
----------------------------
const express = require("express");
const bodyParse = require("body-parser");
const app = express();
// extended:false方法内部使用queryString模块处理请求参数的格式
// extended:true方法内部使用第三方模块qs模块处理请求参数的格式
app.use(bodyParse.urlencoded({ extended: false }));

app.post("/index", (req, res) => {
  console.log(bodyParse);
  // body就是bodyParse在req中添加的一个属性,这个属性里面就是post请求参数
  res.send(req.body);
});

app.listen(3000);

Express路由参数

// http://localhost:3000/find/123
app.get('./find/:id', (req, res)=>{
	console.log(req.params)  // {id: 123}
})
------------------------
const express = require("express");
const bodyParse = require("body-parser");
const app = express();

app.get("/index/:id/:name/:age", (req, res) => {
  // 返回路由参数
  res.send(req.params);
});

app.listen(3000);

静态资源处理

  • 通过Express内置的express.static可以方便地托管静态文件,例如:img、CSS、JavaScript文件
app.use(express.static('public'))

这样设置之后,public目录下的公共静态资源文件就可以被访问了

express-art-template模板引擎

  • 为了使art-template模板引擎能够更好的和Express框架配合,模板引擎官方在原art-template模板引擎的基础上封装了express-art-template
  • 使用npm install art-template express-art-template
// 当渲染后缀为art的模板时,使用express-art-template
app.engine('art', require('express-art-template'))
// 设置模板存放目录
app.set('views', path.join(__dirname, 'views'))
// 渲染模板时不写后缀,默认拼接art后缀
app.set('view engine', 'art')
app.get('/', (req, res)=>{
	// 渲染模板
	res.render('index')
})
-------------------------------------
const express = require("express");

const path = require("path");

const app = express();

// 告诉express框架使用什么模板引擎渲染什么后缀的模板
// 模板后缀
// 使用的模板引擎

app.engine("art", require("express-art-template"));

// 告诉express框架模板存放的位置  ('views')这个参数是固定的

app.set("views", path.join(__dirname, "views"));

// 告诉express框架模板的默认后缀
app.set("view engine", "art");
app.get("/index", (req, res) => {
  //   res.render("index.art");
  // 拼接模板路径
  // 拼接模板 后缀
  // 哪一个模板和哪一个数据进行拼接
  // 将拼接结果响应给客户端
  res.render("index", {
    msg: "message",
  });
});

app.listen(3000);

app.locals对象

  • 将变量设置到app.locals对象下面,这个数据在所有的模板中都可以获取的到
app.locals.users = [{
	name: '张三',
	age: 20
},{
	name:'李四',
	age: 40
}]
 类似资料: