nodejs学习笔记

nodejs学习笔记

nodejs学习配套源码

一、Node.js基础

1.下载官网

https://nodejs.org/zh-cn/>

2.定义

Node.js 是一个 Javascript 运行环境(runtime)。它让 JavaScript 可以开发后端程序,实现几乎其他后端 语言实现的所有功能,可以与 PHP、JSP、Python、Ruby 等后端语言平起平坐。

Nodejs 是基于 V8 引擎,V8 是 Google 发布的开源 JavaScript 引擎,本身就是用于 Chrome 浏览器 的 JS 解释部分,但是 Ryan Dahl 这哥们,鬼才般的,把这个 V8搬到了服务器上,用于做服务器的软件

3.Node的优势

NodeJs 语法完全是 js 语法,只要你懂 JS 基础就可以学会 Nodejs 后端开发。

NodeJs 超强的高并发能力。

现在的服务器端语言中存在着什么问题。在 Java、PHP 或者.net 等服务器端语言中,会为每一个客户端连接创建一个新的线程。而每个线程需要耗费大约 2MB 内存。也就是说,理论上,一个 8GB 内存的服务器可以同时连接的最大用户数为 4000 个左右。要让Web 应用程序支持更多的用户,就需要增加服务器的数量,而 Web 应用程序的硬件成本当然就上升了。Node.js 不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个 内部事件,通过非阻塞 I/O、事件驱动机制,让 Node.js 程序宏观上也是并行

的。使用 Node.js,一个 8GB内存的服务器,可以同时处理超过 4 万用户的连接。

实现高性能服务器

开发周期短、开发成本低、学习成本低。Node.js 自身哲学,是花最小的硬件成本,追求更高的并发,更高的处理性能。

4.Node安装

1.官网下载

官网:http://nodejs.cn/下载

1)node -v
2)npm -v
2.软件环境变量的配置

当在任意盘符下: node-v 会报错:不是内部或外部命令,也不是可运行的程序或批处理文件。原因没有配置环境变量。nodejs 安装路径,即找到node.exe的安装路径,如:C:\nodejs\ 配置环境变量 path中添加安装路径

3.Node.js中npm命令

1.装一个模块

1) npm install jquery (npm i jquery)安装最新版本
2) npm install jquery@1.12.4  指定版本

2.同时装多个模块

1) npm init  
   初始化 package.json

2) npm install bootstrap --save  
   (package.json 中 dependencies生产模式中记录模块)

3) npm install bootstrap --save-dev
   (package.json 中 devDependencies开发模式中记录模块)

4) npm install (读取package.json 中模块依赖安装)

5) npm uninstall jquery

6) npm list  查看安装所有模块及版本

7) npm list  jquery  查看jquery模块及版本
   默认访问服务器: 

npm 官网 国外服务器  https://registry.npmjs.org

访问服务器: 设置为 淘宝镜像路径 国内服务器

8) npm config list  nodejs 配置信息

9) npm config set registry https://registry.npm.taobao.org 设置镜像

10) npm config delete registry 删除镜像

11)  或者cnpm 安装

npm install cnpm -g -registry=https://registry.npm.taobao.org

cnpm -v  来测试是否成功安装
4.创建第一个应用

如何进入当前的Node.js环境

首先,聚焦在编辑器(HBuilder)的项目管理器上,鼠标右键,在外部资源管理器打开(I);进入到当前文件夹,然后按shift+鼠标右键,选择=>在此处打开Powershell窗口,输入命令即可

//在nodejs环境中执行js代码:demo1.js 文件

let user = 'tom';
console.log(user);
for(let i = 0; i< 10; i++){
console.log(i);
}

//powershell命令:  node  demo1.js  
//在nodejs环境中执行js代码:demo_http.js

//node 导入导出模块 : require('http') ; module.export 导出
//Es6 导入导出模块 :  import() 导入 ;  export default 导出

// 手写http 服务器
let http = require('http'); // 引入http系统模块 

// 1. 创建服务器 :  request 请求对象 ; response 响应对象
http.createServer(function(request,response){
	// 回调函数中写算法代码
	// 2. 解析中文内容
	response.writeHead(200,{"Content-Type":"text/html;charset=UTF-8"});
	// 3. 给响应页面信息
	response.write('哈哈');
	response.write('hello world');
	response.end('happy');

}).listen(3000); // 4. 指定端口号

//powershell命令:node demo_http.js
//浏览器地址栏输入:http://localhost:3000

如果我们使用 PHP 来编写后端的代码时,需要 Apache 或者 Nginx 的 HTTP 服务器,来处理客户端的请求相应。不过对 Node.js 来说,概念完全不一样了。使用 Node.js时,我们不仅仅在实现一个应用,同时还实现了整个 HTTP 服务器。

5.Node的模块(module)
1.作用

Node.js 中,将很多的功能,划分为了一个个 module(模块)。

Node.js 中的很多功能都 是通过模块实现。

2.HTTP模块的使用
//引用http模块 
var http = require("http"); 
//创建一个服务器,回调函数表示接收到请求之后做的事情 
var server = http.createServer(function(req,res){ 
    //req 参数表示请求,res 表示响应 
    console.log("服务器接收到了请求" + req.url); 
    res.end();   // End 方法使 Web 服务器停止处理脚本并返回当前结果 
}); 
//监听端口 
server.listen(3000,"127.0.0.1");  
3.URL模块的使用
//例如路径:http://www.xiaochao.com:6000/1.html?user=tom&age=18#haha
Url {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'www.xiaochao.com:6000',
  port: '6000',
  hostname: 'www.xiaochao.com',
  hash: '#haha',
  search: '?user=tom&age=18',
  query: 'user=tom&age=18',
  pathname: '/1.html',
  path: '/1.html?user=tom&age=18',
  href: 'http://www.xiaochao.com:6000/1.html?user=tom&age=18#haha'
}

url.parse()

解析URL(对象格式信息)

url.format(urlObject)

是上面url.parse() 操作的逆向操作 (字符串格式信息)

//在nodejs环境中执行js代码:01_http.js

var http=require('http');//http模块
var url=require('url');//url模块,里面有方法 url.parse() 转为对象的方法

http.createServer(function(req,res){
    // 1.请求地址  字符串
	//console.log(req.url);
    res.writeHead(200, { 'Content-Type': 'text/html;charset=utf8' });
    if(req.url!='/favicon.ico'){
         2. 使用 url 模块中的 parse() 方法将字符串地址转为对象格式(query对象,pathname对象)
        var result=url.parse(req.url,true);  
        //***第一个参数是地址    第二个参数是true的话query键表示把get传值转换成对象
        console.log(result);
        //默认字符串 user=tom&age=18 转对象后  {user:tom,age:18}
        console.log('user='+result.query.user);  /*获取url的get传值*/
        console.log('age='+result.query.age);
    }
    
    res.write('你好nodejs');
    res.end(); /*结束响应*/
}).listen(8000);

>(重点) url.parse(req.url,true).query //获得参数
>(重点) url.parse(req.url).pathname  //获得文件夹及文件路径

//powershell输入:node 01_http.js
//浏览器输入:http://localhost:3000/?user=admin&&age=19
/*powershell输出:
Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?user=admin&&age=19',
  query: [Object: null prototype] { user: 'admin', age: '19' },
  pathname: '/',
  path: '/?user=admin&&age=19',
  href: '/?user=admin&&age=19'
}
admin
19
*/
4.querystring模块(对有字符串的转换)

querystring.parse():将普通字符串转为对象

querystring.stringify():将普通对象转为有地址符的字符串

//02_querystring.js

let querystring = require('querystring'); // 这个模块可以将普通字符串转为对象,或将对象换为字符

let str = 'user=tom&age=18';
let obj = querystring.parse(str,'&','=');  //[Object: null prototype] { user: 'tom', age: '18' }
console.log(obj);

let obj2 ={
	user:'alice',
	age:18
}
// 将普通对象转为有地址符的字符串
let str2 = querystring.stringify(obj2);

console.log(str2);//user=alice&age=18

//powershell输入:node 02_querystring.js
5.path模块

path.join()解析相对路径

path.resolve()解析相对路径

__dirname:绝对路径目录名称

extname:获得文件扩展名称

dirname : 获得文件夹路径

basename:获得文件名称

let path = require('path');// 路径拼接

// 1. path.join() :解析相对路径
console.log(path.join('a','b')); 
// a\b (a/b也是对的,这是由于电脑系统的区别,即斜杠、反斜杠没有影响)

//  / 代表 http服务器中 根目录
console.log(path.join('/a','/b','1.html')); //  \a\b\1.html

// 2. path.resolve() : 能解析根路径,   / 代表 http服务器中 根目录
console.log(path.resolve('a','b')); // C:\Users\ZQL\Desktop\nodejs-teacher\Day02\a\b

console.log(path.resolve('/a','/b'));  //  C:\b

console.log(path.resolve('/a','b'));  //  C:\a\b

// __dirname :绝对路径的目录名称 C:\Users\ZQL\Desktop\nodejs-teacher\Day02\a\b
console.log(path.resolve('a','b'));  
// C:\Users\ZQL\Desktop\nodejs-teacher\Day02\a\b

console.log(path.resolve(__dirname,'a','b'));  
// C:\Users\ZQL\Desktop\nodejs-teacher\Day02\a\b

console.log(path.join(__dirname,'a','b')); 
// C:\Users\ZQL\Desktop\nodejs-teacher\Day02\a\b

console.log(path.extname('a/b/1.html')); //.html 获得文件扩展名称(后缀)
console.log(path.dirname('a/b/1.html')); // a/b 获得文件夹路径
console.log(path.basename('a/b/1.html')); // 1.html 获得文件名称

---------------------------------------------------------------------------------
> (重点) path.join()  路径解析:相对路径
> (重点) path.resolve() 路径解析:绝对路径     / 能解析为根相对路径
6.fs文件系统模块

1)文件夹directory

fs.mkdir(dirname,fn)  //创建文件夹make dir
fs.rmdir(dirname,fn)  //移除文件夹 remove dir(只能删除空文件)
fs.readdir(dirname,fn) //读取文件夹

// 导入文件系统模块(fs)
let fs = require('fs');

// 创建文件夹
fs.mkdir('demo', function (err) {
	/*
	失败 返回错误信息
	成功  null
	*/
	console.log(err);
	if (err) {
		console.log('创建文件夹失败');
	} else {
		console.log('创建文件夹成功');
	}
})

// 导入文件系统模块(fs)
let fs = require('fs');
// 删除空文件夹
fs.rmdir('demo', function (err) {
	if (err) {
		console.log('删除文件夹失败');
	} else {
		console.log('删除文件夹成功');
	}
})

注意: 同步写法: let results = fs.readdirSync(‘demo’);

2)文件 file

- fs.readFile(filename,fn) //读文件
fn(err,results) 

- fs.writeFile(filename,text,fn) //写文件
fn(err)

- fs.appendFile(filename,text,fn) //追加文件内容
fn(err)

- fs.unlink(filenname,fn) //删除文件
fn(err)

let fs = require('fs');
// 读文件
fs.readFile('a.txt', function (err, results) {
if (err) {
	console.log(err);
} else {
	// Buffer二进制文件类型 转为字符串 toString()
	console.log(results.toString());
}
})

let fs = require('fs');
/*写文件
如果文件不存在,那么创建文件
如果文件存在,覆盖文件内容
*/
fs.writeFile('1.txt', 'hello world!', function (err) {
	if (err) {
		console.log('写文件失败');
	} else {
		console.log('写文件成功');
	}
})

let fs = require('fs');
// 追加文件内容
fs.appendFile('2.txt', 'haha', function (err) {
	if (err) {
		console.log('追加写文件失败');
	} else {
		console.log('追加写文件成功');
	}
}

let fs = require('fs');
fs.unlink('1.txt', function (err) {
	if (err) {
		console.log('删除文件失败');
	} else {
		console.log('删除文件成功');
	}
})

3)文件夹和文件都有方法

- fs.rename(oldname,newname,fn) // 文件夹和文件修改名称和剪切移动

- fs.stat(name,fn)   // 文件夹和文件的信息
fn(err,results)

results.isFile()  //判断是否是文件

results.isDirectory() //判断是否是文件夹

let fs = require('fs');//fs模块

// 修改文件夾名
//mydemo旧文件夹名,mytest新文件夹名
fs.rename('mydemo','mytest',function(err){
	if(err){
		console.log(err);
	}else{
		console.log('修改文件夾名称成功');
	}
})

// 修改文件名
//a.txt旧文件名,b.txt新文件名
fs.rename('a.txt','b.txt',function(err){
	if(err){
		console.log(err);
	}else{
		console.log('修改文件名称成功');
	}
})

