node-1
1.模块成员导出导出
a.js
// 在模块内部定义变量
let version = 1.0;
// 在模块内部定义方法(say是方法)
const sayHi = name => `您好, ${name}`;
// 向模块外部导出数据
exports.version = version;
exports.sayHi = sayHi;
module.exports.version = version;
module.exports.sayHi = sayHi;
b.js
// 导入模块a
let a = require('./b');
// 输出b模块中的version变量
console.log(a.version);
// 调用b模块中的sayHi方法 并输出其返回值
console.log(a.sayHi('我'));
2.global
可省略global
global.console.log('我是global对象下面的console.log方法输出的内容');
global.setTimeout(function (){
console.log('123');
}, 2000)
等价:
setTimeout(function (){
console.log('123');
}, 2000)
3.fs模块
npm i fs
- 读取文件内容:
fs.reaFile('文件路径/文件名称','文件编码', callback);
- callback参数err、doc
- 写入文件内容:
fs.writeFile('文件路径/文件名称', '数据', callback);
- callback参数err、result
fs.readFile('./demo.txt', 'utf8', (err, doc) => {
// 如果文件读取出错err 是一个对象 包含错误信息
// 如果文件读取正确 err是 null
// doc 是文件读取的结果
console.log(err);
console.log(doc);
});
fs.writeFile('./demo.txt', '即将要写入的内容', err => {
if (err != null) {
console.log(err);
return;
}
console.log('文件内容写入成功');
})
4.path模块
npm i path
路径拼接语法,自动结尾加’’
path.join(‘路径1’, ‘路径2’, …)
const path = require('path');
const finalPath = path.join('public', 'uploads','avatar');
console.log(finalPath);
输出:
public\uploads\avatar
5.第三方模块 Gulp == webpack
1.项目上线,HTML、CSS、JS文件压缩合并
2.语法转换(es6、less …)
3.公共文件抽离
4.修改文件浏览器自动刷新
npm i gulp -g
npm i gulp-cli -g
5.1Gulp中提供的方法
- gulp.task()
- 建立gulp任务
- gulp.src()
- 获取任务要处理的文件
- gulp.dest()
- 输出文件目录
- gulp.watch()
- 监控文件的变化
gulpfile.js
const gulp = require('gulp');
// 使用gulp.task()方法建立任务
gulp.task('first', () => {
// 获取要处理的文件,pipe中将处理后的文件输出到dist目录
gulp.src('./src/css/base.css').pipe(gulp.dest('./dist/css'));
});
执行上面的文件gulpfile.js:
在命令行中输入gulp task的第一个参数
gulp first
5.2Gulp插件
- gulp-htmlmin
- 压缩html
- gulp-csso
- 压缩css
- gulp-babel
- JavaScript语法转化
- gulp-less
- less语法转化
- gulp-uglify
- 压缩混淆JavaScript
- gulp-file-include
- 公共文件包含
- browsersync
-浏览器实时同步
安装
npm i gulp-htmlmin
npm i gulp-file-include
···
// 引用gulp模块
const gulp = require('gulp');
const htmlmin = require('gulp-htmlmin');
const fileinclude = require('gulp-file-include');
const less = require('gulp-less');
const csso = require('gulp-csso');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
// 使用gulp.task建立任务
// 1.任务的名称
// 2.任务的回调函数
gulp.task('first', () => {
console.log('我们人生中的第一个gulp任务执行了');
// 1.使用gulp.src获取要处理的文件,使用gulp.dest输出处理的文件
gulp.src('./src/css/base.css')
.pipe(gulp.dest('dist/css'));
});
// html任务
// 1.html文件中代码的压缩操作 2.抽取html文件中的公共代码
gulp.task('htmlmin', () => {
gulp.src('./src/*.html')
.pipe(fileinclude())
// 压缩html文件中的代码,选择压缩空格
.pipe(htmlmin({ collapseWhitespace: true }))
.pipe(gulp.dest('dist'));
});
// css任务
// 1.less语法转换 2.css代码压缩
gulp.task('cssmin', () => {
// 选择css目录下的所有less文件以及css文件
gulp.src(['./src/css/*.less', './src/css/*.css'])
// 将less语法转换为css语法
.pipe(less())
// 将css代码进行压缩
.pipe(csso())
// 将处理的结果进行输出
.pipe(gulp.dest('dist/css'))
});
// js任务
// 1.es6代码转换 2.代码压缩
gulp.task('jsmin', () => {
gulp.src('./src/js/*.js')
.pipe(babel({
// 它可以判断当前代码的运行环境 将代码转换为当前运行环境所支持的代码
presets: ['@babel/env']
}))
.pipe(uglify())
.pipe(gulp.dest('dist/js'))
});
// 复制文件夹
gulp.task('copy', () => {
gulp.src('./src/images/*')
.pipe(gulp.dest('dist/images'));
gulp.src('./src/lib/*')
.pipe(gulp.dest('dist/lib'))
});
// 构建任务
gulp.task('default', ['htmlmin', 'cssmin', 'jsmin', 'copy']);
执行上面的文件gulpfile.js:
在命令行中输入gulp task的第一个参数
gulp htmlmin
gulp gulp-file-include
6.第三方模块 nrm
npm下载地址切换工具:
//安装模块
npm i nrm -g
//选择下载地址
nrm ls
//切换下载地址
nrm use taobao
//再次查看下载地址
nrm ls
退出即可
再npm i xxx可更快的下载其他包
7.package.json
1.在"scripts"中,添加别名操作
"build":"nodemon app.js"
- 用
npm run build
代替nodemon app.js操作命令
2.在"scripts"中
-
"name": "node-1"
-
"main": "index.js"
-
Node.js会假设它是系统模块去node_modules文件夹中
-
首先看是否有该名字的JS文件
-
再看是否有该名字的文件夹(node-1)
-
如果是文件夹看里面是否有(index.js)
如果没有index.js查看该文件夹中的package.json中的main选项确定模块入口文件,否则找不到报错
node-2
1.1http模块
express是对http的封装,express 会在后台调用http模块
app.listen() == http.Server.listen()
http模块
const http = require('http');
const app = http.createServer();
app.listen(3000);
express模块
const express = require('express');
const app = express();
app.listen(3000);
注意:https模块
const https = require('https');
const app = https.createServer();
app.listen(3000);
1.2http的res.end
发送到浏览器页面,不是控制台
1.3http的res.writeHead
HTTP状态码、内容类型
- HTTP状态码
- 200 请求成功
- 404 请求的资源没有被找到
- 500 服务器端错误
- 400 客户端请求有语法错误
- 内容类型
- text/html
- text/css
- application/javascript
- image/jpeg
- application/json
//写头部的信息(设置http状态码和内容类型;编码信息)
res.writeHead(200, {
'content-type': 'text/html;charset=utf8'
});
1.4http的app.on(get)
- req.method 获取请求方式
- req.url 获取请求地址
- req.headers 获取请求报文信息
用到npm i url(url模块)
-
url.parse(req.url, true) 要解析的url地址,将查询参数解析成对象形式
- 第一个已拼接的字符串
- 第二个参数,true转换为对象
- 即把拼接字符串username=zhangsan$age=20,转换为对象{username=“zhangsan”,age=20}
-
let { query, pathname } = url.parse(req.url, true);
- query.xxx 获取xxx属性的值(xxx是拼接字符串的属性如name,age)
- pathname是不包含请求参数的请求地址
注:
if (pathname == '/index' || pathname == '/') {
res.end('<h2>欢迎来到首页</h2>');
}else if (pathname == '/list') {
res.end('welcome to listpage');
}else {
res.end('not found');
}
整体:
第一个参数是请求名称
app.on('request', (req, res) => {
// console.log(req.method);
// console.log(req.url);
// console.log(req.headers['accept']);
res.writeHead(200, {
'content-type': 'text/html;charset=utf8'
});
console.log(req.url);
let { query, pathname } = url.parse(req.url, true);
console.log(query.name)
console.log(query.age)
if (pathname == '/index' || pathname == '/') {
res.end('<h2>欢迎来到首页</h2>');
}else if (pathname == '/list') {
res.end('welcome to listpage');
}else {
res.end('not found');
}
if (req.method == 'POST') {
res.end('post')
} else if (req.method == 'GET') {
res.end('get')
}
});
路由概念:客户端请求地址与服务器端程序代码的对应关系。简单的说,就是请求什么响应什么。
目的:得到请求路径,去除请求参数
const pathname = url.parse(req.url).pathname;
- 原路径
- 把localhost:3000/index.html?id=1
- url.parse(req.url)
- 变成localhost:3000/index.html
- url.parse(req.url).pathname
- 变成/index.html
// 获取请求方式
const method = req.method.toLowerCase();
// 获取请求地址
const pathname = url.parse(req.url).pathname;
if (pathname == '/index' || pathname == '/') {
res.end('<h2>欢迎来到首页</h2>');
}else if (pathname == '/list') {
res.end('welcome to listpage');
}else {
res.end('not found');
}
if (req.method == 'POST') {
res.end('post')
} else if (req.method == 'GET') {
res.end('get')
}
});
if (method == 'POST') {
res.end('post')
} else if (method == 'GET') {
res.end('get')
}
再引入:
- npm i path
- npm i fs
- npm i mime
- let type = mime.getType(realPath);
- 是分析文件的内容类型,如:text/html
- res.writeHead(200, {‘content-type’: type})
- 自动分析内容类型
const app = http.createServer();
app.on('request', (req, res) => {
// 获取用户的请求路径
let pathname = url.parse(req.url).pathname;
//判断,如果路径==/,则默认的html路径,如果路径!=/,则就是pathname的路径
pathname = pathname == '/' ? '/default.html' : pathname;
// 将用户的请求路径转换为实际的服务器硬盘路径
let realPath = path.join(__dirname, 'public' + pathname);
let type = mime.getType(realPath)
// 读取文件(第一个参数路径)
fs.readFile(realPath, (error, result) => {
// 如果文件读取失败
if (error != null) {
//返回状态码和返回内容的字符编码
res.writeHead(404, {
'content-type': 'text/html;charset=utf8'
})
res.end('文件读取失败');
return;
}
res.writeHead(200, {
'content-type': type
})
res.end(result);
});
});
app.listen(3000);
console.log('服务器启动成功')
1.5 http的app.on(post)
需要npm i querystring 模块
// 用于创建网站服务器的模块
const http = require('http');
// 处理post请求参数模块
const querystring = require('querystring');
- 绑定app.on(‘request’),request请求事件
- 绑定req.on(‘data’),data数据事件
- 绑定req.on(‘end’),end处理事件
app.on('request', (req, res) => {
//请求参数传递触发
//参数传递完成触发
//变量用于拼接
let postParams = '';
//params是传递过来的参数
req.on('data', params => {
postParams += params;
});
//querystring模块处理请求参数
req.on('end', () => {
console.log(querystring.parse(postParams));
});
res.end('ok');
});
1.6静态资源、动态资源
静态资源:服务器端不需要处理,可以直接响应给客户端的资源
动态资源:相同的请求地址不同的响应资源
如
http://www.itcast.cn/article?id=1
http://www.itcast.cn/article?id=2
1.7客户端请求途径
- GET方式
- 浏览器地址栏
- link标签的href属性
- script标签的src属性
- img标签的src属性
- Form表单提交
- POST方式
- Form表单提交
2.异步
同步sync、异步async
2.1
在浏览器中全局对象是window,在Node中全局对象是global。
Node中全局对象下有以下方法,可以在任何地方使用,global可以省略。
console.log() 在控制台中输出
setTimeout() 设置超时定时器
clearTimeout() 清除超时定时器
setInterval() 设置间歇定时器
clearInterval() 清除间歇定时器
2.2
同步API可以从返回值中拿到API执行的结果;
但是异步API是不可以的(代码执行顺序不同)
console.log('before');
setTimeout(function (){
console.log('last');
}, 2000)
console.log('after');
输出:
before
after
last
console.log('代码开始执行')
setTimeout(function () {
console.log('2s')
}, 2000)
setTimeout(function () {
console.log('0s')
}, 0)
console.log('代码结束执行')
输出:
代码开始执行
代码结束执行
0s
2s
2.3 回调函数:自己定义函数让别人去调用
定义函数的参数:callback
相当于调用函数的参数:匿名函数function (data) {console.log(data);}
function getMsg (callback) {
setTimeout(function () {
callback({
msg: 'hello node.js'
})
}, 2000)
}
getMsg(function (data) {
console.log(data);
});
粗暴方法:
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)
})
})
});
2.4promise模块
简单方法(不引入promise模块)
- 将上例粗暴方法分解成若干个函数
- 在各个函数中用resolve方法输出处理的结果
- 在先执行p1函数,用return拿到p2函数,
- 再then函数执行p2函数,用return拿到p3函数,
- 再then函数执行p3函数,。
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)=> {
console.log(r1);
return p2();
}).then((r2)=> {
console.log(r2);
return p3();
}).then((r3) => {
console.log(r3)
})
解决异步编程的回调地狱问题
- resolve函数, 通过参数形式传出去
- reject函数, 通过参数形式传出去
- then函数,在函数里面的参数是 ,对应resolve函数
- catch函数,在函数里面的参数是 ,对应reject函数
const fs = require('fs');
let promise = new Promise((resolve, reject) => {
fs.readFile('./100.txt', 'utf8', (err, result) => {
if (err != null) {
reject(err);
}else {
resolve(result);
}
});
});
promise.then((result) => {
console.log(result);
})
.catch((err)=> {
console.log(err);
})
更简单方法1
- async function p1(){···}
- async关键字函数(异步函数)
- 自动包裹并返回Promise对象(不需要new Promise)
- async函数内部
- return 返回 (不需要reject)
- throw 返回 (不需要resolve)
- async function run(){···}
- await p1();
- 可以暂停异步函数执行,等待promise对象返回结果(不返回结果不向下执行)
- (不需要then函数和catch函数)
- run()
async function p1 () {
throw '发送错误'
return 'p1';
}
async function p2 () {
throw '发送错误'
return 'p2';
}
async function p3 () {
throw '发送错误'
return 'p3';
}
async function run () {
let r1 = await p1()
let r2 = await p2()
let r3 = await p3()
console.log(r1)
console.log(r2)
console.log(r3)
}
run();
-
- fs.readFile原本不返回promise对象
- 改造const readFile = promisify(fs.readFile);返回promise对象
更简单方法2
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)
console.log(r2)
console.log(r3)
}
run();
3.serve-favicon模块(图标)
npm i serve-favicon
var favicon = require('serve-favicon');
app.use(favicon(__dirname+'/public/images/favicon.ico'));
4.mongoose模块
见MongoDB用户信息后台管理.md
node-3
1.art-template模块(模板引擎)
npm i art-template
//拼接字符串模块
npm i path
path.join()
- 要用的模板路径
- 模板的后缀是 (html类型文本)
template
方法是用来拼接字符串的- 第一个参数:模板路径(绝对路径)
- 第二个参数:要在模板中显示的数据(对象类型)
index.art
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
{{ name }}
{{ age }}
</body>
</html>
app.js
const views = path.join(__dirname, 'views', 'index.art');
// 将特定模板与特定数据进行拼接
const html = template(views, {
name: '张三',
age: 20,
content: '<h1>我是标题</h1>'
})
1.1输出语法
标准语法: {{ 数据 }}
原始语法:<%=数据 %>
注:
<p>{{ content }}</p>
content: '<h1>我是标题</h1>'
<p>{{@ content }}</p>
content: '<h1>我是标题</h1>'
<!-- 标准语法 -->
<p>{{ name }}</p>
<p>{{ 1 + 1 }}</p>
<p>{{ 1 + 1 == 2 ? '相等' : '不相等' }}</p>
<p>{{ content }}</p>
<p>{{@ content }}</p>
<!-- 原始语法 -->
<p><%= name %></p>
<p><%= 1 + 2%></p>
<p><%= 1 + 1 == 2 ? '相等' : '不相等' %></p>
<p><%= content%></p>
<p><%- content%></p>
1.2条件判断
//02.js
const html = template(views, {
name: '张三',
age: 17
})
//02.art
{{if age > 18}}
年龄大于18
{{else if age < 15 }}
年龄小于15
{{else}}
年龄不符合要求
{{/if}}
1.3数据循环
//03.js
const html = template(views, {
users: [{
name: '张三',
age: 20,
sex: '男'
},{
name: '李四',
age: 30,
sex: '男'
}]
});
//模板03.art
<ul>
{{each users}}
<li>
{{$value.name}}
{{$value.age}}
{{$value.sex}}
</li>
{{/each}}
</ul>
1.4子模板include
//04.js
const html = template(views, {
msg: '我是首页'
});
//子模板header.art
我是头部
//子模板footer.art
我是底部
//主模板04.art
{{ include './common/header.art' }}
<div> {{ msg }} </div>
{{ include './common/footer.art' }}
1.5模板继承
父模板
block 'xx'
子模板
extend 'aa.art'
block 'xx'
//app.js
const html = template(views, {
msg: '首页模板'
});
//父模板layout.art
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
{{block 'link'}} {{/block}}
</head>
<body>
{{block 'content'}} {{/block}}
</body>
</html>
//子模板05.art
{{extend './common/layout.art'}}
{{block 'link'}}
<link rel="stylesheet" type="text/css" href="./main.css">
{{/block}}
{{block 'content'}}
<p>{{ msg }}</p>
{{/block}}
1.6模板配置
npm i art-template
npm i path
npm i dateformat
js文件
变量名即dateformat模块暴露的变量名
template.defaults.root = 模板目录
- 设置模板根目录
template.defaults.imports.变量名 = 变量值;
- 向模板中导入变量
template.defaults.extname = '.art'
- 设置模板默认后缀
art模板文件
dateFormat(time, 'yyyy-mm-dd')
- 第一个参数:要处理的时间
- 第二个参数:要转换时间的格式
//06.js
const template = require('art-template');
const path = require('path');
const dateFormat = require('dateformat');
template.defaults.root = path.join(__dirname, 'views');
template.defaults.imports.dateFormat = dateFormat;
template.defaults.extname = '.html';
//写后缀
const html = template('06.art', {
time: new Date()
});
//06和.html进行拼接(只写文件名,不用写后缀,因为已经配置)
console.log(template('06', {}));
console.log(html);
//06.art
{{ dateFormat(time, 'yyyy-mm-dd')}}
//06.html
我是06.html模板
1.7实战
见art-template-学生信息管理.md
2.express模块
npm i express
npm i url
Express是一个基于Node平台的web应用开发框架
- 简洁路由定义方式
- 获取HTTP请求参数简化处理
- 模板引擎支持程度高
- 中间件机制有效控制HTTP请求
区别:
- 获取请求路径pathname变简洁
- 去到的路由路径变简洁
- 获取请求参数query变简洁(GET、POST)
原生node
app.on('request', (req, res) => {
// 获取客户端的请求路径
// 获取GET参数
let { pathname,query } = url.parse(req.url,true);
// 对请求路径进行判断 不同的路径地址响应不同的内容
if (pathname == '/' || pathname == 'index') {
res.end('欢迎来到首页');
} else if (pathname == '/list') {
res.end('欢迎来到列表页页');
} else {
res.end('抱歉, 您访问的页面出游了');
}
// 获取POST参数
let postData = '';
req.on('data', (chunk) => {
postData += chunk;
});
req.on('end', () => {
console.log(querystring.parse(postData)
}));
});
req.query(获取GET参数)
- 例:
http://localhost:3000/?name=zhangsan$age=20
- 接收到name=zhangsan$age=20
req.body(获取POST参数)
- 第三方包body-parser
app.use(bodyParser.urlencoded({extended: false}))
注:拦截所有请求
-
- 方法内部使用 处理请求参数的格式
-
- 方法内部使用 处理请求参数的格式
express框架
// 引入body-parser模块
const bodyParser = require('body-parser');
// 配置body-parser模块
app.use(bodyParser.urlencoded({ extended: false }));
// 当客户端以get方式访问/时
app.get('/', (req, res) => {
res.send('Hello Express');
// 获取GET参数
console.log(req.query);
});
// 当客户端以post方式访问/add路由时
app.post('/add', (req, res) => {
res.send('使用post方式请求了/add路由');
// 获取POST参数
console.log(req.body);
});
post请求对应html
<form action="http://localhost:3000/add" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" name="">
</form>
2.1中间件
app.use
是express注册中间件的方法。- request对象(代表HTTP请求)
- response对象(代表HTTP回应)
- next回调函数(代表下一个中间件)
app.use(function(req, res) {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello world!\n");
});
中间件作用:
- 路由保护
- 客户端在访问需要登录的页面时,可以先使用中间件判断用户登录状态
- 用户如果未登录,则拦截请求,直接响应,禁止用户进入需要登录的页面。
- 用户如果登录,则响应请求
- 网站维护公告
- 在所有路由的最上面定义(app.use)接收所有请求的中间件,直接为客户端做出响应,网站正在维护中。
- 自定义404页面
例子(直接app.use拦截请求)
//网站公告
app.use((req, res, next) => {
res.send('当前网站正在维护...')
})
//自定义404页面
app.use((req, res, next) => {
// 为客户端响应404状态码以及提示信息
res.status(404).send('当前访问的页面是不存在的')
})
//路由保护1
app.use('/admin', (req, res, next) => {
// 用户没有登录
let isLogin = true;
// 如果用户登录
if (isLogin) {
// 让请求继续向下执行
next()
}else {
// 如果用户没有登录 直接对客户端做出响应
res.send('您还没有登录 不能访问/admin这个页面')
}
})
//路由保护2
app.get('/admin', (req, res) => {
res.send('您已经登录 可以访问当前页面')
})
中间件 :
const app = express();
//调用函数,实参是对象形式,用到其属性
app.use(fn ({a: 2}))
//obj是函数形参,obj.a调用其属性
function fn (obj) {
return function (req, res, next) {
if (obj.a == 1) {
console.log(req.url)
}else {
console.log(req.method)
}
next()
}
}
app.get('/', (req, res) => {
// 接收post请求参数
res.send('ok')
})
2.2常用方法
app.use((req, res, next) => { xxx; next(); });
- app.use 匹配所有的请求方式,
- ,不论什么请求方式,只要是这个请求地址就接收这个请求
- 可以直接传入请求处理函数,代表接收所有的请求。
app.get('请求路径', '处理函数')
- 接收并处理get请求
app.post('请求路径', '处理函数')
- 接收并处理post请求
- 并
// 引入body-parser模块
const bodyParser = require('body-parser');
// 配置body-parser模块
app.use(bodyParser.urlencoded({ extended: false }));
- KaTeX parse error: Undefined control sequence: \next at position 28: …以针对同一个请求设置多个中间件\̲n̲e̲x̲t̲()
- 默认,请求从上到下依次匹配中间件,一旦匹配成功,终止匹配。
- 将请求的控制权交给下一个中间件,直到遇到结束请求的中间件。
app.get('/request', (req, res, next) => {
req.name = "张三";
next();
});
app.get('/request', (req, res) => {
res.send(req.name);
});
路由:请求路径参数+请求参数(:xxx)冒号是占位符
- 在当前
下用冒号分割,冒号后面的内容是
- 如果访问/index,是没有请求参数的,
- 必须访问/index/123,这里的123就是请求参数id,这样就可以传递参数
- res.send(req.params)获取请求参数
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.get('/index/:id/:name/:age', (req, res) => {
// 接收post请求参数
res.send(req.params)
})
2.3错误处理中间件:
- 状态码处理
- 500未知错误
- 404找不到页面
- res.status(500).send(···);
- 判断函数错误处理
- if(err){ xxx; next(); }else{···}
- try-catch捕获错误
- await关键字执行
- next()执行
try {
await User.find({name: '张三'})
}catch(ex) {
next(ex);
}
//app.use集中处理
app.use((err, req, res, next) => {
res.status(500).send('服务器发生未知错误'+err.message);
})
//单个处理
app.get('/index', (req, res, next) => {
fs.readFile('./01.js', 'utf8', (err, result) => {
if (err != null) {
next(err)
}else {
res.send(result)
}
})
//捕获错误(异步实现)
app.get("/", async (req, res, next) => {
try {
await User.find({name: '张三'})
}catch(ex) {
next(ex);
}
});
2.4路由继承
一个请求地址下,继续接第二个请求地址
/home
/home/index
const express = require('express')
// 创建路由对象
const home = express.Router();
// 将路由和请求路径进行匹配
app.use('/home', home);
// 在home路由下继续创建路由
home.get('/index', () => {
// /home/index
res.send('欢迎来到博客展示页面');
});
2.5路由参数(类似路由地址继承)
访问地址:localhost:3000/find/123
app.get('/find/:id', (req, res) => {
console.log(req.params); // {id: 123}
});
2.6模块化路由
- 一级路由
/home
- 引用后有二级路由
/home/index
- 引用后有二级路由
- 一级路由
/admin
- 引用后有二级路由
/admin/index
- 引用后有二级路由
// home.js
const home = express.Router();
home.get('/index', () => {
res.send('欢迎来到博客展示页面');
});
module.exports = home;
// admin.js
const admin = express.Router();
admin.get('/index', () => {
res.send('欢迎来到博客管理页面');
});
module.exports = admin;
// app.js
const home = require('./route/home.js');
const admin = require('./route/admin.js');
app.use('/home', home);
app.use('/admin', admin);
2.7静态资源的处理
1.重要
- ,可以多次调用
-
(绝对路径):
app.use(express.static('public'));
app.use(express.static('images'));
如找到:http://localhost:3000/favicon.ico
2. (如virtual),可以指定挂载路径方式实现(第一个参数)
app.use('/virtual', express.static('public'));
如找到:http://localhost:3000/virtual/images/favicon.ico
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
3.构造函数
express.Router
是一个构造函数,调用后返回一个路由器实例var router = express.Router();
- 将
router.get(
)等定义的访问路径,挂载到根目录app.use('/', router);
2.8 express-art-template模板引擎(渲染.art的模板)
npm i art-template
npm i express-art-template
- 渲染后缀为art的模板
app.engine('art', require('express-art-template'));
- 第一个参数:要渲染的文件后缀
- 第二个参数:要使用的模板引擎
- 设置模板存放目录
app.set('views', path.join(__dirname, 'views'));
- 第一个参数:固定的
- 第二个参数:渲染好后存放的模板路径
- 不写后缀拼接art后缀
app.set('view engine', 'art');
- 第一个参数:固定的
- 第二个参数:渲染的默认模板后缀
- 渲染模板
res.render('index' ,{msg: 'message'});
- 第一个参数:模板名称,如index
- 第二个参数:渲染的默认模板后缀
res.render(
)函数作用:
- 拼接模板路径和模板后缀
- 哪一个模板和哪一个数据进行拼接
- 将拼接结果响应给了客户端
app.js
app.engine('art', require('express-art-template'));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'art');
app.get('/index', (req, res) => {
res.render('index', {
msg: 'message'
})
});
index.art
{{msg}}
在页面中看到message
2.9 app.locals 对象
将变量设置到app.locals对象下面,这个数据在所有的模板中都可以获取到。
- 属性名字自定义,如:users,在属性下对应的数据是
app.locals.users =[...];
- 获取属性
- 因为是数据,所以需要循环拿users的数据
- {{each users}}…{{/each}}
- 取值:{{KaTeX parse error: Expected 'EOF', got '}' at position 11: value.name}̲}、{{value.age}}
app.locals.users = [{
name: '张三',
age: 20
},{
name: '李四',
age: 20
}]
//第一个模板
app.get('/index', (req, res) => {
res.render('index', {
msg: '首页'
})
});
//第二个模板
app.get('/list', (req, res) => {
res.render('list', {
msg: '列表页'
});
})
list.art
{{msg}}
<ul>
{{each users}}
<li>
{{$value.name}}
{{$value.age}}
</li>
{{/each}}
</ul>
3.bcrypt密码加密模块
-
哈希单程加密方式(在加密的密码中再加入随机字符串)
-
bcrypt依赖环境第一种
- python 2.7
npm install -g node-gyp
npm install --global --production windows-build-tools
-
bcryptjs依赖环境第二种
npm install bcryptjs
-
genSalt();
方法生成随机字符串- 参数:数值越大 生成的随机字符串复杂度越高
- 默认值是 10
-
hash('123456', salt);
方法加密- 第一个参数:要进行加密的明文
- 第二个参数:生成的随机字符串
-
compare(xxx, xxx);
方法比对- 第一个参数:明文密码
- 第二个参数:加密密码
使用方法一
//导入bcrypt模块
const bcrypt = require('bcryptjs');
//生成随机字符串,genSalt()方法生成随机字符串盐
//默认随机程度为10
let salt = await bcrypt.genSalt(10);
//使用随机字符串对密码进行加密
let pass = await bcrypt.hash('123456',salt);
//比对密码
let isValid = await bcrypt.compare('123456', pass);
使用方法二
var bcrypt = require('bcryptjs');
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash("明文密码", salt, function(err, hash) {
// Store hash in your password DB.
});
});
4.cookie和session(express-session模块)
-
cookie是存储在客户端中,供 存储数据
- cookie中的数据以域名形式存储数据
- cookie中的数据有过期时间,不设置的话,会在关闭浏览器时删除,在下次打开浏览器时服务器端会继续发送
- cookie中的数据会随请求自动发送到服务器端
-
在网站中
F12
找到- 方式一:
Application
在Storge
下的Cookies
- 方式二:
Network
在当前主网站名中点击,找到Request Headers
下的Cookie
(字符串形式)
- 方式一:
-
session是对象,存储在服务器端的内存中
- session对象中可以存储多条数据
- 每一条数据都有一个
sessionId
做唯一标识
过程
-
第一次访问
1.客户端将请求数据(用户名、密码)传递给服务器端
2.服务器端接收到请求参数,并验证请求参数成功后
3.服务器端使用 session,为该用户生成唯一的 sessionId ,在session对象中存储用户信息
4.服务器端将sessionId写入客户端的cookie,发送给客户端 -
以后访问
5.客户端下次访问浏览器时,cookie会自动发送给服务器端
6.服务器端拿到cookie中存储的sessionId,然后在服务器端存储的session对象中验证当前的sessionId
7.验证成功后,服务器端响应数据
4.1 express-session模块
- 用中间件
app.use()
处理先拦截请求,再交给session处理- session() 生成session对象
- secret参数:存储秘钥,对数据加密
//导入express-session模块
const session = require('express-session');
//配置session1
app.use(session( { secret:"secret key"} ));
// 配置session2
app.use(session({
secret: 'secret key',
saveUninitialized: false,
cookie: {
maxAge: 24 * 60 * 60 * 1000
},
resave: true,
}));
5.joi模块
是js对象的规则描述语言和验证器
5.1 定义验证规则,声明变量,它的值是对象 Joi.xxx
string()
- 字符串类型
alphanum()
- 只能是字母或数字字符串
email()
- 只能是邮件格式
min(2).max(5)
- 表示字符串最小长度和最大长度
required()
- 表示该属性是必选属性
error(new Error('该属性没有通过验证'))
- 没有通过验证则指定错误信息
regex(/xxx/)
- 正则规则
- number()
- 数字类型
- integer()
- 只能整数
可以指定数组
- 如:
xxx:[Joi.string(),Joi.number()]
5.2 验证
Joi.validate(xx,xx)
- 第一个参数:要验证的对象
- 第二个参数:验证的规则
- 如:
Joi.validate({username:'abc',birthyear:1900},schema);
5.3 异步验证
- async 和 await
- try-catch 语句输出正确或错误
async function run () {
try {
// 实施验证
await Joi.validate({username: 'ab', birth: 1800}, schema);
}catch (ex) {
console.log(ex.message);
return;
}
console.log('验证通过')
}
JOi.js 完整
// 引入joi模块
const Joi = require('joi');
// 定义对象的验证规则
const schema = {
username: Joi.string().alphanum().min(2).max(10).required().error(new Error('username属性没有通过验证')),
//该正则方法跟上面等价
password:Joi.string().regex(/^[a-zA-Z0-9]{2,10}$/),
birth: Joi.number().min(2).max(10).error(new Error('birth没有通过验证')),
email:Joi.string().email()
};
//验证规则
async function run () {
try {
// 实施验证
await Joi.validate({username: 'ab', birth: 1800}, schema);
}catch (error) {
console.log('验证错误:'+error.message);
return;
}
console.log('验证通过')
}
//运行
run();
3.项目:博客
见node-博客.md
转载:https://blog.csdn.net/qq_45062586/article/details/105126403