数据库概念
- 一个数据库软件中可以包含多个数据仓库,在每个数据仓库中,可以包含多个数据集合,每个数据集合中可以包含多条文档(具体的数据)
术语 | 解释说明 |
---|---|
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
}]