Node.js
Node.js的Http服务器
- 引入required模块:我们可以使用require指令来载入Node.js模块。
- **创建服务器:**服务器可以监听客户端的请求,类似于Apache 、Nginx等HTTP服务器。
- 接收请求与响应请求:服务器很容易创建,客户端可以使用浏览器或终端发送HTTP请求,服务器接收请求后返回响应数据。
NPM
建立了NodeJS生态圈
包管理工具,新版nodejs已经集成了npm
升级:npm install npm -g
本地安装
- \1. 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录,即当前项目目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
- \2. 可以通过 require() 来引入本地安装的包。
全局安装
- \1. 将安装包放在 /usr/local 下或者你 node 的安装目录。
- \2. 可以直接在命令行里使用。
如果你希望具备两者功能,则需要在两个地方安装它或使用 npm link。
NPM应用场景
- 下载第三方包
- npm i 安装包名称 —默认下载最新版本
- npm i 安装包名称@ —下载指定版本的安装包
- NPM对package.json的字段做了扩展,允许在其中申明第三方包依赖(dependencies中用键值对的写法)
- 安装命令行程序
- 从NPM服务上下载安装一个命令行程序的方法与第三方包类似。
- 例如上例中的node-echo提供了命令行使用方式,只要node-echo自己配置好了相关的package.json字段,对于用户而言,只需要使用以下命令安装程序。
- 发布代码
- 第一次使用NPM发布代码前需要注册一个账号。终端下运行npm adduser,之后按照提示做即可。
版本号
使用NPM下载和发布代码时都会接触到版本号。NPM使用语义版本号来管理代码,这里简单介绍一下。
语义版本号分为X.Y.Z三位,分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。
- 如果只是修复bug,需要更新Z位。
- 如果是新增了功能,但是向下兼容,需要更新Y位。
- 如果有大变动,向下不兼容,需要更新X位。
版本号有了这个保证后,在申明第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。例如"argv": "0.0.x"表示依赖于0.0.x系列的最新版argv。
NPM支持的所有版本号范围指定方式可以查看官方文档。
NPM常用命令
除了本章介绍的部分外,NPM还提供了很多功能,package.json里也有很多其它有用的字段。
除了可以在npmjs.org/doc/查看官方文档外,这里再介绍一些NPM常用命令。
- NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令。
- 使用npm help 可查看某条命令的详细帮助,例如npm help install。
- 在package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试。
- 使用npm update 可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。
- 使用npm update -g可以把全局安装的对应命令行程序更新至最新版。
- 使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。
- 使用npm unpublish @可以撤销发布自己发布过的某个版本代码。
Node.js REPL(交互式解释器)
Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应。
REPL 的交互式的编程环境可以实时的验证你所编写的代码,非常适合于验证 Node.js 和 JavaScript 的相关 API。
REPL的作用
Node 自带了交互式解释器,可以执行以下任务:
- 读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
- 执行 - 执行输入的数据结构
- 打印 - 输出结果
- 循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。
开始学习REPL
启动终端
在命令行中输入node回车,这时我们就可以在 > 后输入简单的表达式,并按下回车键来计算结果。
**简单表达式运算:**加减乘除运算
**使用变量:**var关键字声明变量,没有var会直接打印出来,使用var可以用console.log()来输出
**多行表达式:**类似js的语句
下划线变量:
$ node
var x = 10
undefined
var y = 20
undefined
x + y
30
var sum = _
undefined
console.log(sum)
30
undefined
REPL命令
- ctrl + c - 退出当前终端。
- ctrl + c 按下两次 - 退出 Node REPL。
- ctrl + d - 退出 Node REPL.
- 向上/向下 键 - 查看输入的历史命令
- tab 键 - 列出当前命令
- .help - 列出使用命令
- .break - 退出多行表达式
- .clear - 退出多行表达式
- .save filename - 保存当前的 Node REPL 会话到指定文件
- .load filename - 载入当前 Node REPL 会话的文件内容。
服务器端开发要做的事
- 实现网站的业务逻辑
- 数据增删改查
node概念
- Node:基于Chrome V8引擎的JavaScript代码的运行环境
- LTS:Long Term Support长期支持稳定版
- Node.js
- ECMAScript语法(JS核心语法)
- node环境提供的API
- node 文件名//执行文件
- Shift+右键:在当前窗口打开PowerShell
- 输入文件名开头+Tab可以自动补全
- clear:清屏
Node的全局对象-global
- console.log()
- setTimeout()
- clearTimeout()
- setInterval()
- clearInterval()
模块化开发
- Node.js规定一个js文件就是一个模块,模块内部定义的变量和函数默认在外部无法得到
- export对象进行成员(属性和方法)导出
- require方法导入:有返回值,是文件中exports对象
- exports是module.exports的别名,引用了后者的地址,当module.exports={},改变其地址时,exports失效,所以最终以module.exports为准
系统模块(API)
fs文件操作模块
-
const fs = require(‘fs’)//得到的返回值是fs对象
-
fs.readFile(‘文件路径/文件名称’[,‘文件编码’],callback)硬盘操作(耗时)
-
callback
-
(err,doc) => { //如果读取错误,err为错误对象(包含错误信息),否则err为null doc为文件内容 if(err == null){ console.log(doc) } }
-
-
-
fs.writeFile(‘文件路径/文件名称’,‘数据’,callback)
-
callback
-
err => { if(err != null){ console.log(err) return } console.log('文件写入成功') }
-
-
path路径操作
原因:不同操作系统的路径分隔符不统一(window:\或/;linux:/)
- 路径拼接:path.join(‘路径1’,‘路径2’,…)
- 获取当前文件所在绝对路径
__dirname
- require可以写相对路径,因为require是相对当前文件
第三方模块(包)
一、js文件:提供实现项目具体功能的API接口
二、以命令行工具形式存在,辅助项目开发
npmjs.com:第三方模块的存储和分发仓库
npm(node package manager):node第三方模块管理工具
- nodemon:监控保存操作,自动执行(用nodemon来代替node命令)
- nrm:npm下载地址切换工具(nrm ls:可用下载地址列表 nrm use:切换默认下载地址)
- gulp:前端构建工具
- 使用过程
- npm i gulp//安装gulp
- npm i gulp-cli -g//全局安装gulp命令行工具
- 在根目录下创建一个gulpfile.js文件
- 在命令行工具中,用gulp+任务名即可执行任务
- 方法
- gulp.task() 创建任务
- gulp.src() 获取要处理的文件
- gulp.dest() 输出文件
- gulp.watch() 监控文件变化
- 插件
- gulp-htmlmin:压缩html
- gulp-csso:压缩css
- gulp-uglify:压缩混淆js
- gulp-babel:ES6 => ES5
- gulp-less:less => css
- browsersync:浏览器实时同步
- gulp-file-include:公共文件抽离
- 使用过程
const gulp = require('gulp')
const htmlmin = require('gulp-htmlmin')
const fileinclude = require('gulp-file-include')
const csso = require('gulp-csso')
const less = require('gulp-less')
const uglify = require('gulp-uglify')
const babel = require('gulp-babel')
//1.html任务 压缩html文件 抽取公共部分
gulp.task('htmlmin', () => {
gulp.src('./src/*.html')
.pipe(fileinclude())
.pipe(htmlmin({
collapseWhitespace: true
}))
.pipe(gulp.dest('dist'))
})
//2.css任务 less转换 css压缩
gulp.task('cssmin', () => {
gulp.src(['./src/css/*.less', './src/css/*.css'])
.pipe(less())
.pipe(csso())
.pipe(gulp.dest('dist/css'))
})
//3.js任务 ES6转换 js压缩
gulp.task('jsmin', () => {
gulp.src('./src/js/*.js')
.pipe(babel({
presets: ['@babel/env'] //转为ES5语法,一定性要安装多个包 gulp-babel @babel/core @babel/presets?
}))
.pipe(uglify())
.pipe(gulp.dest('dist/js'))
})
//4.复制图片和库文件
gulp.task('copy', () => {
gulp.src('./src/images')
.pipe(gulp.dest('dist/images'))
gulp.src('./src/lib')
.pipe(gulp.dest('dist/lib'))
})
//5.构建综合任务
gulp.task('default',['htmlmin','cssmin','jsmin','copy'])
package.json
{
"name": "node01",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"browsersync": "^0.0.1-security",
"gulp": "^4.0.2",
"gulp-babel": "^8.0.0",
"gulp-csso": "^3.0.1",
"gulp-less": "^4.0.1",
"gulp-file-include": "^2.1.0",
"gulp-htmlmin": "^5.0.1",
"gulp-uglify": "^3.0.2"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
- “main”:项目主模块
- “dependencies”:项目依赖,(npm i --production:项目运行依赖)
- “devDependencies”:开发依赖(线上运营阶段不需要依赖)npm i 包名 --save -dev
- “scripts”:命令的别名(“build”:“nodemon app.js” => npm run build)
模块加载机制
- 完整路径直接引入
- 有路径无后缀
- 同名js文件
- 同名文件夹
- 其中的index.js文件
- 没有index.js,找当前文件夹的package.json中的“main”的入口文件(此时将find当成一个模块)
- 指定入口文件不存在、没有指定入口文件 =》报错
- 没有路径没有后缀(默认会当成系统模块)
- 去node_modules中
- 找同名js文件
- 同名文件夹
- 其中的index.js文件
- 没有index.js,找当前文件夹的package.json中的“main”的入口文件(此时将find当成一个模块)
- 指定入口文件不存在、没有指定入口文件 =》报错
- 去node_modules中
服务器端概念
-
网站的组成
- 客户端:HTML、Css、Js
- 服务器端:在服务器运行,负责存储数据和处理应用逻辑
-
Node网站服务器
- 提供网站访问服务的机器,能够接受客户端请求,而且能够对请求作出响应
-
IP:互联网中设备的唯一标识(域名–》IP:才能找到网站服务器)
-
端口:区分服务器电脑中提供不同的服务
-
URL:统一资源定位符
传输协议://服务器IP地址/域名:端口/资源所在位置标识 http ://www.itcast.cn/ index.html
-
本机
- 本机域名:localhost
- 本机IP:127.0.0.1
创建web服务器
const http = require('http')
const app = http.createSever()
app.on('request',(req,res) => {//为服务器对象添加请求事件
res.end(req.url)
})
app.listen(3000)//监听3000端口,必须监听,否则无法提供服务
console.log('Sever is running')
http协议
概念
超文本传输协议:客户端(用户)和服务器端(网站)请求和应答的标准
报文
请求报文
req.method
:获取请求方式- GET:地址栏输入,跳转,表单默认提交方式
- 参数放在浏览器的地址栏中
- url.parse(req.url)//将整个url解析为对象
- url.parse(req.url,true)//将内部的参数也都解析为对象形式
- url.parse(req.url,true).query//请求参数
- url.parse(req.url,true).pathname//请求地址
- POST:表单设置的另一种提交方式
- 参数放在报文的FormData中
- post的参数是通过事件来接受的
- 当请求参数传递的时候触发data事件
- 当参数传递完成的时候触发end事件
- 用querystring系统模块来处理post参数
- querystring.parse(postparams)//将参数转为对象
- GET:地址栏输入,跳转,表单默认提交方式
req.url
:获取请求地址(默认是/)req.headers
:获取请求报文信息(对象形式)request.rawHeaders
全部头信息–数组形式request.httpVersion
请求的协议方式
响应报文
- HTTP状态码
- 200:成功
- 404:请求资源未找到
- 500:服务器端错误
- 400:客户端请求有语法错误
- 常见响应方法
res.statusCode=404
; //响应码res.statusmessage="not found"
//响应消息res.setHeader('Content-Type','text/plain;charset=utf-8')
//要放在 res.write() 和 res.end() 之前设置res.writeHead(404,'not found',{'content-type':'内容类型;charset=utf8'})
//响应状态码和设置头内容- text/html
- text/css
- application/javascript
res.write("快国庆了,开心!!")
res.end()
//每个请求都必须调用的方法,==结束响应(请求)==要响应数据的话,数据必须是 String 类型或者是 Buffer 类型
路由
客户端请求地址与服务器端程序代码的对应关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1aPT1MoG-1571715311772)(C:\Users\明明\AppData\Roaming\Typora\typora-user-images\1571555856606.png)]
静态资源
- 概念:服务器端不需要处理,可以直接响应给客户端的资源(谁来请求都一个样)
- 动态资源:相同的请求地址,不同的相应资源
- 根据请求地址、请求类型、设置响应内容类型
- mime插件:指定返回资源的类型
- 将返回的内容类型设置为:mime.getType(req.url)
异步编程
同步API和异步API
- 同步API
- 代码从上至下,逐行执行
- 可以从返回值中拿结果
- 前面的代码会阻塞和后面代码
- 异步API
- 当前API的执行不会阻塞后续代码的执行async
- 只能通过回调函数,将需要返回的值通过回调函数的实参来返回,调用时用回调函数的形参来接受
当需要同步执行异步API时,如果用回调函数来实现,可能会造成回调地狱
const fs = require('fs')
fs.readFile('./1.txt','utf8',(err,result1) => {
console.log(result1)
fs.readFile('./2.txt','utf8',(err,result2) => {
console.log(result2)
fs.readFile('./3.txt','utf8',(err,result3) => {
console.log(result3)
})
})
})
promise解决回调地狱
const fs = require('fs')
function p1(){
return new Promise((resolve,reject) => {
fs.readFile('./1.txt','utf8',(err,result) => {
resolve(result)
})
})
}
function p2(){
return new Promise((resolve,reject) => {
fs.readFile('./2.txt','utf8',(err,result) => {
resolve(result)
})
})
}
function p3(){
return new Promise((resolve,reject) => {
fs.readFile('./3.txt','utf8',(err,result) => {
resolve(result)
})
})
}
p1().then(r1 => {//promise支持链式编程
console.log(r1)
return p2()
}).then(r2 => {
console.log(r2)
return p3()
}).then(r3 => {
console.log(r3)
})
异步函数解决解决回调地狱
async function p1(){
return 'p1'//return 关键字代替resolve,默认返回的是promise对象 throw关键字代替reject
}
async function p2(){
return 'p2'
}
async function p3(){
return 'p3'
}
async function getcallback(){
let r1 = await p1()
let r2 = await p2()
let r3 = await p3()
console.log(r1 ,r2 ,r3)
}
getcallback()
用util.promisify改造异步函数读取文件
const fs = require('fs')
const promisify = require('util').promisify
const readFile = promisify(fs.readFile)
async function run (){
let r1 = await readFile('./1.txt','utf8')
let r2 = await readFile('./2.txt','utf8')
let r3 = await readFile('./3.txt','utf8')
console.log([r1,r2,r3])
}
run()
数据库
为什么用数据库
- 动态网站中的数据是存储在数据库中的
- 能够持久存储客户端通过表单收集的用户信息
- 数据库软件本身可以对数据进行高效管理
什么是数据库
- 独立于语言之外的软件,可以通过API来操作
- 常见数据库软件有:mysql、mongodb、oracle
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tXH6mvD5-1571715311777)(C:\Users\明明\AppData\Roaming\Typora\typora-user-images\1571557954142.png)]
MongoDB可视化软件
是使用图形界面操作数据库的一种方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uYozUMaI-1571715311782)(C:\Users\明明\AppData\Roaming\Typora\typora-user-images\1571558070078.png)]
数据库相关词汇
- database:数据库
- collection:数据集合
- document:一条数据
- field:一条数据的属性名
Node通过第三方包mongoose来操作MongoDB(命令行工具)
- 启动MongoDB
net start mongodb
- 关闭MongoDB
net end mongodb
- 安装mongoose
npm i mongoose
连接MongoDB数据库
mongoose.connect('mongoose://localhost/数据库名')//返回值为promise对象=》可以使用异步函数
.then(() => {console.log('数据库连接成功')})
.catch(err => {console.log('数据库连接失败',err)})
数据库增删改查
创建集合并新增文档
-
设定集合规则
-
const userSchema = new mongoose.Schema({ name:String, age:Number })
-
-
创建集合(两种方式)
-
//创建集合实例,写入文档数据 const User = new mongoose.model('User',userSchema)//集合在数据库中对应的名字为users const user = new User({ name:'Melody', age:24 }) user.save()//新增文档
-
//异步API,返回promise对象,支持异步函数 const User = new mongoose.model('User',userSchema)//集合在数据库中对应的名字为users User.create({name:'Melody',age:24}) .then(result => {console.log(result)}) .catch(err => {console.log(err)})//直接新增文档
-
查询文档
-
User.find().then(result=>{console.log(result)})//查询多个文档 //find()参数为键值对,没有条件查询全部,返回值为数组
-
User.findOne().then(result=>{console.log(result)})//查询单个文档 //无条件返回第一个文档
-
查询条件
- 范围:
{key:{$gt:大于值,$lt:小于值}}
- 包含:
{key:{$in:['项']}}
- 选择查询字段:
User.find().select('name age').then(result => {console.log(result)})
- 根据字段排序:
User.find.sort('age').then()
- 跳过文档数:
User.find().skip(2)
- 限制显示文档数:
User.find().limit(3)
- 范围:
删除文档
-
.findOneAndDelete({key:value})//查找并删除单个文档,无条件时删除第一个,返回删除的文档
-
.deleteMany({}).then()//删除多个文档,返回{n:删除文档数量,ok:1},不写回调函数无法实现效果
更新文档
-
.updateOne({查询条件},{要修改的值}).then()//{}都是键值对形式
-
.updateMany({空位所有文档},{要更改的值})//更改多个
其他操作
MongoDB数据库导入数据
在命令行输入mongoimport -d 数据库名称 -c 集合名称 --file 要导入的数据文件
mongoose验证
在创建集合规则是,可以为字段设置验证规则,验证失败则插入失败
-
required:必填字段
-
maxlength:最大长度
-
minlength:最小长度
-
trim:去除两端空格
-
min:数值最小值
-
max:数值最大值
-
default:默认值
-
enum:[]枚举可选项
-
自定义验证器:
-
validate:{ validator: v=>return v&&v.length>4, message:'输入用户名不合法' }
-
捕获mongoose错误信息
Course.create({name:'移动Web',director:'Pink',isPublished:true})
.then(result => {console.log(result)})
.catch(err => {
const error = err.errors
for(var attr in error){
console.log(error[attr]['message'])
}
})
集合关联
const mongoose= require('mongoose')
mongoose.connect('mongodb://localhost/playground')
.then(() => {console.log('数据库连接成功')})
.catch(err => {console.log('数据库连接失败')})
const userSchcma = new mongoose.Schema({
name:String,
age:Number,
hobbies:[String],
email:String
})
const librarySchema = new mongoose.Schema({
title:'String',
author:{
type:mongoose.Schema.Types.ObjectId,
ref:'User'
},
price:String
})
const User = new mongoose.model('User',userSchcma)
const Library = new mongoose.model('Library',librarySchema)
Library.create({
title:'趁早',
author:'5c09f267aeb04b22f8460968',
price:'58'
})
.then(result=> {console.log(result)})
.catch(err => console.log(err))
Library.find().populate('author').then(result => {console.log(result)})
模版引擎–第三方模块art-template
介绍
-
优点:
- 以更加友好的方式拼接字符串
- 是项目代码更加清晰,易于维护
-
基础
-
安装并引入模版引擎
const template = require('art-template')
-
将数据和模版进行拼接
const html = template(模版文件绝对路径,{数据})。举例如下
-
const html = template(artpath,{ name:'宝宝', age:7, say:'<h1>哈哈</h1>' })
-
-
模版语法
-
输出
-
{{name}}//直接输出数据对象的属性值 {{age}} {{@say}}//直接输出数据对象的属性值(html标签及内容) <%= 1+2%>//直接输出数据对象的属性值 <%= age>10 ? '大宝宝' : '小宝贝'%>//直接输出三元表达式运算结果 <%- say%>//直接输出数据对象的属性值(html标签及内容)
-
-
条件判断
-
{{if age>7}} 大宝宝你好 {{else if age==7}} 耶又吃成长快乐了 {{else if age<7}} 小宝宝你好 {{/if}} <% if(age>7){%> 开始漫长的求学之路 <% }else if(age==7){ %> 哈哈哈上小学了 <% }else if(age<7){%> 幼儿园真好玩 <%}%>
-
-
循环
-
const template = require('art-template') const path = require('path') const artpath = path.join(__dirname,'/views','index.art') const user = template(artpath,{ users:[{ name:'张三', age:20, hobby:'弹琴唱歌' }, { name:'桃桃', age:30, hobby:'绘画' }, { name:'月月', age:24, hobby:'种植' }] }) console.log(user)
-
<ul> {{each users}} <li>{{$index}}</li> <li>{{$value.name}}</li> <li>{{$value.age}}</li> <li>{{$value.hobby}}</li> {{/each}} </ul> <ul> <% for (var i = 0;i < users.length;i++) { %> <li> <%= users[i].name %> <%= users[i].age %> <%= users[i].hobby %> </li> <% } %> </ul>
-
-
子模版(抽离公共部分)
-
//在文件中需要的部分引入即可 {{ include '模版路径' }} <% include('模版路径')%>
-
-
模版继承(HTML骨架模版继承,可以在模版中预留位置)
-
{{extend './layout.art'}}//继承骨架模版的文件 {{block 'head'}}<link>{{block}}
-
骨架模版
-
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> {{block 'head'}}{{/block}} <title>Document</title> </head> <body> {{block 'content'}}{{/block}} </body> </html>
-
-
首页模版
-
{{extend './extend.html'}} {{block 'head'}}<link>{{/block}} {{block 'content'}}<p>哈哈哈</p>{{/block}}
-
-
模版配置
-
向模版导入变量,举例dateFormat(日期格式处理工具)
-
const dateFormat = require('dateformat') template.defaults.imports.变量名=dateFormat//第三方方法,在模版中即可使用
-
-
模版根目录及后缀配置
-
template.defaults.root=根目录//设置根目录
-
template.defaults.extname='.art'或者'.html'//设置后缀名
-
配置过后,模版路径只写文件名即可
-
第三方模块:router(路由)
const getRouter = require('router')
const router = getRouter()
router.get('/add',(req,res) => {
let add = template('index',{})
res.end(add)
})
app.on('request',(req,res) => {
router(req,res,() => {
})//调用router方法
})
第三方模块:serve-static(静态资源服务)
const serveStatic = require('serve-static')
const serve = serveStatic(path.join(__dirname,'public'))
app.on('request',(req,res) => {
serve(req,res,() => {
})//调用serve方法
})
Express框架
-
node的第三方模块,是web应用开发框架
-
优点:
- 简洁的路由:express.Router()
- 简化服务器创建:const app = express()
- 简化http请求参数处理:
- app.get(’/’,(req,res)=>{req.query/req.params})
- app.post(’/add’,(req,res)=>{req.body})
- 对模版引擎支持度高
-
流程
const express = require('express')//引包 const app = express()//创建服务器 app.get('/',(req,res)=>{//接受get请求 res.send(req.query/req.params)//.send()优点:检测响应类型,自动设置响应的内容类型及编码,自动设置http状态码 })
中间件
中间件就是一堆方法,用来处理请求,可以将复杂的请求由多个中间件进行多次处理
- 中间件方法:由Express提供,负责连接请求,eg:app.get(),app.use()等
- 请求处理函数:开发人员提供,负责处理请求,eg:(req,res,next)=>{}
应用
- 路由保护:先app.use()判断用户的登陆状态,然后决定是否next()
- 网站维护公告:在所有路由的最前
- 404:在所有路由的最后(res.status(404).send(‘404’))
错误处理中间件:集中处理预料之外的错误
app.use((err,req,res,next) => {
res.status(500).send('服务器端错误')
})
app.get('index',(req,res)=>{
throw new Error('程序发生未知错误')
})
app.get('index',(req,res,next)=>{
fs.readFile('./demo.txt','utf8',(err,result)=>{
if(err!=null){
next(err)//异步API触发错误处理中间件
}else{
res.send(result)
}
})
})
捕获错误
异步函数和其他同步代码,用try catch
app.get('index',async (req,res,next)=>{
try{
await User.find({name:'zs'})
}catch(ex){
next(ex)//手动触发错误处理中间件,将错误信息传给下一个中间件
}
})
构建模块化路由
将多个路由模块抽离出去,在app.js中调用即可
app.use(’/home’,home)//后面是路由对象
app.use(’/admin’,admin)
参数获取
get参数:
req.query/req.params
post参数:
const bodyParser = require('body-parser')
app.use(bodaParser.urlencoded({extend:false}))//false:方法内部用querystring处理,true:方法内部调用第三方模块qs处理
req.body
Express路由参数
app.get('/find/:id',(req,res)=>{})//用占位符来对顶请求路径必须包含参数
静态资源访问
express.static(‘目录’)//托管静态资源
app.use(express.static(‘目录’))//拦截所有请求,交给express.static()方法来处理,如果是静态资源会终止请求并处理,不是则next()交给下一个中间件处理
模版引擎
express进一步封装了art-template
npm i art-template express-art-template
app.engine('art',require('express-art-template'))//配置模版引擎处理后缀名为art的模版文件
app.set('views',模版文件夹根目录)//模版引擎配置项views,默认根目录
app.set('view engine','art')//模版引擎配置项view engine,默认后缀名
app.get('/',(req,res)=>{
res.render('index',{//index是省略了根目录和后缀名的模版文件
msg:'message'//数据
})
})
app.locals对象
app.locals.users=[{//users设置在locals下,可以在所有模版中使用
name:'zs',
age:18
},{
name:'ls',
age:12
}]