/*
当前文件和目标文件的关系:
同级 : ./1.jpg  1.jpg 
下一级  images/2.jpg
上一级: ../images/2.jpg

*/
// 剪切移動  
//w.txt当前文件相对路径;../mytest/w.txt需要移动到的目的文件相对路径
fs.rename('w.txt','../mytest/w.txt',function(err){
	if(err){
		console.log(err);
	}else{
		console.log('剪切移动成功');
	}
})

let fs = require('fs');

// 获得文件夹信息
//注:dir是一个文件夹名称
fs.stat('dir',function(err,results){
	if(err){
		console.log(err);
	}else{
		//console.log(results);
		// 判断是否是文件信息
		console.log(results.isFile()); //false
		// 判断是否是文件夹息
		console.log(results.isDirectory()); //true
	}
})

4)递归删除文件夹例子

// 导入模块 文件系统fs
let fs = require('fs');
let path = require('path');

// 递归删除非空文件夹算法
// 递归 文件的同步方法
function rmDir(pathdir) {
	// 1.读文件夹内容:
	let pathList = fs.readdirSync(pathdir);
	
	// 2. 循环遍历文件夹内容
	pathList.forEach(val => {
		let p = path.join(pathdir, val);
		//let p = pathdir + '/' + val;
		// 3. 判断是文件还是文件夹  stat.isFile()
		let stat = fs.statSync(p);
		if (stat.isFile()) {
			// 4.1是文件,删除文件
			fs.unlinkSync(p);
		} else {
			// 4.2不是文件 递归调用
			rmDir(p);
		}
	})
	// 5. 删除空文件夹
	fs.rmdirSync(pathdir);
}
rmDir('a');

从里往外删

let fs = require('fs');

function myRmdir(pathDir){
	// 0 读当前文件夹
	let contents = fs.readdirSync(pathDir);  // 同步写法
	
	contents.forEach(val=>{
		let p = pathDir + '/' + val; // 下一级的文件或文件夹路径
		
		//  判断是否是文件
		let statResults = fs.statSync(p);
		
		// 1. 判断是否是文件 如果是文件 unlink删除文件
		// 2,否则文件夹递归 myRmdir(??)
		if(statResults.isFile()){
			fs.unlinkSync(p);
		}else{
			myRmdir(p);
		}
	})
	
	// 3. 当前函数中如果是空文件  rmdir
	fs.rmdirSync(pathDir);
}
//注:111是文件夹名称
myRmdir('111');

注意: 文件处理方法 readdir()异步的,readdirSync() 同步方法;异步没有阻塞,node大部分异步。递归算法:用同步方法

5)什么是CommonJs

JavaScript 是一个强大面向对象语言,它有很多快速高效的解释器。然而, JavaScript 标准定义的 API 是为了构建基于浏览器的应用程序。并没有制定一个用于更广泛的应用程序 的标准库。CommonJS 规范的提出,主要是为了弥补当前 JavaScript 没有标准的缺陷。它的终极目标就是:提供一个类似 Python,Ruby 和 Java 语言的标准库而不只是停留在小脚本程序 的阶段。用 CommonJS API 编写出的应用,不仅可以利用 JavaScript 开发客户端应用,而且 还可以编写以下应用。

  • 服务器端 JavaScript 应用程序。(nodejs)

  • 命令行工具。

  • 桌面图形界面应用程序。

Node 应用由模块组成,采用 CommonJS 模块规范

6) 在 Node 中,模块分为两类

  • 核心模块: HTTP 模块 、URL 模块、Fs 模块 、path模块、queryString 模块

  • 文件模块:自定义模块

7) 自定义模块及使用第三方模块

1.把公共的功能抽离成为一个单独的 js 文件作为一个模块,在模块里面通过 exports 或者 module.exports 暴露属性或者方法。(导出 module.exports 或 exports)

注意: exports 是 module.exports对象的一个变量缩写

2.在需要使用这些模块的文件中,通过 require 的方式引入这个模块。这个时候就可以 使用模块里面暴露的属性和方法。(导入 require )

方法一:

//07_person.js
let person = {
	name:'tom',
	age:18,
	run(){
		console.log('能跑');
	},
	eat(){
		console.log('能吃');
	}
}
// 导出模块
module.exports = person;
//07_getPerson.js

let p = require('./07_person.js');
console.log(p.name); //tom
p.run(); //能跑

方法二:

//08_person2.js

let sleep = function(){
	console.log('能睡');
}
let run = function(){
	console.log('能跑');
}
let name = 'tom';

exports.sleep = sleep;
exports.run = run;
exports.name = name;
// exports 是 module.exports 缩写
//08_getPerson2.js

let p = require('./08_person2.js');

console.log(p.name);//tom
p.sleep();//能睡

安装 npm install silly-datetime

 // 1. 安装  npm install silly-datetime
// 2. 导入第三方模块
//注:不需要指明node_modules/silly-datetime路径;它会自动去node_modules下找
let sd = require('silly-datetime');
let http = require('http'); // http 模块

let server = http.createServer(function(req,res){
	res.writeHead(200,{'Content-Type':'text/html;charset=UTf-8'});
	// new Date() 
	let datetime = sd.format(new Date(),'YYYY-MM-DD HH:mm')
	res.end('时间是:'+datetime);
    //时间是:2020-04-20 22:28
})
server.listen(3000);

8)小练习:遍历所有文件,获得总的大小

var fs = require('fs');

function total(dirname){
	var files = fs.readdirSync(dirname);
	
	var sum = 0;
	files.forEach(function(value,key){
		var p = dirname+'/'+value; // 下一级路径
		var result = fs.statSync(p);
		
		// 如果是文件,直接获取文件大小并做累加
		if (result.isFile()) {
			// 每个文件大小的累加
			sum += result.size;
		} else {
			// total 递归--函数在执行过程中调用本身
			sum += total(p);
		}
	})
	return sum;
}

// 文件的总大小
var sum = total('Day02');
console.log(sum);

// total('test') return a+b+c文件总大小
// total('test/d') return d1+d3 文件总大小
// total('test/d/d2') return d21+d22+d23 文件总大小
7.路由原理

什么是路由:路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等 ajax传递)组成的,涉及到应用如何响应客户端对某个网站节点的访问

通俗的讲:路由就是根据不同的 URL 地址,加载不同的页面实现不同的功能

// 1. 导入模块
let http = require('http');

let app = function (req, res) {
    //console.log(req.url); 获得url根目录下的路径 
    if (req.url == '/') {
        // 首页
        // 3.1
        res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
        // 3.2结束响应并且向浏览器响应数据
        res.end('<b>我是首页</b>');
    } else if (req.url == '/list') {
        // 列表页
        // 3.1
        res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
        // 3.2结束响应并且向浏览器响应数据
        res.end('<b>我是列表</b>');
    } else if (req.url == '/show') {
        // 详细页面
        // 3.1
        res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
        // 3.2结束响应并且向浏览器响应数据
        res.end('<b>我是详细页</b>');
    } else {
        // 404 出错页面
        // 3.1
        res.writeHead(404, { 'Content-Type': 'text/html;charset=utf-8' });
        // 3.2结束响应并且向浏览器响应数据
        res.end('<b>出错页面</b>');
    }
}
// 2. 创建web server服务器
let server = http.createServer(app);

// 4.监听端口
server.listen(3000, () => {
    console.log('服务器开启了');
});
// 1. 导入模块
let http = require('http');
let fs = require('fs');
let app = function (req, res) {
   //console.log(req.url); 获得url根目录下的路径 
   if (req.url == '/') {
       //首页
       // ***读取html页面
       fs.readFile('./views/index.html', function (err, results) {
           // 3.1
           res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
           // 3.2结束响应并且向浏览器响应数据
           res.end(results);
       });
   } else if (req.url == '/list') {
       // 列表页
       fs.readFile('./views/list.html', function (err, results) {
           // 3.1
           res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
           // 3.2结束响应并且向浏览器响应数据
           res.end(results);
       })
   } else if (req.url == '/show') {
       // 详细页面
       fs.readFile('./views/show.html', function (err, results) {
           // 3.1
           res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
           // 3.2结束响应并且向浏览器响应数据
           res.end(results);
       })
   } else {
       // 404 出错页面
       fs.readFile('./views/error.html', function (err, results) {
           // 3.1
           res.writeHead(404, { 'Content-Type': 'text/html;charset=utf-8' });
           // 3.2结束响应并且向浏览器响应数据
           res.end(results);
       })
   }
}
// 2. 创建web server服务器
let server = http.createServer(app);

// 4.监听端口
server.listen(3000, () => {
   console.log('服务器开启了');
});
let http =require('http');
let url = require('url'); // url.parse() 解析地址

let fs = require('fs'); // 文件模块
let server = http.createServer(function(req,res){
let urlpath = url.parse(req.url).pathname; // 文件路径名称

// console.log(urlpath);  根路径首页 /  列表页 /list  显示页 /show
if(urlpath == '/'){
	fs.readFile('./views/index.html',function(err,results){
		res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
		res.end(results);
	})
}else if(urlpath =='/list'){
	fs.readFile('./views/list.html',function(err,results){
		res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
		res.end(results);
	})
	
}else if(urlpath =='/show'){
	fs.readFile('./views/show.html',function(err,results){
		res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
		res.end(results);
	})
}else{
	// 出错页面
	fs.readFile('./views/error.html',function(err,results){
		res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
		res.end(results);
	})
}
})
server.listen(8000);
8.路由get传值

query对象

路由结果: http://localhost/user?age=18
接收值: url.parse(req.url, true).query 接收  

动态传值 params对象

(express框架,koa 框架实现 将来 restFul 接口常用写法)

//路由结果: http://localhost/user/18

//接收值: express或 koa 中封装了 req.params 接收 

//路由:  /user/:age

let http =require('http');
let url = require('url'); // url.parse() 解析地址
let fs = require('fs');
let server = http.createServer(function(req,res){
let urlpath = url.parse(req.url).pathname; // 文件路径名称
if(urlpath == '/user'){
//  get 传值 :age=18&name=lis  转成对 {age:18,name:'lis'}
let  urlInfo = url.parse(req.url,true).query;// query對象
let  name = urlInfo.name;
if(urlInfo){
	// 返回成功信息
	res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
	res.end(`{"ok":true,"msg":"成功返回客户端值","data":"名称${name}"}`);
}else{
	// 返回失败信息
	res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
	res.end(`{"ok":false,"msg":"失败","data":null}`);
}
}else{
// 加载资源  3_get.html,  juery库,css  读需要资源的文件
let file_name = './public' + urlpath;
console.log(file_name);
fs.readFile(file_name,function(err,results){
	res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
	res.end(results);
})
}
})
server.listen(8000);
9.路由post传值

文件流操作 (node)

req.on('data',fn)  事件监听(触发事件)

req.on('end',fn)

body-parser 第三方模块 (express,koa 实现)

let http =require('http');
let url = require('url'); // url.parse() 解析地址
let fs = require('fs');
let querystring = require('querystring');

let server = http.createServer(function(req,res){
// 文档流post 数据传递
let str = '';
req.on('data',function(data){
	str += data;  // post 接收的数据
})
req.on('end',function(){
	// 写路由代码
	let urlpath = url.parse(req.url).pathname; // 文件路径名称
	if(urlpath == '/user'){
		let postData = querystring.parse(str); // 将post数据转为对象
		let name = postData.name;
	    if(postData){
			// 返回成功信息
			res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
			res.end(`{"ok":true,"msg":"成功返回客户端值","data":"名称${name}"}`);
		}else{
			// 返回失败信息
			res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
			res.end(`{"ok":false,"msg":"失败","data":null}`);
		}
	}else if(urlpath == '/post'){
		// 渲染静态页面
		fs.readFile('./4_post.html',function(err,results){
			res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
			res.end(results);
		})
	}else{
		// 资源加载
		// 加载资源 juery库,css  读需要资源的文件
		let file_name = './public2' + urlpath;
		console.log(file_name);
		fs.readFile(file_name,function(err,results){
			res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
			res.end(results);
		})
	}
})

})
server.listen(8000);
/*
   node  服务器的热更新:
    npm install -g nodemon  热更新模块
	运行: node http.js  换成  nodemon http.js
*/

let http =require('http');
let url = require('url'); // url.parse() 解析地址
let fs = require('fs');
let querystring = require('querystring');

