node-模块


个人blog-1: 拾忆生活
个人blog-2: 极简-拾忆生活
欢迎大家来踩,同步更新


node-1

1.模块成员导出导出

e x p o r t s 是 m o d u l e . e x p o r t s 的 别 名 ( 地 址 引 用 关 系 ) ; 导 出 对 象 最 终 以 m o d u l e . e x p o r t s 为 准 \color{red}exports是module.exports的别名(地址引用关系) ;导出对象最终以module.exports为准 exportsmodule.exports();module.exports

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

s r c 目 录 放 置 源 代 码 文 件 \color{red}src目录放置源代码文件 src
s r c d i s t 目 录 放 置 构 建 后 文 件 \color{red}srcdist目录放置构建后文件 srcdist

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是不包含请求参数的请求地址

注: g e t 请 求 参 数 在 地 址 栏 中 \color{red}get请求参数在地址栏中 get

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);
});

执 行 异 步 时 , 异 步 A P I 后 面 代 码 的 执 行 依 赖 当 前 异 步 A P I 的 执 行 结 果 , 但 要 执 行 后 续 代 码 在 执 行 的 时 候 异 步 A P I 还 没 有 返 回 结 果 \color{red}执行异步时,异步API后面代码的执行依赖当前异步API的执行结果,但要执行后续代码在执行的时候异步API还没有返回结果 APIAPIAPI

粗暴方法:

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模块

解 决 异 步 a p i 执 行 时 , 可 以 顺 序 执 行 异 步 代 码 后 面 的 代 码 \color{red}解决异步api执行时,可以顺序执行异步代码后面的代码 api

简单方法(不引入promise模块)

  • 将上例粗暴方法分解成若干个函数
  • 在各个函数中用resolve方法输出处理的结果
  • 在先执行p1函数,用return拿到p2函数,
  • 再then函数执行p2函数,用return拿到p3函数,
  • 再then函数执行p3函数,。

链 式 编 程 \color{red}链式编程

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函数, 执 行 成 功 结 果 \color{red}执行成功结果 通过参数形式传出去
  • reject函数, 执 行 失 败 结 果 \color{red}执行失败结果 通过参数形式传出去
  • then函数,在函数里面的参数是 获 取 成 功 结 果 \color{red}获取成功结果 ,对应resolve函数
  • catch函数,在函数里面的参数是 获 取 失 败 结 果 \color{red}获取失败结果 ,对应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);
})

e s 7 异 步 的 a s y n c 、 a w a i t \color{red}es7异步的async、await es7asyncawait

更简单方法1

  • async function p1(){···}
    • async关键字函数(异步函数)
    • 自动包裹并返回Promise对象(不需要new Promise)
  • async函数内部
    • return 返回 执 行 成 功 结 果 \color{red}执行成功结果 (不需要reject)
    • throw 返回 执 行 失 败 结 果 \color{red}执行失败结果 (不需要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();

a s y n c 异 步 读 文 件 \color{red}async异步读文件 async

  • u t i l 模 块 \color{red}util模块 util
    • p r o m i s i f y 方 法 \color{red}promisify方法 promisify
    • 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()
    • 要用的模板路径
    • 模板的后缀是 . a r t \color{red}.art .art(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>' 不 会 解 析 h 1 标 签 \color{red}不会解析h1标签 h1
  • <p>{{@ content }}</p>
    • content: '<h1>我是标题</h1>' 会 解 析 h 1 标 签 \color{red}会解析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参数)
    • 接 收 地 址 栏 后 的 ? 参 数 \color{red}接收地址栏后的?参数
    • 例:http://localhost:3000/?name=zhangsan$age=20
    • 接收到name=zhangsan$age=20
  • req.body(获取POST参数)
    • 第三方包body-parser
    • app.use(bodyParser.urlencoded({extended: false}))

注:拦截所有请求

  • e x t e n d e d : f a l s e \color{red}extended: false extended:false
    • 方法内部使用 q u e r y s t r i n g 模 块 \color{red}querystring模块 querystring处理请求参数的格式
  • e x t e n d e d : t r u e \color{red}extended: true extended:true
    • 方法内部使用 q s 模 块 \color{red}qs模块 qs处理请求参数的格式

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('您已经登录 可以访问当前页面')
})

中间件 参 数 是 函 数 \color{red}参数是函数

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 匹配所有的请求方式,
    • 第 一 个 参 数 也 可 以 传 入 请 求 地 址 \color{red}第一个参数也可以传入请求地址 ,不论什么请求方式,只要是这个请求地址就接收这个请求
    • 可以直接传入请求处理函数,代表接收所有的请求。
  • app.get('请求路径', '处理函数')
    • 接收并处理get请求
  • app.post('请求路径', '处理函数')
    • 接收并处理post请求
    • 借 助 第 三 方 包 b o d y − p a r s e r \color{red}借助第三方包 body-parser bodyparser
 // 引入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̲()
    • 默认,请求从上到下依次匹配中间件,一旦匹配成功,终止匹配。
    • n e x t ( ) \color{red}next() next()将请求的控制权交给下一个中间件,直到遇到结束请求的中间件。
app.get('/request', (req, res, next) => {
     req.name = "张三";
     next();
 });
 app.get('/request', (req, res) => {
     res.send(req.name);
 });

路由:请求路径参数+请求参数(:xxx)冒号是占位符

  • 在当前 请 求 路 径 \color{red}请求路径 下用冒号分割,冒号后面的内容是 请 求 参 数 \color{red}请求参数
    • 如果访问/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.重要

  • e x p r e s s . s t a t i c 方 法 \color{red}express.static方法 express.static,可以多次调用
  • 配 合 p a t h 模 块 \color{red}配合path模块 path (绝对路径):
    app.use(express.static('public'));
    app.use(express.static('images'));
    如找到:http://localhost:3000/favicon.ico

2. 访 问 文 件 存 放 在 一 个 不 存 在 的 目 录 下 面 \color{red}访问文件存放在一个不存在的目录下面 访(如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()函数作用:

  1. 拼接模板路径和模板后缀
  2. 哪一个模板和哪一个数据进行拼接
  3. 将拼接结果响应给了客户端

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,在属性下对应的数据是 数 组 \color{red}数组
    • 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是存储在客户端中,供 服 务 器 端 \color{red}服务器端 存储数据

    • cookie中的数据以域名形式存储数据
    • cookie中的数据有过期时间,不设置的话,会在关闭浏览器时删除,在下次打开浏览器时服务器端会继续发送
    • cookie中的数据会随请求自动发送到服务器端
  • 在网站中 F12 找到

    • 方式一:ApplicationStorge 下的 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值