nodeJS

1 简介

优点

  1. nodejs的对象,语法和 js 一样
  2. 性能相对于一些后台语言(PHP)高
  3. 前后台配合方便

缺点
没有像 java 那样有丰富的库的支持
用处:

  1. 小型后台系统、中间层
  2. 基于 node 的工具 :测试、构建(gulp、WebPack、grunt)、抓取

2 数据交互

服务器与页面进行数据交互
Node接收GET数据、POST数据、文件数据
文件数据优化:流操作、zlib压缩

2.1 get数据

获取数据主要要得到 路径pathname, 参数query

const http = require('http');
const urllib = require('url');

http.createServer((req, res) => {
	let {pathname, query} = urllib.parse(req.url, true);
	console.log(pathname, query);
	res.end();
}).listen(8080);

2.2 post数据

post数据较大,不能一次性传输
需要将一个大包切成若干个小包

const http = require('http');
const urllib = require('url');
const querystring = require('querystring');

http.createServer((req, res) => {
	let arr = [];
	req.on('data', buffer => {
		arr.push(buffer);				// ?
	});
	req.on('end', () => {
	    // 将数组连接成buffer字符串
		let buffer = Buffer.concat(arr);
		// querystring => 解析buffer字符串数据
		let post = querystring.parse(buffer.toString());

		console.log(buffer);
		console.log(post);
	});
	res.end();
}).listen(8080);

2.3 文件post数据

解析二进制文件数据
步骤一:分析

------WebKitFormBoundaryYcAqX5KAq2SdHuVG		// 分隔符
Content-Disposition: form-data; name="user"		// 数据描述(文件型有两行数据描述)
												// 每个换行都有/r/n
dwadwd
------WebKitFormBoundaryYcAqX5KAq2SdHuVG
Content-Disposition: form-data; name="pass"

wdawd
------WebKitFormBoundaryYcAqX5KAq2SdHuVG
Content-Disposition: form-data; name="f1"; filename="wda.txt"
Content-Type: text/plain

wdawdwwdwd
------WebKitFormBoundaryYcAqX5KAq2SdHuVG--

步骤二:简化

<分隔符>\r\n数据描述\r\n\r\n数据值\r\n
<分隔符>\r\n数据描述\r\n\r\n数据值\r\n
<分隔符>\r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n
<分隔符>--

步骤四:用"<分隔符>"切开数据

[,
  \r\n数据描述\r\n\r\n数据值\r\n,
  \r\n数据描述\r\n\r\n数据值\r\n,
  \r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n,
  -\-\r\n
]

步骤五:丢弃头尾元素

[
  \r\n数据描述\r\n\r\n数据值\r\n,
  \r\n数据描述\r\n\r\n数据值\r\n,
  \r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n,
]

步骤六:丢弃每一项的头尾\r\n

[
  数据描述\r\n\r\n数据值,
  数据描述\r\n\r\n数据值,
  数据描述1\r\n数据描述2\r\n\r\n<文件内容>,
]

步骤七:用第一次出现的"\r\n\r\n"切分

  普通数据:[数据描述, 数据值]
  或
  文件数据:[数据描述1\r\n数据描述2, <文件内容>]

步骤八:判断描述的里面有没有"\r\n"

  有\r\n——文件数据:[数据描述1\r\n数据描述2, <文件内容>]
  没有\r\n——普通数据:[数据描述, 数据值]

步骤九:分析"数据描述"

2.3.1 代码实现

const http=require('http');
const common=require('./libs/common');
const fs=require('fs');
const uuid=require('uuid/v4');

let server=http.createServer((req, res)=>{
  let arr=[];

  req.on('data', data=>{
    arr.push(data);
  });
  req.on('end', ()=>{
    let data=Buffer.concat(arr);

    //data
    //解析二进制文件上传数据
    let post={};
    let files={};
    if(req.headers['content-type']){
      let str=req.headers['content-type'].split('; ')[1];
      if(str){
        let boundary='--'+str.split('=')[1];

        //1.用"分隔符切分整个数据"
        let arr=data.split(boundary);

        //2.丢弃头尾两个数据
        arr.shift();
        arr.pop();

        //3.丢弃掉每个数据头尾的"\r\n"
        arr=arr.map(buffer=>buffer.slice(2,buffer.length-2));

        //4.每个数据在第一个"\r\n\r\n"处切成两半
        arr.forEach(buffer=>{
          let n=buffer.indexOf('\r\n\r\n');

          let disposition=buffer.slice(0, n);
          let content=buffer.slice(n+4);

          disposition=disposition.toString();

          if(disposition.indexOf('\r\n')==-1){
            //普通数据
            //Content-Disposition: form-data; name="user"
            content=content.toString();

            let name=disposition.split('; ')[1].split('=')[1];
            name=name.substring(1, name.length-1);

            post[name]=content;
          }else{
            //文件数据
            /*Content-Disposition: form-data; name="f1"; filename="a.txt"\r\n
            Content-Type: text/plain*/
            let [line1, line2]=disposition.split('\r\n');
            let [,name,filename]=line1.split('; ');
            let type=line2.split(': ')[1];

            name=name.split('=')[1];
            name=name.substring(1,name.length-1);

            filename=filename.split('=')[1];
            filename=filename.substring(1,filename.length-1);

            // 将文件内容存入upload文件夹中,文件名由uuid生成
            let path=`upload/${uuid().replace(/\-/g, '')}`;

            fs.writeFile(path, content, err=>{
              if(err){
                console.log('文件写入失败', err);
              }else{
                files[name]={filename, path, type};
                console.log(files);
              }
            });
          }
        });
        //5.完成
        console.log(post);
      }
    }
    res.end();
  });
});
server.listen(8080);