// 模拟数据库中的 用户表的 信息
let user =[
	{user:'lis',pass:'123456'}
]
let server = http.createServer(function(req,res){
	let str = '';
	req.on('data',function(data){
		str += data; // post数据的接收
	})
	req.on('end',function(){
		// 写代码
		let urlpath = url.parse(req.url).pathname; // 路径名称
		console.log(urlpath);
		// 写路由
		if(urlpath == '/login'){
			// 渲染静态页面
			fs.readFile('./5_login.html',function(err,results){
				res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
				res.end(results);
			})
		}else if(urlpath == '/dologin'){
			let postData = querystring.parse(str);
			// 登录判断
			if(user[0].user != postData.user){
				res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
				res.end(`{"ok":false,"msg":"用户名称验证不正确"}`);
			}else if(user[0].pass != postData.pass){
				res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
				res.end(`{"ok":false,"msg":"用户密码验证不正确"}`);
			}else{
				res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
				res.end(`{"ok":true,"msg":"登录成功"}`);
			}
		}else{
			// 加载资源
			let file_names = './public' + urlpath;
			fs.readFile(file_names,function(err,results){
				res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
				res.end(results);
			})
		}
	})
	
})
server.listen(8000);
//usered.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<!--  判断用户是否被占用  -->
		用户名称:<input type="text" class="user"><span class="msg"></span>
		<script src="jquery-1.12.4.min.js"></script>
		<script>
			$('.user').blur(function(){
				$.ajax({
					url:'http://localhost:8000/usered',
					type:'get',
					data:{user:$('.user').val()},
				    dataType:'json',
					success(res){
						//alert(res.msg);
						$('.msg').html(res.msg);
					}
					
				})
			})
		</script>
	</body>
</html>

--------------------------------------------------------------------------------------------
//nodejs环境下:usered-router.js

let http =require('http');
let url = require('url'); // url.parse() 解析地址
let fs = require('fs');
let querystring = require('querystring');

let user =[
	{user:'zhangs',pass:'123456'},
	{user:'lis',pass:'123456'},
	{user:'alice',pass:'123456'},
	{user:'tina',pass:'123456'},
	{user:'jerry',pass:'123456'}
]

let server = http.createServer(function(req,res){
	
	// 写路由代码
	let urlpath = url.parse(req.url).pathname; // 文件路径名称
	if(urlpath == '/usered'){
		let getData = url.parse(req.url,true).query; // get 传输接收 query对象的数据
		let getuser = getData.user;
		for(let i = 0; i< user.length; i++){
			if(user[i].user == getuser){
				// 返回失败信息
				res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
				res.end(`{"ok":false,"msg":"用户名已存在"}`);
			}
		}
		// 返回成功信息
		res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
		res.end(`{"ok":true,"msg":"用户名称可以使用"}`);
	}else if(urlpath == '/user'){
		// 渲染静态页面
		fs.readFile('./usered.html',function(err,results){
			res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
			res.end(results);
		})
	}else{
		// 资源加载
		// 加载资源 juery库,css  读需要资源的文件
		let file_name = './public' + urlpath;
		console.log(file_name);
		fs.readFile(file_name,function(err,results){
			res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
			res.end(results);
		})
	}
})
server.listen(8000);
10.动态路由(动态路由):params

(express框架,koa 框架实现 将来 restFul 接口常用写法)

路由结果: http://localhost/user/18

接收值: express或 koa 中封装了 req.params 接收 

路由  /user/:age
11.模板引擎

ejs,jade,koa中 art-template (思想:正则表达式编译语法)

安装:npm install ejs

语法:

<%= 变量 %> html标记会转为 实体

<%- 变量 %> 能解析html标记 

<% %> 写 js 

//注意: = 向浏览器输出内容
// 1. 系统模块导入模块
let http = require('http');
let fs = require('fs');

// 第三方自定义模块 导入ejs的模板引擎
let ejs = require('ejs');
let app = function (req, res) {
	//console.log(req.url); 获得url根目录下的路径 
	if (req.url == '/') {
	    //首页
	    // ***读取html页面
	    fs.readFile('./views/index.html', function (err, results) {
	        // 3.1
	        res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
	        // 3.2结束响应并且向浏览器响应数据
	        res.end(results);
	    });
	} else if (req.url == '/list') {
	    // 列表页
	    fs.readFile('./views/list.html', function (err, results) {
	        // 链接数据库查询列表数据(暂时用临时模拟数据)
	        let data = {
	            head: '技术文章列表',
	            itemLists: [
	            { title: 'vue-router的使用', author: 'tom' },
	            { title: 'better-scroll的使用', author: 'alice' },
	            { title: 'lazy懒加载的使用', author: 'jerry' },
	            { title: 'axios的用法', author: 'tom' },
	            { title: 'scss的使用', author: 'tom' }
	            ]
			}
	        // *** 用ejs 模板引擎将动态数据传递 list.html页面中
	        //result二进制转为字符串
	        let ejsdata = ejs.render(results.toString(), data);
	        // 3.1
	        res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
	        // 3.2结束响应并且向浏览器响应数据
	        res.end(ejsdata);
	    })
	}else if (req.url == '/show') {
	    // 详细页面
	    fs.readFile('./views/show.html', function (err, results) {
	        let data = {
	            title: 'vue-router的使用',
	            time: '2019-02-28',
	            content: 'vue-router路由原理实现'
	
	        }
	        let ejsdata = ejs.render(results.toString(), data);
	        // 3.1
	        res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
	        // 3.2结束响应并且向浏览器响应数据
	        res.end(ejsdata);
	    })
	} else {
	    // 404 出错页面
	    fs.readFile('./views/error.html', function (err, results) {
	        // 3.1
	        res.writeHead(404, { 'Content-Type': 'text/html;charset=utf-8' });
	        // 3.2结束响应并且向浏览器响应数据
	        res.end(results);
	    })
	}
}

// 2. 创建web server服务器
let server = http.createServer(app);

// 4.监听端口
server.listen(3000, () => {
    console.log('服务器开启了');
});
//简单小例子1:
//views/test.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		{{name}}
	</body>
</html>
--------------------------------------------------------------------------------------------
//nodejs环境下: 06_模板引擎.js

let http = require('http');
let fs = require('fs');

let server = http.createServer(function(req,res){
	let name = 'tom诸葛亮';
	let test = fs.readFileSync('./views/test.html').toString();
	let content = test.replace('{{name}}',name);
	res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
	res.end(content);
})
server.listen(9000);

//powershell:nodemon 06_模板引擎.js
//浏览器:http://localhost:9000
//输出:tom诸葛亮
//简单小例子2:
//  views/list2.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		//解析变量
		<%= head %>
		
		<!--  ejs 的循环 -->
		<%for(var i = 0; i< itemList.length; i++){%>
		    <p><%= itemList[i].title%></p>
		<%}%>
		
		//ejs 条件 
		<%if(head !='ok'){%>
		  <b>哈哈</b>
		<%}else{%>
		  <b>good</b>
		<%}%>
	</body>
</html>

--------------------------------------------------------------------------------------------
//nodejs环境下: 07_ejs.js

let http = require('http');
let fs = require('fs');
let url = require('url');

// 安装 ejs 模板引擎  npm install ejs
let ejs = require('ejs');

let server = http.createServer(function(req,res){
	let urlpath = url.parse(req.url).pathname;
	// 写路由
	console.log(urlpath);
	if(urlpath == '/list'){
		fs.readFile('./views/list2.html',function(err,results){
			let data ={
				head:'技术文章列表',
				itemList:[
					{title:'html5标记',author:'tom'},
					{title:'CSS3样式',author:'tom'},
					{title:'javascript',author:'tom'},
					{title:'jquery',author:'tom'},
					{title:'node',author:'tom'}
				]
			}
			// 使用ejs 模板引擎
			// results 二进制 转为字符串
			let ejscontent = ejs.render(results.toString(),data);
			res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});
			res.end(ejscontent);
		})
	}
})
server.listen(9000);

//powershell输入:nodemon 07_ejs.js
//浏览器地址栏输入;http://localhost:9000/list

/*浏览器输出:
技术文章列表
html5标记

CSS3样式

javascript

jquery

node

哈哈
*/

二、express

1. 什么是express

Express 是一个基于 Node.js 封装的上层服务框架,它提供了更简洁的 API 更实用的新功能。它通过中间件和路由让程序的组织管理变的更加容易;它提供了丰富的 HTTP 工具;它让动态视图

的渲染变的更加容易;它还定义了一组可拓展标准。

2.简单的express

//全局安装: npm install express -g

//例子1
var express = require("express");
var app = express();
app.get("/", function(request, response) {
	response.send("Hello world!");
});

app.listen(3000, function() {
	console.log("Express app started on port 3000.");
}); 
//例子2:
app.use(express.static('./public')); // 加载静态资源
//例子3: ejs 模板引擎使用
//npm install ejs
const template = require('ejs');
//设置模板引擎来渲染

// 设置模板的渲染函数
// 第一个参数指的是文件扩展名
// 第二个参数callback 是模板引擎的主函数,接受文件路径、参数对象和回调函数作为其参数。
app.engine('.html',template.__express);
// 设置模板文件的路径,放模板文件的目录
app.set('views',path.join(__dirname,'./views'));
// 设置默认的模板引擎,第二个参数指明视图文件的后缀
app.set('view engine','html');
//例子4  express  get 数据

//npm install body-parser
const bodyParser=require('body-parser');

server.use(bodyParser.urlencoded({extended: false}));

//用户请求
server.use('/', function (req, res, next){
   console.log(req.query, req.body);
});

//接收用户请求
server.get('/index', function (req, res){
   res.render('1.ejs', {name: 'blue'});
});
//例子5  express  post 数据
const bodyParser=require('body-parser');

server.use(bodyParser.urlencoded({extended: false}));

//用户请求
server.use('/', function (req, res, next){
	console.log(req.query, req.body);
});

//接收用户请求
server.get('/index', function (req, res){
	res.render('1.ejs', {name: 'blue'});
});
//例子6  express路由:
const express=require('express');
var server=express();
//目录1:/user/
var routeUser=express.Router();
routeUser.get('/1.html', function (req, res){  
    //http://xxx.com/user/1.html
	res.send('user1');
});
routeUser.get('/2.html', function (req, res){   
    //http://xxx.com/user/2.html
	res.send('user22222');
});
server.use('/user', routeUser);

//目录2:/article/
var articleRouter=express.Router();
server.use('/article', articleRouter);

articleRouter.get('/10001.html', function (req, res){   
    //http://xxxx.com/article/10001.html
	res.send('asdfasdfasdf');
});
server.listen(8080);
//例子7  node中   mysql 连接数据库
const mysql=require('mysql');

//1.连接
//createConnection(哪台服务器, 用户名, 密码, 库)
var db=mysql.createConnection({host: 'localhost', user: 'root', password: '123456', database: '20161222'});

//2.查询
//query(干啥, 回调)
db.query("SELECT * FROM `user_table`;", (err, data)=>{
   if(err)
       console.log('出错了', err);
   else
       console.log('成功了');
   console.log(JSON.stringify(data));
});
//例子8 express mysql 连接数据库

const express=require('express');
const static=require('express-static');
const bodyParser=require('body-parser');
const mysql=require('mysql');

//连接池
const db=mysql.createPool({
	host: 'localhost', 
	user: 'root', 
	password: '123456',
	database: 'cms'
});

var server=express();
server.listen(8080);

//3.post数据
server.use(bodyParser.urlencoded({extended: false}));

//4.配置模板引擎
//输出什么东西
server.set('view engine', 'html');
//模板文件放在哪儿
server.set('views', './template');
//哪种模板引擎
server.engine('html', consolidate.ejs);

//接收用户请求
server.get('/', (req, res)=>{
   //查询banner的东西
   db.query("SELECT * FROM banner_table", (err, data)=>{
       if(err){
           console.log(err);
           res.status(500).send('database error').end();
       }else{
           console.log(data);
           res.render('index.ejs', {banners: data});
       }
   });
});

3.脚手架 express

npm install -g express-generator@4
express -e 项目名称
cd  项目名称
npm install
npm start  开启   (nodemon start 实时更新)

4.中间件

1.什么是中间件

我们不采用一个巨大的 request 请求处理函数,相反我们将一系列简单的处理函数组合起来。每一个小的处理 函数对应一个小任务,而这些处理函数就被称为中间件;

中间件的本质就是一个函数,在收到请求和返回相应的过程中做一些我们想做的事情。

2.特性

执行任何代码。

修改请求和响应对象。

终结请求-响应循环。

调用堆栈中的下一个中间件。

3.中间件种类
应用级中间件:
//例子1
var express = require('express')
var app = express()

var myLogger = function (req, res, next) {
   console.log('LOGGED')
   next()
}