2.3.2 buffer操作

  1. 查找 indexOf()
  2. 截取 slice(s, e) [s, …, e)
  3. 切分 split(需要自己实现)
// split切分实现
// this —— 需要切分的字符串(buffer型)
// separator—— 分隔符
// abc---wer---qwe
Buffer.prototype.split = Buffer.prototype.split || function(separator) {
  let arr = [];
  let current = 0;
  let n = 0;
  while((n=this.indexOf(separator, current)) > 0) {
  	arr.push(this.slice(current, n));
  	current = n + separator.length;
  };
  arr.push(this.slice(current));
  return arr;
};

3 mysql使用

数据库:navicat

3.1 引入mysql模块

cnpm init
cnpm i mysql -D

3.2 使用mysql

const mysql = require('mysql');
let db = mysql.createPool({host: 'localhost', user: 'root', password: '', port: 3306, database: '20180127'});

3.3 SQL增删改查

1.INSERT
  INSERT INTO(字段列表) VALUES(值列表)
  INSERT INTO user_table (ID, name, gender, chinese, math, english) VALUES(0, 'blue', '男', 35, 18, 29);

2.DELETE
  DELETE FROMWHERE 条件
  DELETE FROM user_table WHERE ID=3;

3.UPDATE
  UPDATESET 字段=, 字段2=2, ... WHERE 条件
  UPDATE user_table SET chinese=100 WHERE ID=2;

4.SELECT
  SELECT 字段列表 FROMWHERE 条件
  SELECT * FROM user_table WHERE user='${user}';

SQL注入
概念:将SQL语句通过表单提交,对数据库执行恶意SQL命令
防范:充分校验

3.4 例子

// 1. 连接数据库(config配置文件)
// 2. query命令增删改查
// 3. crypto二次加密
const mysql=require('mysql');
const config=require('./config');
const crypto=require('crypto');

let db=mysql.createPool({
  host:       config.db_host,
  port:       config.db_port,
  user:       config.db_user,
  password:   config.db_pass,
  database:   config.db_name,
});

let md5=crypto.createHash('md5');

let username='skylake';
let password='123456';
md5.update(password+config.md5_key);
password=md5.digest('hex');

db.query(`INSERT INTO user_table (name,password) VALUES('${username}', '${password}')`, (err, rows)=>{
  if(err){
    console.log(err);
  }else{
    console.log(rows);
  }
});

4 express框架

入门简单、做东西极其麻烦——回调函数
写法

const express = require('express');

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

server.get('/xxx', ()=>{});
server.post('/xxx', ()=>{});
server.use('/xxx', ()=>{});     //use = get + post

server.get(()=>{});
server.post(()=>{});
server.use(()=>{});

4.1 数据交互

4.1.1 GET数据

数据在 req.query 中

server.get('/路径名', (req, res) => {
	console.log(req.query);
})

4.1.2 普通POST

通过引入中间件 body-parser 来处理

cnpm i body-parser -D
const express=require('express');
const body=require('body-parser');

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

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

server.post('/路径名', (req, res)=>{
  console.log(req.body);
});

4.1.3 文件POST

通过引入中间件 multer 来处理

cnpm i multer -D

普通数据:req.body
文件数据:req.files

const express=require('express');
const multer=require('multer');

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

server.use(multer({dest: 'upload/'}).any());

server.post('/upload', (req, res)=>{
  console.log(req.body);
  console.log(req.files);
});

4.1.4 cookie(签名)

通过引入中间件 cookie-parser 来处理

cnpm i cookie-parser -D
const express=require('express');
const cookieParser=require('cookie-parser');

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

server.use(cookieParser({}));

server.get('/a', (req, res)=>{
  console.log(req.cookies);		// 接受
  res.cookie(名字,, 选项);		// 发送——express自带
  res.send('aaa');
});

cookie签名