app.use(myLogger)

app.get('/', function (req, res) {
	res.send('Hello World!')
})

app.listen(3000)
// 例子2
var express = require('express')
var app = express()

var requestTime = function (req, res, next) {
    req.requestTime = Date.now()
    next()
}

app.use(requestTime)

app.get('/', function (req, res) {
    var responseText = 'Hello World!<br>'
    responseText += '<small>Requested at: ' + req.requestTime + '</small>'
    res.send(responseText)
})

app.listen(3000)
//例子3
    
module.exports = function(options) {
    return function(req, res, next) {
        // Implement the middleware function based on the options object
        next()
    }
}

var mw = require('./my-middleware.js')

app.use(mw({ option1: '1', option2: '2' }))
路由级中间件
错误处理中间件
内置中间件

express.static 是Express目前唯一内置的一个中间件。

第三方中间件

例如 : body-parser 模块…

简单小例子1:
//  routes/admin/index.js

var express = require('express');
var router = express.Router();

/* localhost:3000/admin */
router.get('/', function(req, res, next) {
	res.send('我是后台首页');
});

module.exports = router;
//app.js

/*注:更改package.json文件里面的
"scripts": {
    "start": "nodemon ./bin/www"
  }
*/

// 1. 报错模块
var createError = require('http-errors');
// 2. express 模块
var express = require('express');
//3. 路径模块
var path = require('path');

// 4. 类似本地存储,cookie 模块
var cookieParser = require('cookie-parser');

// 5. 日志模块
var logger = require('morgan');

// 9. 路由分组的管理
var adminRouter = require('./routes/admin/index');


// 6. 实例express模块
var app = express();

// 中间件
// (1) 企业级中间件
app.use(function(req,res,next){
	console.log('hellowordhaha');
	next();  // 到下一个中间件
})

// (2)路由级中间件
app.get('/users',function(req,res,next){
	console.log('users下根路由匹配成功');
	next();
})

app.get('/users',function(req,res,next){
	console.log('users下我是根路径');
	next();
})


// 7. ejs 模板引擎 默认 ejs 的后缀修改为  html
app.engine('.html',require('ejs').__express);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');


// 8. (5)第三方中间件
app.use(logger('dev'));
// 支持json
app.use(express.json());
// body-parser 模块的配置: 支持post 数据传递
app.use(express.urlencoded({ extended: false }));
// cookie 的应用
app.use(cookieParser());
// 静态资源加载 (4) 内置中间件
app.use(express.static(path.join(__dirname, 'public')));

// 应用路由分组
app.use('/admin', adminRouter);
// app.use('/api', apiRouter);




//(3) 错误处理的中间件(简单错误处理,注意放到最后)
// app.use(function(req,res){
// 	res.status(404).send('这是404没有匹配到路径');
	
// })
// 应用报错机制

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

//powershell: nodemon start
//浏览器:http://localhost:3000/admin
//输出:我是后台首页

三、mySQL数据库

1.mySQL数据库基础

1.针对库的操作
1. 创建数据库:CREATE DATABASE lamp;
2. 删除数据库:DROP DATABASE lamp;
3. 展示数据库:SHOW DATABASES;
4. 使用数据库:USE lamp;
2.针对表的操作
1. 创建数据表:

CREATE TABLE users(

	  id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
	
	  name VARCHAR(60) NOT NULL UNIQUE,
	
	  sex TINYINT NOT NULL

)

2. 展示数据表:SHOW TABLES;

3. 删除数据表:DROP TABLE user;

4. 查看表结构:DESC user; 
3.增删改
1.添加数据:
insert into users(username,age,password) values("zhangsan",20,"123456")

// 同时添加多条数据
insert into users(username,age,password) values("zhangsan",20,"123456"),("zhangsan",20,"123456"),("zhangsan",20,"123456")

注意:
1.users必须是数据表,后面的()内必须是数据表的字段值
2.values是设置数据表的内容
内容如果是字符串,必须加引号
数字可以不加
2.修改数据:update 表名 set 字段名=修改值[,字段名=修改值[,....]] [where 条件]
update users set age=20,password="123456" where username="wangwu"

注意:
1.字符串必须添加引号

3.删除数据:delete from 表名 [where 条件]
delete from users where username="four"

// 查询所有的数据
select * from users;
4.查询
select 字段名|* from 表名
-- [ where 搜索条件]
-- [ group by 分组列名[having 分组后的子条件]]
-- [ order by 排序列名 [desc降序|asc升序(默认)]]
-- [ limit m[,n] 获取部分数据(分页) ]

查询用户表中所有的数据:select * from users

根据条件查询
where username="zhangsan"
where age > 20
where username="zhangsan" and age < 30
where age < 18 or age > 60
where age between 20 and 30
where age not between 20 and 30
where age in (18,30,42)
where age not in (18,30,42)
where username like "%zhang"
where username like "san%"
where username like "%ang%"

排序
order by age desc:年龄降序排序
order by age asc:年龄生序排序

限制
limit 5:只展示5条数据
limit 5,5:跳过5条数据,展示5条数据(也就是第二页数据)
limit 10,5:跳过10条数据,展示5条数据(也就是第三页数据)

特殊查询
select count(*) from users:查询数据总条数
select age+5 from users:每个人的年龄加5岁
select age as nianling from users:取别名
5.赋予权限
1.给指定用户赋予相应的权限:GRANT ALL ON lamp.* to zhangsan@'%' IDENTIFIED BY '123';
2.刷新生效,否则就要重启MySQL服务才可以:FLUSH PRIVILEGES;
3.查看指定用户的权限信息:SHOW GRANTS FOR zhangsan@localhost
4.重设密码:GRANT USAGE ON *.* TO 'zhangsan'@localhost IDENTIFIED BY PASSWORD '';
5.删除整个用户的权限:DROP zhangsan; 
6.数据的导入和导出
注意:必须在cmd命令下直接导入和导出,不能在mysql命令行下操作

导出blog库的数据
mysqldump -u root -p blog > blog.sql

导出blog下users表的数据
mysqldump -u root -p blog users > users.sql

将sql库文件导入到数据库中(要求数据库必须存在)
mysql -u root -p blog < blog.sql

将sql数据表文件导入到数据库中
mysql -u root -p blog < users.sql
7.字段限制
unsigned:无符号(没有负号) 0-255
not null:不能为空
default:默认值
primary key:主键
auto_increment:自增
unique:唯一键(对应的值绝对不能重复)
8.字段类型
tinyint:1个字节(-128-1271字节 = 2^8 =-128-127) 
存储年龄等类型
int:4个字节   4字节 = 2^32 = (-2147483648,2147483647)
存储ID主键,时间戳
char():固定长度字符串
密码,手机号码,身份证号码,邮编固定位数
varchar():不固定长度字符串 最多是255
用户名,邮箱等信息
text:超大文本 随便存
存储文章

2. node操作mySQL

// 1.引入MySQL
var mysql = require('mysql');
// 2.连接MySQL
var connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: '123456',
    database: 'h532'
});
// 3.判断连接是否成功
connection.connect(function(err) {
    if (err) {
        console.error('连接mysql失败: ' + err.stack);
        return;
    }
});
// 4.发送SQL语句
connection.query('select * from user', function(error, results, fields) {
    if (error) throw error;
    console.log(results);
});
// 4.添加数据
connection.query('insert into users(username,age,password,phone) values("jeff",28,"123456","18501097813")',function(err,results){
    console.log(err);
    if (err) return;
    console.log('添加数据成功');
});
// 4.修改数据
connection.query('update users set password="1234abcdpass" where username="lisi1"',function(err,result){
    if (err) return;
    console.log('修改成功');
});
// 4.删除数据
connection.query('delete from users where username="lisi2"',function(err,results){
    if (err) return;
    console.log('删除成功');
});

3. express操作mysql表的增删改等操作

//  routes/admin/index.js

let express = require('express');
let router = express.Router();

// localhost:3000/admin/
router.get('/',function(req,res){
	res.send('我是后台的首页');
})

// 路由分组
var managerRouter = require('./manager.js');
var goodsRouter = require('./goods.js');

// localhost:3000/admin/manager
router.use('/manager', managerRouter);

// localhost:3000/admin/goods
router.use('/goods', goodsRouter);

module.exports = router;

// routes/admin/goods.js

let express = require('express');
let router = express.Router();

// localhost:3000/admin/goods
router.get('/',function(req,res){
	res.send('我是商品列表');
})

router.get('/add',function(req,res){
	res.send('我是商品添加');
})

module.exports = router;
//  module/db.js
// 1. 按装并且导入mysql
let mysql = require('mysql');
// 2. 连接mysql
let db = mysql.createConnection({
	host:'127.0.0.1', // mysql 域名或ip
	user:'root', // mysql 用户名
	password:'123456',// mysql 密码
	database:'myshop' //数据库名称
});
module.exports =db;
// views/admin/manager/add.html
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<form action="/admin/manager/doadd" method="post">
			用户名称:<input type="text" name="username"><br>
			用户密码:<input type="password" name="password"><br>
			用户电话:<input type="text" name="mobile"><br>
			<button>添加</button>
		</form>
	</body>
</html>
//  views/admin/manager/edit.html
 <!DOCTYPE html>
 <html>
 	<head>
 		<meta charset="utf-8">
 		<title></title>
 	</head>
 	<body>
 		<form action="/admin/manager/doedit" method="post">
 			用户名称:<input type="text" name="username" value="<%=dataone.username%>"><br>
 			用户密码:<input type="password" name="password" value="<%=dataone.password%>"><br>
 			用户电话:<input type="text" name="mobile" value="<%=dataone.mobile%>"><br>
 			<input type="hidden" name="id" value="<%=dataone.id%>">
 			<button>添加</button>
 		</form>
 	</body>
 </html>
//  views/admin/manager/list.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<a href="/admin/manager/add">添加管理员</a>
		<table border="1" align="center" width="800">
			<tr>
				<th>编号</th>
				<th>姓名</th>
				<th>电话</th>
				<th>创建时间</th>
				<th>操作</th>
			</tr>
			<%for(var i = 0; i<data.length; i++){%>
			<tr>
				<td><%= data[i].id%></td>
				<td><%= data[i].username%></td>
				<td><%= data[i].mobile%></td>
				<td><%= data[i].addtime%></td>
				<td>
					<a href="/admin/manager/edit?id=<%=data[i].id%>">修改</a>
					<a href="/admin/manager/del?id=<%=data[i].id%>">删除</a>
				</td>
			</tr>
			<%}%>
		</table>
	</body>
</html>
//  views/admin/manager/list2.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<a href="/admin/manager/add">添加管理员</a>
		<table border="1" align="center" width="800">
			<tr>
				<th>编号</th>
				<th>姓名</th>
				<th>电话</th>
				<th>创建时间</th>
				<th>操作</th>
			</tr>
			<%for(var i = 0; i<data.length; i++){%>
			<tr>
				<td><%= data[i].id%></td>
				<td><%= data[i].username%></td>
				<td><%= data[i].mobile%></td>
				<td><%= data[i].addtime%></td>
				<td>
					<a href="/admin/manager/edit?id=<%=data[i].id%>">修改</a>
					<a href="/admin/manager/del?id=<%=data[i].id%>">删除</a>
				</td>
			</tr>
			<%}%>
			
			<tr>
				<td colspan="6" align="center">
					   <a href="/admin/manager?p=1">首页</a>
				   <a href="/admin/manager?p=<%=page.prev%>">上一页</a>
				   <%for(var i = 1; i<=page.pages;i++){%>
				   <a href="/admin/manager?p=<%=i%>"><%=i%></a>	  
				   <%}%>
				   <a href="/admin/manager?p=<%=page.next%>">下一页</a>
				   <a href="/admin/manager?p=<%=page.pages%>">尾页</a>
				</td>
			</tr>
		</table>
	</body>
</html>
//  routes/admin/manager.js

let express = require('express');
let router = express.Router();
// 导入数据链接的对象
let db = require('../../module/db.js');

// localhost:3000/admin/manager

// 1.查询的显示
router.get('/',function(req,res){
	//res.send('我是管理员列表');
	
	// db.query(sql语句,fn)
	db.query('select * from manager',function(err,data){
		if(err){
			console.log('查询失败');
		}else{
			console.log('查询成功'+ data);
			res.render('admin/manager/list.html',{data:data});//,{data:data}
		}
	})
	
	
})