// 生成带签名的cookie
server.use(cookieParser('任意字符串'));
...
res.cookie('c', 11, {signed: true});
// 获取cookie
req.signedCookie;

4.2 命令

4.2.1 res.send()

res.send() 是 res.write() 升级版,可以向页面写任意形式的数据
不需要 res.end()

4.2.2 res.sendFile()

发送文件
必须用绝对路径:path.reslove(’./xxx’);

4.2.3 res.sendStatus()

发送状态码
= writeHeader + write + end

4.2.4 res.redirect()

重定向

res.redirect(网址)	// 等于
res.setHeader('location', '网址');
res.writeHeader(302);
res.end();

4.2.5 中间件

流水线:

  • 利于重用
  • 利于分工
  • 要用next
  • 顺序执行
中间件功能写法
express.static(‘路径’)(自带)加载静态文件server.use(express.static(‘路径’))
body-parser处理普通post数据server.use(body.urlencoded({extended: false}));
multer处理文件post数据server.use(multer({dest: ‘/path’}).any());
cookie-parser处理cookie数据server.use(cookieParser(‘加密字符串’));
cookie-session处理session出局server.use(cookieSession({keys: [’’, ‘’…]c}));

4.3 路由router

  1. 创建
    let router=express.Router();
  2. 给路由填东西
    router.get(‘地址’, ()=>{});
    router.post(‘地址’, ()=>{});
    router.use(‘地址’, ()=>{});
  3. 添加到父级
    server.use(‘路径’, router);
    parentRouter.use(‘路径’, router);

5 koa框架

入门麻烦、做东西舒服
  generator/async
  koa@1 generator
  koa@2 generator+async 过渡版
  koa@3 async
使用

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

let server=new koa();
server.listen(8080);

let r1=router();
server.use(r1.routes());

r1.get('/aaa', async (ctx, next) => {
  ctx.request
  ctx,response
}

5.1 路由

koa强依赖router(必须使用router)

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

let r1=router();
server.use(r1.routes());

r1.get('xxx', async);
r1.post('xxx', async);
r1.use('xxx', async);
r1.put('xxx', async);
r1.delete('xxx', async);
...

5.2 请求/响应

  ctx.request
    ctx.request.method
    ctx.request.url
    ctx.request.header/headers

  ctx.response
  	ctx.response.set('名字', '值')	设置Response Headers
    ctx.response.status=xxx     	状态码
    ctx.response.body=xxx       	向页面输出内容

5.3 加载静态文件

const koa=require('koa');
const staticCache=require('koa-static-cache');
const pathlib=require('path');

let server=new koa();
server.listen(8080);

server.use(staticCache(pathlib.resolve('www')));	// 推荐绝对路径

5.4 数据

  1. GET : ctx.request.query
  2. 路由参数 : ctx.params
  3. POST/文件
server.use(convert(betterBody({	//koa-convert——转换老版插件
	uploadDir:      '路径',
	keepExtensions: bool
})));
ctx.request.fields        数据+文件信息
ctx.request.files         文件信息()
  1. cookie:
    获取:ctx.cookies.get(name)
    设置:ctx.cookies.set(name, val, options)
  2. session:
    使用:server.use(session({}, server))
    获取/设置:ctx.session

5.5 mysql-pro

5.5.1 transaction事务

要么都发生、要么不发生
事务的四个特性:ACID

  • A 原子性:要么都发生、要么都不发生
  • C 持久性:只要事务提交了,他的作用就是永久
  • I 隔离性:各个事务之间是独立
  • D 一致性:事务前后的状态是一致的
// 使用事务
const koa=require('koa');
const router=require('koa-router');
const Mysql=require('mysql-pro');

const db=new Mysql({
  mysql: {host: 'localhost',user: 'root',password: '',database: '20180412'}
});

const server=new koa();
server.listen(8080);

let r1=router();
server.use(r1.routes());

r1.get('/user', async ctx=>{
  await db.startTransaction();
  // 强制绑定事物
  let data=await db.executeTransaction("SELECT * FROM user_table");
  await db.stopTransaction();

  console.log(data);
  ctx.response.body=data;
});

5.5.2 防止注入

? 占位符

db.executeTransaction('SELECT * FRO user_table WHERE ID=? AND age=?', [id, 18])

5.5 中间件

中间件功能
koa-convert转换老版插件
path相对路径 -> 绝对路径
koa-better-body解析post数据
koa-static-cache加载静态资源(+压缩)
koa-router路由
koa-sessionsession数据
koa-mysql
koa-pug
koa-ejs
mysql-pro

待完善↓

回调地狱

模块机制

作用:可以减少模块耦合
实现:把一个功能单独写在一个文件里,用 exports 或 module.exports 暴露接口

  1. exports.xxx 用来导出属性和方法
  2. module.exports = function Fn {};——可以直接new实例对象
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值