/* 查询的显示 */
/* router.get('/', function(req, res) {
  // 1. 查询总条数
  db.query('select count(*) as total from manager',function(err,results){
  	 var page = {};
  	 //2. 总条件数
  	 page.total = results[0].total;
  	 // 3. 每页显示的条数
  	 page.every = 3;
  	 // 4. 总页数
  	 page.pages = Math.ceil(page.total/page.every);
  	 // 5. page.now 动态当前页??????*******
  	 page.now = req.query.p?Number(req.query.p):1; // get 接收。没有req.query.p值时,初始值是第一页
  	 console.log(page.pages);
  	 // 6. 判断后的上一页
  	 page.prev = page.now-1 <1?1:page.now-1; 
  	 // 7. 判断后的下一页
  	 page.next = page.now+1 >page.pages?page.pages:page.now+1;
  	 // 8. 开始索引的偏移量
  	 var offset =(page.now-1)*page.every;
  	 // 9 用 limit 实现分页的数据查询
  	 db.query('select * from manager limit '+offset+','+page.every+';',function(err,results){
  	 	   if(err){
  	 	   	  console.log('查询失败');
  	 	   }else{
  	 	   	  res.render('admin/manager/list',{data:results,page:page});
  	 	   }
  	 })
  	 
  })
	
}); */

// 2.添加  /admin/manager/add
router.get('/add',function(req,res){
	//res.send('我是管理员添加');
	res.render('admin/manager/add.html');  // 渲染静态添加页面
})

router.post('/doadd',function(req,res){
	// 数据库添加
	//console.log(req.body);
	let username = req.body.username;
	let password = req.body.password;
	let mobile = req.body.mobile;
	let addtime = new Date().getTime();  // 时间戳
	//console.log('insert into manager(username,password,mobile,addtime) values ("'+username+'","'+password+'","'+mobile+'","'+addtime+'")');
	db.query('insert into manager(username,password,mobile,addtime) values ("'+username+'","'+password+'","'+mobile+'","'+addtime+'")',function(err,results){
		if(err){
			res.redirect('back');// 历史记录,返回上一页, 添加页面
		}else{
			res.redirect('/admin/manager'); // 管理员列表
		}
	})
	
})

// 3修改
router.get('/edit',function(req,res){
	// 通过数据库id 编号查找对应的一条数据
	let id = req.query.id;  //get 接收
	db.query('select * from manager where id='+id,function(err,results){
		let dataone = results[0]; // results 二维对象。
		res.render('admin/manager/edit.html',{dataone:dataone});
	})
	
});
router.post('/doedit',function(req,res){
	let id = req.body.id;
	let username = req.body.username;
	let password = req.body.password;
	let mobile = req.body.mobile;
	// 通过 id 编号修改 对应语句
	//console.log('update manager set username="'+username+'",password="'+password+'",mobile="'+mobile+'" where id='+id)
	db.query('update manager set username="'+username+'",password="'+password+'",mobile="'+mobile+'" where id='+id,function(err,results){
		if(err){
			res.redirect('back');// 返回到上一页,edit编辑页面
		}else{
			res.redirect('/admin/manager');
		}
		
	})
})

// 4.删除
router.get('/del',function(req,res){
	let id = req.query.id;// get接收id
	db.query('delete from manager where id='+id,function(err,results){	
		res.redirect('/admin/manager');	
	})
})

module.exports = router;
// routes/api/index.js

let express = require('express');
let router = express.Router();

// localhost:3000/api/
router.get('/',function(req,res){
	res.send('我是提供前端的接口');
})

module.exports = router;

//  app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

// 路由分组
var adminRouter = require('./routes/admin/index');
var apiRouter = require('./routes/api/index');

var app = express();


// view engine setup
app.engine('.html',require('ejs').__express);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');

app.use(logger('dev'));
app.use(express.json());
// post 传递数据
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/admin', adminRouter);
app.use('/api', apiRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

4. 格式时间

//安装:npm install silly-datetime --save
var sd = require('silly-datetime');
sd.format(new Date(), 'YYYY-MM-DD HH:mm');

5. 加密

module.exports = {
   //使用md5加密密码,不可逆的加密
   md5:function(password){
       // 加密密码(原生模块-c++的模块),安装并导入
       var crypto = require('crypto');
       // 创建hash加密的方式
       var md5 = crypto.createHash('md5');
       // 加密
       md5.update(password);
       // 获取加密结果
       return md5.digest('hex');
   }
}

6. 路径注意的几个地方

app.js 配置app.use('/admin',..)   admin/index.js 配置 router.use('/manager',..)
(1) managerjs中    router.get('/add',fn) 
根据 配置的路由位置 找访问结果 http://localhost:3000/admin/manager/add  
(2)  res.render('admin/manager/add.html')  // 路径: views视图下的 对应的html页面
(3) <form action="/admin/manager/doadd" method="post"></form>
当前的路径 http://localhost:3000js
替换成  http://localhost:3000/admin/manager/doadd 找对应路由
就能找到  router.get('/doadd',fn) 路由匹配  
(4) res.redirect('/admin/manage')  重定向路由 : 管理员列表路由

7.上传步骤

1)模板: admin/manager/image.html
 
 form 标记上传编码:enctype="multipart/form-data"(重要)
 
 <form action="/admin/manage/doimage" method="post" enctype="multipart/form-data">
 <input type="file" name="pic">
 <button>上传</button>
 </form>
(2) 路由
 
 // 显示头像表单页面路由
 router.get('/image',function(req,res){
 res.render('admin/manage/image.html');
 })
 
 // 执行上传操作路由
 router.post('/doimage',function(req,res){
 //...
 })
3) express 上传模块 multer实现上传
 
 a. 安装 npm install multer --save
 
 b. 引入 multer 模块并且设置 上传的目录位置 (***var multer  = require('multer')
 var upload = multer({ dest: 'public/uploads/' })
 
 c. 接收  表单name属性名称是pic的上传数据 
 
 app.post('/doimage', upload.single('pic'), function (req, res, next) {
 // 获得 上传文件的数据  req.body req.query
 //console.log(req.file);
 
 // d. 修改上传文件的filename的新名称(***)
 var fs =require('fs');
 var path = require('path')   path.extname() 获得后缀名称
 fs.rename(oldname,newname,function(err){
 // e. sql语句  update 更新 image字段图像路径
 })
 })
 f. 在  admin/manage/index.html 模板 展示头像
 
 <img src="<%='/uploads/'+data[i].image%>">

8.Cookie 原理

1.Cookie 简介

cookie 是存储于访问者的计算机中的变量。可以让我们用同一个浏览器访问同一个域名的时候共享数据。

HTTP 是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页面,服务器无法认识到这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何关系的

2.使用
var cookieParser = require('cookie-parser');
设置: res.cookie('user', 'tom', { maxAge: 60 * 1000});
获得: console.log(req.cookies)

注意:
path:指定 cookie 影响到的路径 
expires: 指定时间格式
maxAge:指定 cookie 什么时候过期
secure:当 secure 值为 true 时,在 HTTPS 中才有效;反之,cookie 在 HTTP 中是有效。
httpOnly:浏览器不允许脚本操作 document.cookie 去更改 cookie。设置为true可以避免被 xss 攻击拿到 cookie
3.用在哪儿
  1. 保存用户信息
  2. 浏览器历史记录
  3. 猜你喜欢的功能
  4. 10天免登陆
  5. 多个页面之间的数据传递
  6. cookie实现购物车功能

9.Sessiong原理

1.Session简单介绍

session 是另一种记录客户状态的机制 ,不同的是 Cookie 保存在客户端浏览器中 , 而session 保存在服务器上

2.Session的工作流程

当浏览器访问服务器并发送第一次请求时,服务器端会创建一个 session 对象,生成一个类似于 key,value 的键值对, 然后将 key(sessionid存在cookie)返回到浏览器(客户)端,浏览器下次再访问时,携带 key(cookie),找到对应的 session(value)。 客户的信息都保存在 session 中

3.使用
//安装:  npm install express-session --save

var session = require('express-session');

// session 配置
app.use(session({
    name: 'session-name', // 这里是cookie的name,默认是connect.sid
    secret: 'my_session_secret', // 建议使用 128 个字符的随机字符串
    resave: true,
    saveUninitialized: false,
    cookie: { maxAge: 60 * 1000, httpOnly: true }
}));
/*
注意:
name: 设置cookie中,保存session的字段名称,默认为connect.sid
store: session的存储方式,默认为存放在内存中,我们可以自定义redis等
genid: 生成一个新的session_id时,默认为使用uid2这个npm包
rolling: 每个请求都重新设置一个cookie,默认为false
resave: 即使session没有被修改,也保存session值,默认为true
saveUninitialized:强制未初始化的session保存到数据库
secret: 通过设置的secret字符串,来计算hash值并放在cookie中,使产生的signedCookie防篡改
cookie : 设置存放sessionid的cookie的相关选项
*/

设置: req.session.username = 'jerry';
获得: console.log(req.session.username);
4.Cookie和Session区别

cookie 数据存放在客户的浏览器上,session 数据放在服务器上。

cookie 不是很安全,别人可以分析存放在本地的 COOKIE 并进行 COOKIE 欺骗考虑到安全应当使用 session。

session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用 COOKIE。

单个 cookie 保存的数据不能超过 4K,很多浏览器都限制一个站点最多保存 20 个 cookie。

小例子
//app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
// 支持cookie模块
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

// 安装并且导入 express-session 模块
let session = require('express-session');


var app = express();

// 配置 session
app.use(session({
	name:'session-name',
	secret:'my_session_secret',
	resave:true,
	saveUninitialized:false,
	cookie:{maxAage:24*60*60*1000,httponly:true}
}))


// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;
 //  routes/index.js

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {

    // 获得 cookie值
	/* if(req.cookies.user){
		res.send('显示cookie中用户名:'+req.cookies.user)
	}else{
		res.send('还没有用户信息,请去登录')
	} */
	
	// 获得 session值
	if(req.session.username){
		res.send('显示session中用户名:'+req.session.username)
	}else{
		res.send('还没有用户信息,请去登录')
	} 
});

module.exports = router;

//   routes/user.js

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  //服务器端:  设置cookie
  // res.cookie('user','tom',{maxAage:24*60*60*1000});
  // res.send('用cookie记录用户信息');
  
  // 服务器:设置session
  req.session.username ='jerry';
  res.send('用session记录用户信息');
});

module.exports = router;

/*
  express -e  mycookie
  cd mycookie
  npm install
  npm start

*/

四、Mongodb数据库

1.数据库种类

关系型数据库 Sql (mysql,sqlserver等),表(行,列)

非关系型数据库NoSql (mongodb, radis) ,对象(键值对)

2.NoSql介绍

1.NoSQL介绍

由于互联网的迅速发展,云计算与 Web2.0。这样大量的交互给数据库提出了更高的性能要求,传统的数据库(本文泛指 SQL 数据库),即关系数据库虽然具备良好的事物管理,但在处理 大量数据的应用时很难在性能上满足设计要求.NoSQL 就是主要为了解决当下大量高并发高要求的数据库应用需求,关系数据库具有严格的参照性,一致性,可用性,原子性,隔离性等特点,因此会产生一些例如表连接等操作,这样会大大降低系统的性能。而在当前很多应用场景下对性能的要求远远强于传统数据库关注的点,NoSQL 就是为了解决大规模数据与多样数据种类等问题,尤其是其中大数据的相关问题

2.NoSQL应用情况介绍

国内的互联网蓬勃发展,不仅涌现出 BAT(百度,阿里巴巴,腾讯)之类的巨头,也带动了整个互联 网行业的发展,大量的创业型公司如春笋般的涌出,在国家层面也提出了“互联网+”和“万众创业”的口 号。更多传统的行业也开始拥抱互联网。但是无论是做所谓的生态平台还是传统业务的转型,涉及到的业务是多种多样的。这个时候企业架构师对于应用系统的核心——数据库管理不仅有传统的 SQL 选项也有了NoSQL 这种适合特定场景需求的选项

NoSQL 数据库在以下的这几种情况下比较适用:

  1. 数据模型比较简单;
  2. 需要灵活性更强的 IT 系统;
  3. 对数据库性能要求较高;
  4. 不需要高度的数据一致性;
  5. 对于给定 key,比较容易映射复杂值的环境。
  6. 易扩展
3.NoSQL发展现状

国外: Google 的 BigTable 和 Amazon 的 Dynamo 使用的就是 NoSQL 型数据库。

国内:百度、阿里、腾讯、新浪微博、视觉中国、优酷运营数据分析、飞信空间、豆瓣社区等…

4.NoSql和传统数据库简单对比

非结构型数据库。没有行、列的概念。用 JSON 来存储数据。集合就相当于“表”,文档就相当于“行”。

5.MongoDB介绍

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的他支持的数据结构非常松散,是类似 json 的 bson 格式,因此可以存储比较复杂的数据类 型。Mongo 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它的特点是高性能、易部署、 易使用,存储数据非常方便。

MongoDB安装

官网:https://www.mongodb.com/download-center/community

手册:https://docs.mongodb.org/manual/

MongoDB基础语法:cmd 输入 mongo

1)MongoDB数据库创建删除、表(集合)创建删除

(1)查看所有数据库列表 show dbs  
(2)使用数据库、创建数据库 use myshop
(3)创建集合():db.manager.insert({“name”:”xiaoming”});
(4)显示当前的数据集合 show collections 
(5)删除数据库 db.dropDatabase();6)删除集合 db. manager.drop()

注意: 如果真的想把这个数据库创建成功,那么必须插入一个数据。 数据库中不能直接插入数据,只能往集合(collections)中插入数据。不需要专门创建集合,只 需要写点语法插入数据就会创建集合;

2)数据增删改查

(1) 插入数据:db.表名.insert({"name":"zhangsan"});
(2) 查找数据:db.manager.find(); 
(3) 更新: 查找名字叫做小明的,把年龄更改为 16 岁:
 db. manager.update({"name":"小明"},{$set:{"age":16}}); 
(4)删除:db.manager.remove({id: 8});

1、查询所有记录 

db.manager.find(); 

相当于:select* from manager; 

2、查询去掉后的当前聚集集合中的某列的重复数据

db.manager.distinct("username"); 

会过滤掉 name 中的相同数据 
相当于:select distinct name from manager; 

3、查询 age = 22 的记录 

db.manager.find({"age": 22}); 
相当于: select * from manager where age = 22; 

4、查询 age > 22 的记录 

db.manager.find({age: {$gt: 22}}); 
相当于:select * from manager where age >22; 

5、查询 age < 22 的记录 

db.manager.find({age: {$lt: 22}}); 

相当于:select * from manager where age <22; 
6、查询 age >= 25的记录 
db.manager.find({age: {$gte: 25}}); 
相当于:select * from manager where age >= 25; 

7、查询 age <= 25 的记录 
db.manager.find({age: {$lte: 25}}); 

8、查询 age >= 23 并且 age <= 26      注意书写格式 
db.manager.find({age: {$gte: 23, $lte: 26}}); 

9、查询 name 中包含 mongo 的数据       模糊查询用于搜索 
db.manager.find({name: /mongo/}); 
/相当于%% 
select * from manager where name like ‘%mongo%;

10、查询 name 中以 mongo 开头的 
db.manager.find({name: /^mongo/}); 
select * from manager where name like ‘mongo%;

11、查询指定列 name、age 数据       
db.manager.find({}, {name: 1, age: 1}); 
相当于:select name, age from manager; 
当然 name 也可以用 truefalse,当用 ture 的情况下河 name:1 效果一样,如果用 false 就 是排除 name,显示 name 以外的列信息。

12、查询指定列 name、age 数据, age > 25 
db.manager.find({age: {$gt: 25}}, {name: 1, age: 1}); 
相当于:select name, age from manager where age >25; 

13、按照年龄排序    1 升序    -1 降序 
升序:db.manager.find().sort({age: 1}); 
降序:db.manager.find().sort({age: -1}); 

14、查询 name = zhangsan, age = 22 的数据 
db.manager.find({name: 'zhangsan', age: 22}); 
相当于:select * from manager where name = ‘zhangsan’ and age =22; 

15、查询前 5 条数据 
db.manager.find().limit(5); 	
相当于:select top 5 * from manager;

16、查询 10 条以后的数据  (实现分页)

db.manager.find().skip(10); 

注意: 实现分页:    db.manger.find().skip(2).limit(2)  第二页
db.manger.find().skip(4).limit(2) 第三页


17、查询在 5-10 之间的数据 

db.manager.find().limit(5).skip(5); 

可用于分页,limit 是 pageSize,skip 是第几页*pageSize 

18、or 与 查询 

db.manager.find({$or: [{age: 22}, {age: 25}]}); (容易错)

相当于:select * from manager where age = 22 or age = 25; 

19、findOne 查询第一条数据 

db.manager.findOne(); 

相当于:select top 1 * from manager; 

db.manager.find().limit(1); 


20、查询某个结果集的记录条数   统计数量 

db.manager.find({age: {$gte: 25}}).count(); 

相当于:select count(*) from manager where age >= 20; 
如果要返回限制之后的记录数量,要使用 count(true)或者 count(0) db.users.find().skip(10).limit(5).count(true);

更新的例子:
查找数学成绩是 70,把年龄更改为 33 岁: 
db.student.update({"score.shuxue":70},{$set:{"age":33}});

更改所有匹配项目:db.student.update({"sex":"男"},{$set:{"age":33}},{multi: true});

完整替换,不出现$set 关键字了:  注意 db.student.update({"name":"小明"},{"name":"大明","age":16});

db.users.update({name: 'Lisi'}, {$inc: {age: 50}}, false, true); 
相当于:update users set age = age + 50 where name = ‘Lisi’; 
注意: 第一 false 不允许插入,第二true 允许多条的更新。

db.users.update({name: 'Lisi'}, {$inc: {age: 50}, $set: {name: 'hoho'}}, false, true); 
相当于:update users set age = age + 50, name = ‘hoho’ where name = ‘Lisi’;

db.manager.save():
注意: 已有的 _id 是修改,不存在的_id 是添加

db.manager.save({"_id" : ObjectId("5ea64f4366cccb86b1dd8265"),username:"hello",password:"1234",age:18});

********
$sum:求和
db.user_name.aggregate([{$group:{_id:"$字段",统计结果保存键值(自定):{$sum:1}}}]):

$sum 统计男生女生各有多少人
db.user.aggregate([{$group:{_id:"$sex",sum:{$sum:1}}}]):
$sum: 1  相当于单位。

$avg:计算平均值
db.user.aggregate([{$group:{_id:"$sex",avg:{$avg:"$age"}}}])

$min:获取集合中所有文档对应值的最小值
db.user.aggregate([{$group:{_id:"$sex",min:{$min:"$age"}}}])

$max:获取集合中所有文档对应值的最大值
db.user.aggregate([{$group:{_id:"$sex",max:{$max:"$age"}}}])

3.MongoDB索引和explain的使用

1.索引

索引是对数据库表中一列或多列的值进行排序的一种结构,可以

让我们查询数据库变得更快。MongoDB 的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的查询优化技巧

2.索引的命令
(1) 创建:db.user.ensureIndex({"username":1})

(2) 获得:db.user.getIndexes()

(3)删除索引: db.user.dropIndex({"username":1})

(4)复合索引:db.user.ensureIndex({"username":1, "age":-1})

(5)唯一索引:db.user.ensureIndex({"userid":1},{"unique":true})

注意: 数字1表示 username 键的索引按升序存储,-1 表示 age 键的索引按照降序方式存储。缺省情况下创建的索引均不是唯一索引

3.使用explain
1)explain 会返回查询使用的索引情况,耗时和扫描文档数的统计信息:

db.user.find({"username":"tom"}).explain()2)explain executionStats  查询具体的执行时间

db.user.find().explain("executionStats")

4.NodeJS操作MongoDB

1.安装: npm install mongodb –save
2.NodeJs连接 MongoDB
3.详细步骤
// 1. 引入 mongodb 模块
var mongo = require('mongodb');

 / 2.mongodb 生成客户端对象
var MongoClient = mongo.MongoClient;

// 3 连接Mongodb的数据库的地址 url
var DBURL = 'mongodb://127.0.0.1:27017';

//数据库名字:myshop
var dbname ='myshop'

/* 显示列表 */
router.get('/', function(req, res, next) {
  MongoClient.connect(DBURL,function(err,client){
  	 if(err){
  	 	 console.log('连接mongodb 失败')
  	 }else{
  	 	var db = client.db(dbname);
  	 	db.collection('manager').find().toArray(function(err,data){
  	 		//console.log(data)
  	 		res.render('admin/manager/list',{data:data});
  	 		
  	 	});
  	 	client.close();
  	 }
  })
  
});


// 添加的显示界面
router.get('/add',function(req,res,next){
	res.render('admin/manager/add.html');
})
// 添加执行
router.post('/doadd',function(req,res,next){
	MongoClient.connect(DBURL,function(err,client){
		if(err){
			console.log('数据库连接失败');
		}else{
			var db = client.db(dbname);
			// db.manager.insert({username:'tom',password:'12345'})
			objUser = req.body;
			db.collection('manager').insert(objUser,function(err){
				if(err){
					 console.log('添加管理员manager数据库失败');
				}else{
					res.redirect('/admin/manager');
				}
			});
		    client.close();
		}
	})
})

// 修改显示界面
router.get('/edit',function(req,res,next){
	// 通过 _id 找对应的旧数据,渲染到 修改界面模板上,在旧数据上修改内容
	var _id = req.query._id;
	MongoClient.connect(DBURL,function(err,client){
		if(err){
			console.log('连接数据库失败');
		}else{
			var db = client.db(dbname);
			db.collection('manager').find({_id:mongo.ObjectId(_id)}).toArray(function(err,dataone){
				//console.log(dataone);
				res.render('admin/manager/edit.html',{dataone:dataone[0]});
				
			})
			client.close();
		}
	})
	
})
// 修改执行
router.post('/doedit',function(req,res,next){
	//console.log(req.body);
	MongoClient.connect(DBURL,function(err,client){
		if(err){
			console.log('连接数据库失败');
		}else{
			var db = client.db(dbname);
			// db.manager.save() 如果有id 修改文档
			req.body._id = mongo.ObjectId(req.body._id);
			//console.log(req.body);
			db.collection('manager').save(req.body,function(err){
				if(err){
					console.log('修改数据库失败')
				}else{
					res.redirect('/admin/manager');
				}
			})
			client.close();
		}
	})
})

// 删除
router.get('/del',function(req,res,next){
	var _id = req.query._id;
	MongoClient.connect(DBURL,function(err,client){
		if(err){
			console.log('连接数据库失败');
		}else{
			var db = client.db(dbname);
			db.collection('manager').remove({_id:mongo.ObjectId(_id)},function(err){
				if(err){
					console.log('删除成功')
				}else{
					res.redirect('/admin/manager');
				}
			})
			client.close();
		}
	})
})

5.小例子:express 操作mongodb实现manager表的增、删、改、分页效果

// app.js

// 1、报错模块
var createError = require('http-errors');
// 2、express模块
var express = require('express');
// 3.路径模块
var path = require('path');
// 4.本地储存,cookie模块
var cookieParser = require('cookie-parser');
// 5.日志模块
var logger = require('morgan');

// 9、路由分组管理
var adminRouter = require('./routes/admin/index');
var apiRouter = require('./routes/api/index');

// 6.实例express模块
var app = express();

//7、ejs模板引擎,默认ejs的后缀修改为html
app.engine('.html', require('ejs').__express);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');

// 8、第三方中间件
app.use(logger('dev'));
// 支持json
app.use(express.json());
//body-parser 模块的配置;支持post数据传递
app.use(express.urlencoded({
  extended: false
}));
//cookie的应用
app.use(cookieParser());
//静态资源加载(内置中间件)
//制定程序的/public库文件目录:包里面的文件就会被映射,因为 这个__dirname 已经是获取当前模块文件所在目录的完整绝对路径
app.use('/public', express.static(path.join(__dirname, 'public')));

// 应用路由分组
app.use('/admin', adminRouter);
app.use('/api', apiRouter);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;
// routes/admin/index.js

var express = require('express');
var router = express.Router();

var managerRouter = require('./manager.js');
var goodsRouter = require('./goods.js');

router.get('/', function(req, res, next) {
  res.send("我是admin首页");
});

// 应用路由分组
router.use('/manager', managerRouter);
router.use('/goods', goodsRouter);

module.exports = router;


// routes/admin/goods.js

var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
  res.send("我是goods首页");
});

router.get('/add', function(req, res, next) {
  res.send("我是goods添加页");
});

module.exports = router;
// routes/admin/manager.js

var express = require('express');
var router = express.Router();

 // 导入格式化时间的第三方模块
let sd = require('silly-datetime');

// 1.安装并且导入  mongodb
let mongo = require('mongodb');
// 2.获得 MongoClient 对象
let MongoClient = mongo.MongoClient;
// 3.设置域名
let  dburl = 'mongodb://127.0.0.1:27017';
// 4.设置数据库
let  dbname = 'myshop';

//  localhost:3000/admin/manager/
router.get('/', function(req, res, next) {
  // 5连接mongodb数据库 
  MongoClient.connect(dburl,async function(err,client){
	  if(err){
		  console.log('连接mongodb失败');
	  }else{
		  // 6 获得db对象
		  var db = client.db(dbname);
		  // 分页: mongo:  limit 偏移量索引,每页显示的条数
		  let page ={};
		  // 数据的总条数
		  page.total = await db.collection('manager').find({}).count();
		  //每页显示的条数
		  page.length = 3;
		  //总页数
		  page.pages =Math.ceil(page.total/page.length);
		  //动态当前页
		  page.now = req.query.p?Number(req.query.p):1;
		  // 上一页  p-1  ,判断当 小于第一页那最小值就是第一页
		  page.prev = page.now -1 < 1? 1:page.now-1;
		  //下一页  p+1,判断当 大于最后一页那最大值就是最后一页
		  page.next = page.now +1 >page.pages?page.pages:page.now+1;
		  // // 3.偏移量索引 = (当前页 -1)* 每页显示的条数
		  let offset = (page.now -1) * page.length;
		  // 7. 查询语句
		  db.collection('manager').find().skip(offset).limit(page.length).toArray(function(err,data){
		  	res.render('admin/manager/list.html',{data:data,page:page,sd:sd});
		  });
		  client.close();
	  }
  })
  
});

// // 添加管理员
// //  localhost:3000/admin/manager/add
router.get('/add', function(req, res, next) {
  res.render('admin/manager/add.html');
});
router.post('/doadd', function(req, res, next) {
    MongoClient.connect(dburl,function(err,client){
		if(err){
			console.log('连接数据库失败');
			req.body.state = false;
		}else{
			var db = client.db(dbname);
			//状态:true
			req.body.state = true;
			// 格式时间
			req.body.addtime = sd.format(new Date(), 'YYYY-MM-DD HH:mm')
			// 添加
			objManager = req.body;
			//console.log(objManager);
			db.collection('manager').insert(objManager,function(err){
				if(err){
					console.log('添加管理员失败');
				}else{
					res.redirect('/admin/manager');// 跳转到列表页。
				}
			})
			 client.close();
		}
	})
});

// 修改
router.get('/edit',function(req,res){
	// 获取当前修改数据的_id
	var _id = req.query._id;
	// 通过 _id查询旧的一条数据
	MongoClient.connect(dburl,function(err,client){
		if(err){
			console.log('数据库连接失败');
		}else{
			var db = client.db(dbname);
			db.collection('manager').find({_id:mongo.ObjectId(_id)}).toArray(function(err,data){
				res.render('admin/manager/edit.html',{dataone:data[0]});
			})
			client.close();
		}
	})
})
router.post("/doedit",function(req,res){
	MongoClient.connect(dburl,function(err,client){
		if(err){
			console.log('数据库连接失败')
		}else{
			var db = client.db(dbname);
			var objManager = req.body;
			//修改 _id 为ObjectId(_id)
			objManager._id = mongo.ObjectId(req.body._id);
			//修改语句
			db.collection('manager').save(objManager,function(err){
				if(err){
					console.log('修改数据库失败')
				}else{
					res.redirect('/admin/manager')
				}
			})
			client.close();
		}
	})
})

// 删除
router.get('/del',function(req,res){
	// 获取当前删除数据的_id
	var _id = req.query._id;
	MongoClient.connect(dburl,function(err,client){
		if(err){
			console.log('数据库连接失败');
		}else{
			var db = client.db(dbname);
			db.collection('manager').remove({_id:mongo.ObjectId(_id)},function(err){
				if(err){
					console.log('删除失败');
				}else{
					res.redirect('/admin/manager');
				}
			})
			client.close();
		}
	})
})
module.exports = router;
//  views/admin/manager/list.html

<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport"
		content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
	<link rel="stylesheet" type="text/css" href="/public/stylesheets/bootstrap.min.css" />
	<script src="/public/javascripts/jquery.min.js"></script>
	<script src="/public/javascripts/bootstrap.min.js"></script>
	<title>list</title>
</head>

<body>
	<div class="table-responsive">
		<table class="table table-bordered">
			<caption style="text-align: center;font-weight: bold;">manager详情页</caption>
			<thead>
				<tr>
					<th>名称</th>
					<th>手机</th>
					<th>状态</th>
					<th>创建时间</th>
					<th>操作</th>
				</tr>
			</thead>
			<tbody>
				<%for(var i = 0; i<data.length; i++){%>
				<tr>
					<td><%= data[i].username%></td>
					<td><%= data[i].mobile%></td>
					<td>
						<%if(data[i].state){%>
						<span class="glyphicon glyphicon-ok" style="color:green"></span>
						<%}else{%>
						<span class="glyphicon glyphicon-ok" style="color:darkred"></span>
						<%}%>
					</td>
					<td><%= data[i].addtime%></td>
					<td>
						<a href="/admin/manager/edit?_id=<%=data[i]._id%>">修改</a>
						<a href="/admin/manager/del?_id=<%=data[i]._id%>">删除</a>
					</td>
				</tr>
				<%}%>
				<tr>
					<td colspan="1">
						<a href="/admin/manager/add">添加管理员</a>	
					</td>
					<td colspan="4" align="center">
						<a href="/admin/manager?p=1" style="margin-left:20px;">首页</a>
						<a href="/admin/manager?p=<%=page.prev%>" style="margin-left:20px;">上一页</a>
						<!--  需要总页数? -->
						<%for(var i = 1; i<= page.pages;i++){%>
						   <%if(page.now == i){%>
								<a href="/admin/manager?p=<%=i%>" style="color:red;margin-left:20px;"><%=i %></a>
						   <%}else{%>
								<a href="/admin/manager?p=<%=i%>" style="margin-left:20px;"><%=i %></a>
						   <%}%>
						<%}%>
						<a href="/admin/manager?p=<%=page.next%>" style="margin-left:20px;">下一页</a>
						<a href="/admin/manager?p=<%=page.pages%>" style="margin-left:20px;">尾页</a>
					</td>
				</tr>
			</tbody>
		</table>
	</div>
</body>

</html>
//  views/admin/manager/add.html

<!DOCTYPE html>
<html lang="zh">
<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">
	<link rel="stylesheet" type="text/css" href="/public/stylesheets/bootstrap.min.css" />
	<script src="/public/javascripts/jquery.min.js"></script>
	<script src="/public/javascripts/bootstrap.min.js"></script>
	<title>add</title>
</head>
<body>
	<div class="table-responsive">
		  <table class="table table-bordered">
		    <caption style="text-align: center;font-weight: bold;">manager添加页</caption>
		      <tr>
		        <td style="overflow: hidden;">
					<form class="form-horizontal" role="form" action="/admin/manager/doadd" method="post">
						<div class="form-group">
							<label class="col-sm-1 col-sm-offset-1 control-label">名称:</label>
							<div class="col-sm-8">
								<input type="text" name="username"class="form-control">
							</div>
						</div>
						<div class="form-group">
							<label class="col-sm-1 col-sm-offset-1 control-label">手机:</label>
							<div class="col-sm-8">
								<input type="text" name="mobile" class="form-control">
							</div>
						</div>
						<div class="form-group">
							<button type="submit" name="submit" style="margin:0px auto;display: block;width:120px;height:40px;line-height: 40px;text-align: center;background-color: #aaa;border:1px solid #ddd;">添加</button>
						</div>
					</form>
				</td>
			 </tr>
		  </table>
		</div>
</body>
</html>
// views/admin/manager/edit.html

<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport"
		content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
	<link rel="stylesheet" type="text/css" href="/public/stylesheets/bootstrap.min.css" />
	<script src="/public/javascripts/jquery.min.js"></script>
	<script src="/public/javascripts/bootstrap.min.js"></script>
	<title>edit</title>
</head>

<body>
	<div class="table-responsive">
	  <table class="table table-bordered">
	    <caption style="text-align: center;font-weight: bold;">manager修改页</caption>
	      <tr>
	        <td style="overflow: hidden;">
				<form class="form-horizontal" role="form" action="/admin/manager/doedit" method="post">
					<div class="form-group">
						<label class="col-sm-1 col-sm-offset-1 control-label">名称:</label>
						<div class="col-sm-8">
							<input type="text" name="username" value="<%=dataone.username%>" class="form-control">
						</div>
					</div>
					<div class="form-group">
						<label class="col-sm-1 col-sm-offset-1 control-label">手机:</label>
						<div class="col-sm-8">
							<input type="text" name="mobile" value="<%=dataone.mobile%>" class="form-control">
						</div>
					</div>
					<!-- 隐藏域  传递 id编号;state状态;时间不修改 -->
					<input type="hidden" name="state" value="<%=dataone.state%>">
					<input type="hidden" name="addtime" value="<%=dataone.addtime%>">
					<input type="hidden" name="_id" value="<%=dataone._id%>">
					<div class="form-group">
						<button type="submit" name="submit" style="margin:0px auto;display: block;width:120px;height:40px;line-height: 40px;text-align: center;background-color: #aaa;border:1px solid #ddd;">修改</button>
					</div>
				</form>
			</td>
		 </tr>
	  </table>
	</div>
</body>

</html>

五、Koa框架

1.koa介绍

Node.js 是一个异步的世界,官方 API 支持的都是 callback 形式的异步编程模型,这会带来许多问题,例如:1、callback 嵌套问题 2、异步函数中可能同步调用 callback 返回数据,带来不一致性。为了解决以上问题 Koa 出现了。

koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的Web 框架。 使用 koa 编写 web 应用,可以免除重复繁琐的回调函数嵌套, 并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。开发思路和 express 差不多,最大的特点就是可以避免异步嵌套

阿里是业界最早的一批使用 Node.js 来做线上大流量应用的公司

2.使用情况

早在 2011 年的就已经开始在生产环境中使用。众所周知,在阿里的技术栈中, Java 是最最核心的,那 Node.js 扮演怎么样的一个角色呢?

1、基础设施大部分采用 Java 实现,变化较少,有事务要求的 Business Services 通常使用Java。

2、而 Node.js 则替代过去 PHP/Java Web 的场景,用在需要快速迭代,需求变化非常快的用户侧。

3、很多内部的工程化支撑系统也逐渐基于 Node.js 了。据不完全统计,目前阿里 Node.js 的开发者几百号人,线上的应用也非常之多,仅次于 Java 应用,光对外服务的进程数就超过 1w+。阿里内部就在使用 Koa 框架,并在 Koa 基础上面做了一些扩展和封装。并且基于 koa 开发了一个开源框架 egg。

3.Koa2.x框架的安装使用

1.安装Node.js 8.x以上的版本

开发 Koa2 之前,Node.js 是有要求的,它要求 Node.js 版本高于 V7.6。因为 node.js 7.6 版本开始完全支持 async/await,所以才能完全支持我们的 Koa2。

2.npm install --save koa
//引入 Koa
const koa=require('koa');
const app=new koa();
//配置中间件 (可以先当做路由)
app.use( async (ctx)=>{
	ctx.body='hello koa2'
})
//监听端口
app.listen(3000);
3.Koa异步处理 Async…Await和Promise使用(重点)

async 是“异步”的简写,而 await 可以认为是 async wait 的简写。所以应该很好理解 async用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。

async 将普通方法转为 异步并且返回 promise对象

await 将异步代码转为同步结果,等着异步代码执行完才执行后面的代码

由回调函数、Promise到async/await的同步写法执行异步代码

4.Koa路由、get传值、动态路由、post传值

1.安装路由 :npm install --save koa-router
const Koa = require('koa'); 
const router = require('koa-router')(); //注意:引入的方式 
const app = new Koa(); 
router.get('/', function (ctx, next) { 
	ctx.body="Hello koa";
})
router.get('/news',(ctx,next)=>{ 
    ctx.body="新闻 page"
});

//作用:启动路由 
app.use(router.routes()); 

// 作用: 这是官方文档的推荐用法,我们可以 看到 router.allowedMethods()用在了路由匹配 router.routes()之后,所以在当所有 路由中间件最后调用.此时根据 ctx.status 设置 response 响应头
app.use(router.allowedMethods()); 

app.listen(3000,()=>{ 
	console.log('starting at port 3000');
});
2.Koa路由get传值

在 koa2 中 GET 传值通过 request 接收,但是接收的方法有两种:query 和 querystring。

query:返回的是格式化好的参数对象。

querystring:返回的是请求字符串

const Koa = require('koa'); 
const Router = require('koa-router');
const app = new Koa(); 
const router = new Router(); router.get('/', function (ctx, next) { 
	ctx.body="Hello koa";
}) 
router.get('/newscontent,(ctx,next)=>{
	let url =ctx.url;
	//从 request 中获取 GET 请求 
	let request =ctx.request; 
	let req_query = request.query; 
	let req_querystring = request.querystring; 
	
	//从上下文中直接获取,效果值和上面一样
	let ctx_query = ctx.query; 
	let ctx_querystring = ctx.querystring; 
	ctx.body={ 
		url, 
		req_query, 
		req_querystring, 
		ctx_query, 
		ctx_querystring
	}
}); 
app.use(router.routes()); 
//作用:启动路由 

app.use(router.allowedMethods()); 
//作用: 当请求出错时的处理逻辑 

app.listen(3000,()=>{ 
	console.log('starting at port 3000');
});
3.动态路由
//请求方式 http://域名/product/123 
router.get('/product/:aid',async (ctx)=>{ 
	console.log(ctx.params); //{ aid: '123' } //获取动态路由的数据 ctx.body='这是商品页面';
});
4.koa post提交数据 koa-bodyparser 中间件的使用

1.安装 npm install --save koa-bodyparser

2.安装引入配置中间件

var Koa = require('koa'); 
var bodyParser = require('koa-bodyparser'); 
var app = new Koa(); 
app.use(bodyParser());
app.use(async ctx => { 
	ctx.body = ctx.request.body;
});

5.中间件

应用级中间件
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
app.use(async (ctx,next)=>{
	console.log(new Date());
	await next();
})
router.get('/', function (ctx, next) {
	ctx.body="Hello koa";
})
router.get('/news',(ctx,next)=>{
	ctx.body="新闻页面"
});
app.use(router.routes()); //作用:启动路由
app.use(router.allowedMethods()); //作用: 当请求出错时的处理逻辑
app.listen(3000,()=>{
	console.log('starting at port 3000');
});
路由中间件
router.get('/', async(ctx, next)=>{
console.log(1)
	await next()
})
router.get('/', function (ctx) {
	ctx.body="Hello koa";
})
错误处理中间件
app.use(async (ctx,next)=> {
   await next();
   if(ctx.status==404){
       ctx.status = 404;
       ctx.body="这是一个 404 页面"
   }
});
第三方中间件
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());

6.Koa中间件执行过程

Koa 的中间件和 Express 不同,Koa 选择了洋葱圈模型

 //引入 koa模块
var Koa=require('koa');
var router = require('koa-router')();  /*引入是实例化路由** 推荐*/
var app=new Koa();

//Koa中间件

//匹配任何路由  ,如果不写next,这个路由被匹配到了就不会继续向下匹配
app.use(async (ctx,next)=>{
    console.log('1、这是第一个中间件01');
    await next();
    console.log('5、匹配路由完成以后又会返回来执行中间件');
})
app.use(async (ctx,next)=>{
    console.log('2、这是第二个中间件02');
    await next();
    console.log('4、匹配路由完成以后又会返回来执行中间件');
})
router.get('/',async (ctx)=>{
	ctx.body="首页";
})
router.get('/news',async (ctx)=>{

    console.log('3、匹配到了news这个路由');
    ctx.body='这是一个新闻';
})
app.use(router.routes());   /*启动路由*/
app.use(router.allowedMethods());
app.listen(3002);

7.koa ejs模板引擎

安装 koa-views 和 ejs

npm install --save koa-views

npm install ejs –save

8.koa koa-static 静态资源中间件

1)安装
npm install --save koa-static

(2)引入配置中间件 
const static = require('koa-static'); app.use(static( 
	path.join( __dirname, 'public')
))

9.koa art-template 模板引擎

(1) 安装
npm install --save art-template
npm install --save koa-art-template
(2)art-template 引入

const Koa = require('koa');
const render = require('koa-art-template');
const app = new Koa();
render(app, {
    root: path.join(__dirname, 'view'),
    extname: '.html',
    debug: process.env.NODE_ENV !== 'production'
});
app.use(async function (ctx) {
	await ctx.render('user');
});
app.listen(8080);

10.cookie

// 设置cookie
ctx.cookies.set('userinfo','zhangsan2222',{
    maxAge:60*1000*60,
    
    // path:'/news',  /*配置可以访问的页面*/
    //domain:'.baidu.com'  /*正常情况不要设置 默认就是当前域下面的所有页面都可以方法*/

    httpOnly:false,  //true表示这个cookie只有服务器端可以访问,false表示客户端(js),服务器端都可以访问
    /*
	 a.baidu.com
     b.baidu.com  共享cookie的数据
	 express基础教程
    * */

});

// 获得cookie

var userinfo=ctx.cookies.get('userinfo');

>> 设置中文 
>> 设置前转var userinfo=new Buffer('张三').toString('base64');
>> 获得后转 var userinfo=new Buffer(data, 'base64').toString();

11.session

1.npm install koa-session  --save
2const session = require('koa-session');
3、
app.keys = ['some secret hurr'];   /*cookie的签名*/
const CONFIG = {
    key: 'koa:sess', /** 默认 */
    maxAge: 10000,  /*  cookie的过期时间        【需要修改】  */
    overwrite: true, /*(boolean) can overwrite or not (default true)  没有效果,默认 */
    httpOnly: true, /**  true表示只有服务器端可以获取cookie */
    signed: true, /** 默认 签名 */
    rolling: true, /** 在每次请求时强行设置 cookie,这将重置 cookie 过期时间
    (默认:false) 【需要修改】 */
    renew: false, /*(boolean) renew session when session is nearly expired【需要修改】*/
};
app.use(session(CONFIG, app));

设置 session
ctx.session.username = "张三"

获取 session
ctx.session.username
小例子
//app.js

//koa模块
const Koa = require('koa')
//app实例化koa对象
const app = new Koa()
// 模板引擎模块
const views = require('koa-views')
//json格式
const json = require('koa-json')
// error模块
const onerror = require('koa-onerror')
// 数据传输模块
const bodyparser = require('koa-bodyparser')
// 日志模块
const logger = require('koa-logger')

// 导入 koa-session 模块
const session = require('koa-session');
app.keys = ['some secret hurr'];   /*cookie的签名*/
const CONFIG = {
		 key: 'koa:sess', /** 默认 */
		 maxAge: 1000*60,  /*  cookie的过期时间        【需要修改】  */
		 overwrite: true, /*(boolean) can overwrite or not (default true)  没有效果,默认 */
		 httpOnly: true, /**  true表示只有服务器端可以获取cookie */
		 signed: true, /** 默认 签名 */
		 rolling: true, /** 在每次请求时强行设置 cookie,这将重置 cookie 过期时间
(默认:false) 【需要修改】 */
		renew: false, /*(boolean) renew session when session is nearly expired【需要修改】*/
};
app.use(session(CONFIG, app));

// 路由分组
const index = require('./routes/index')
const users = require('./routes/users')

//1. 应用级中间件
app.use( async function(ctx,next){
	let time = new Date();
	console.log('时间:' + time);
	await next();
})

// error handler
onerror(app)

// 第三方模块:middlewares
app.use(bodyparser({
  enableTypes:['json', 'form', 'text']
}))
app.use(json())
app.use(logger())

// 加载静态资源
app.use(require('koa-static')(__dirname + '/public'))

// ejs模板引擎使用
app.use(views(__dirname + '/views', {
  extension: 'ejs'
}))

// logger
app.use(async (ctx, next) => {
  const start = new Date()
  await next()
  const ms = new Date() - start
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})

// routes
app.use(index.routes(), index.allowedMethods())
app.use(users.routes(), users.allowedMethods())

// error-handling
app.on('error', (err, ctx) => {
  console.error('server error', err, ctx)
});

module.exports = app

// powershell:npm start
//浏览器:http://localhost:3000/
//浏览器:http://localhost:3000/users
// routes/index.js

const router = require('koa-router')()

//2. 路由中间件
router.get('/', async function(ctx,next){
	console.log('/的路由中间件');
	await next();
})
router.get('/', async function(ctx,next){
	console.log('/的路由中间件2');
	await next();
})


router.get('/', async (ctx, next) => {
  // await ctx.render('index', {
  //   title: 'Hello Koa 2!'
  // })
  // 获得cookie
  //let username = ctx.cookies.get("username");
  // 获得 session
  let username = ctx.session.user;
  if(username){
	  ctx.body ='用户名是:'+username;
  }else{
	  ctx.body ='还没有用户信息';
  }
})

router.get('/string', async (ctx, next) => {
  ctx.body = 'koa2 string'
})

router.get('/json', async (ctx, next) => {
  ctx.body = {
    title: 'koa2 json'
  }
})

module.exports = router

// routes/users.js

const router = require('koa-router')()

router.prefix('/users')

router.get('/', function (ctx, next) {
  // 设置cookie
  //ctx.cookies.set("username","zhangsan",{maxAage:60*60*24*1000});
  //ctx.body = 'cookie设置username成功';
  // 设置 session
  ctx.session.user = '李四';
  ctx.body = 'session设置user成功';
})

router.get('/bar', function (ctx, next) {
  ctx.body = 'this is a users/bar response'
})

module.exports = router

12.koa脚手架创建项目

1、全局安装

npm install koa-generator -g

2、创建项目
koa2 -e mydemo

3.进入当前目录
cd mydemo

4、安装依赖包
npm install

5、启动项目
npm start

13.小例子

//app.js

// koa 模块
const Koa = require('koa')
// app 实例化koa的对象
const app = new Koa()
// 模板引擎模块
const views = require('koa-views')
const json = require('koa-json')
// error 模块
const onerror = require('koa-onerror')
// post 数据传输模块
const bodyparser = require('koa-bodyparser')
// 日志模块
const logger = require('koa-logger')


// 路由分组
const index = require('./routes/index')
const users = require('./routes/users')

// error handler
onerror(app)

// 使用第三方模块middlewares
app.use(bodyparser({
  enableTypes:['json', 'form', 'text']
}))
app.use(json())
app.use(logger())
// 加载静态资源
app.use(require('koa-static')(__dirname + '/public'))
// ejs 模板引擎使用
app.use(views(__dirname + '/views', {
  extension: 'ejs'
}))

// logger
app.use(async (ctx, next) => {
  const start = new Date()
  await next()
  const ms = new Date() - start
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})

// 启动路由分组routes 
app.use(index.routes(), index.allowedMethods())
app.use(users.routes(), users.allowedMethods())

// error-handling
app.on('error', (err, ctx) => {
  console.error('server error', err, ctx)
});

module.exports = app

//routes/index.js

// 安装并且导入路由的模块
const router = require('koa-router')()

// 路由结果: http://localhost:3000?user=tom&age=18
router.get('/', async (ctx, next) => {
  // ctx.body ='向浏览器输入内容';
  /* await ctx.render('index', {
    title: 'Hello Koa 2!'
  }) */
  //  get  传输 (两个效果一样)
  // ctx.body = 'get 的传值' + ctx.request.querystring; // 字符串
  // ctx.body = 'get 的传值' + ctx.querystring; // 字符串
  
  // 将来实现前端接口调用(两个效果一样)
  // ctx.body = ctx.request.query; // 对象
  ctx.body = ctx.query; // 对象
})

// 路由结果: http://localhost:3000/list/tom/18
// 动态路由: get 传输
router.get('/list/:user/:age',async(ctx,next)=>{
	let  username = ctx.params.user;  // 动态路由用  ctx.params 接收的
	let  age = ctx.params.age;
	ctx.body = '结果:'+ username +',' + age;
	//结果:tom,18
})

// post 传值
router.get('/add',async(ctx,next)=>{
	await ctx.render('add.ejs');
})
router.post('/doadd',async(ctx,next)=>{
	console.log(ctx.request.body); // post 接收
	ctx.body = ctx.request.body;
})


module.exports = router

// routes/users.js

const router = require('koa-router')()

router.prefix('/users')

router.get('/', function (ctx, next) {
  ctx.body = 'this is a users response!'
})

router.get('/bar', function (ctx, next) {
  ctx.body = 'this is a users/bar response'
})

module.exports = router

<!-- views/add.ejs-->

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<form action="/doadd" method="post">
			用户名称:<input type="text" name="username"><br>
			用户密码:<input type="password" name="password"><br>
			用户电话:<input type="text" name="mobile"><br>
			<button>添加</button>
		</form>
	</body>
</html>

<!-- views/index.ejs-->

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>EJS Welcome to <%= title %></p>
  </body>
</html>

前端知识日新月异,小菜鸟只能一步一个脚印;经过半个月简单学习,也只是粗略的写nodejs,还需在实战项目中突破自我,加油!

只有脚踏实地的人,才能够说:路,就在我的脚下

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值