NodeJS踩坑实录

nodejs的常用api

  • url 主要是配置一系列和路径相关的信息
url.parse(urlString[, parseQueryString[, slashesDenoteHost]]) 将一个URL字符串解析为URL对象
urlString: 解析的路径字符串
parseQueryString: 返回是布尔类型,主要用来解析query的
slashesDenoteHost: 返回是布尔类型,当你不确定你的请求协议时,辅助帮助你进行解析

url.format(urlObj,parseObj,slashesObj) 将url对象转换为字符串
与parse参数相反

url.resolve(from, to) 将基础路径和后缀路径转换成目标路径
from 解析时相对的基本URL
to 要解析的超链接 URL
值得注意的是基本路径要在路径最后添加'/',否则合并会找到你最近的'/'并替换
const url = require('url');
url.resolve('/one/two/three', 'four');         // '/one/two/four'
url.resolve('http://example.com/', '/one');    // 'http://example.com/one'
url.resolve('http://example.com/one', '/two'); // 'http://example.com/two'
  • queryString 为查询字符串提供扩展
querystring 模块提供了一些实用函数,用于解析与格式化 URL 查询字符串
querystring.parse(str,con,seq)
str 要解析的 URL 查询字符串
con用于界定查询字符串中的键值对的子字符串。默认为 '&'
seq 用于界定查询字符串中的键与值的子字符串。默认为 '='

querystring.stringify(obj,con,seq)
obj 要序列化成 URL 查询字符串的对象
con 用于界定查询字符串中的键值对的子字符串。默认为 '&'
seq 用于界定查询字符串中的键与值的子字符串。默认为 '='

querystring.escape(str) 相当于encodeURI 将Asc编码转换成utf-8
对给定的str进行 URL编码
该方法是提供给 querystring.stringify()使用的,通常不直接使用

querystring.unescape(str) 相当于decodeURI 将utf-8转换成ASc
对给定的str进行解码
该方法是提供给 querystring.parse()使用的,通常不直接使用
  • events - 事件触发器

大多数 Node.js 核心 API 构建于惯用的异步事件驱动架构,其中某些类型的对象(又称触发器,Emitter)会触发命名事件来调用函数(又称监听器,Listener)

当 EventEmitter 对象触发一个事件时,所有绑定在该事件上的函数都会被同步地调用

例子,一个简单的 EventEmitter 实例,绑定了一个监听器。 eventEmitter.on() 方法用于注册监听器,eventEmitter.emit() 方法用于触发事件。

const Eventemitter = require("events")

class Player extends Eventemitter {}

const player = new Player()

//使用 eventEmitter.on() 注册监听器时,监听器会在每次触发命名事件时被调用
player.on("change",(track) => {
    console.log(`node事件机制`,${track})
})


//使用 eventEmitter.once() 可以注册最多可调用一次的监听器。 当事件被触发时,监听器会被注销,然后再调用
//player.once("change",(track) => {
//    console.log(`node事件机制`,${track})
//})

player.emit("change","react")
player.emit("change","vue")

传参数与this到监听器
eventEmitter.emit() 方法可以传任意数量的参数到监听器函数。 当监听器函数被调用时,this 关键词会被指向监听器所绑定的 EventEmitter 实例

const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {
  console.log(a, b, this, this === myEmitter);
  // 打印:
  //   a b MyEmitter {
  //     domain: null,
  //     _events: { event: [Function] },
  //     _eventsCount: 1,
  //     _maxListeners: undefined } true
});
myEmitter.emit('event', 'a', 'b');

emitter.removeAllListeners([eventName])
移除全部监听器或指定的 eventName 事件的监听器。

  • fs - 文件系统

fs 模块提供了一些接口用于以一种类似标准 POSIX 函数的方式与文件系统进行交互

所有的文件系统操作都有同步和异步两种形式

异步形式的最后一个参数都是完成时的回调函数。 传给回调函数的参数取决于具体方法,但回调函数的第一个参数都会保留给异常。 如果操作成功完成,则第一个参数会是 null 或 undefined

fs.Stats
fs.Stats 对象提供了一个文件的信息
stats.isDirectory() 如果 fs.Stats 对象表示一个文件系统目录,则返回 true
stats.isFile() 如果 fs.Stats 对象表示一个普通文件,则返回 true

fs.mkdir(path[, options], callback)
异步地创建目录。 完成回调只有一个可能的异常参数

// 创建 /temp/a/apple 目录,不管 `/temp` 和 /temp/a 目录是否存在。
fs.mkdir('/temp/a/apple', (err) => {
  if (err) throw err;
});

fs.writeFile(file, data[, options], callback)
异步地写入数据到文件,如果文件已经存在,则覆盖文件。 data 可以是字符串或 buffer

fs.writeFile('temp.js', 'keep study', (err) => {
  if (err) throw err;
  console.log('文件已保存!');
});

fs.appendFile(path, data[, options], callback)
异步地追加数据到文件,如果文件不存在则创建文件。 data 可以是字符串或 Buffer

fs.appendFile('temp.js', '追加的数据', (err) => {
  if (err) throw err;
  console.log('数据已追加到文件');
});

fs.readFile(path[, options], callback)
异步地读取一个文件的全部内容

fs.readFile('/etc/passwd', (err, data) => {
  if (err) throw err;
  console.log(data);
});

回调有两个参数 (err, data),其中 data 是文件的内容。

如果未指定字符编码,则返回原始的 buffer。

如果 options 是一个字符串,则它指定了字符编码。例子:

fs.readFile('/etc/passwd', 'utf8', callback);

fs.readdir(path[, options], callback)
读取目录的内容。 回调有两个参数 (err, files),其中 files 是目录中文件名的数组,不包含 '.' 和 '..'。
options 参数用于传入回调的文件名。 它可以是一个字符串,指定字符编码。 也可以是一个对象,其中 encoding 属性指定字符编码。 如果 encoding 设为 'buffer',则返回的文件名会是 Buffer 对象。

fs.rmdir(path, callback)
删除目录

fs.readFileSync(path[, options])
同步读取文件

fs.readdirSync(path[, options])
同步读取目录

fs.unlink(path, callback)
解除关系(也即删除文件)

readFileSync和unlink结合实现删除一个目录及其目录下的文件的例子:

const fs = require('fs');

fs.readdirSync("logs").map((file) => {
    fs.unlink(`logs/${file}`,() => {
        console.log("删除成功")
    })
})

fs.rmdir("logs", (err)=> {
    console.log("确定要删除吗?")
})

fs.watch(filename[, options][, listener])
监视 filename 的变化,filename 可以是一个文件或一个目录。

如果提供的 options 是一个字符串,则它指定了 encoding。 否则 options 应该传入一个对象。

监听器回调有两个参数 (eventType, filename)。 eventType 可能是 'rename' 或 'change',filename 是触发事件的文件的名称。

在大多数平台,当目录中一个文件出现或消失时,就会触发 'rename' 事件。

fs.createReadStream(path[, options])

fs.createWriteStream(path[, options])

const fs = require('fs');

const ws = fs.createWriteStream('./demo.txt');

const tid = setInterval(() => {
    const num = parseInt(Math.random()*10);
    if (num < 8) {
        ws.write(num + '');
    } else {
        clearInterval(tid);
        ws.end()
    }
},200)

ws.on('finish', () => {
    console.log('done');
})

fs 解决回调地狱问题

const fs = require('fs');
const promisify = require('util').promisify;

const read = promisify(fs.readFile);

// read('./promisify.js').then(data => {
//     console.log(data.toString());
// }).catch(ex => {
//     console.log(ex)
// })

async function test() {
    try {
        const content = await read('./promisify.js');
        console.log(content.toString());
    } catch (ex) {
        console.log(ex);
    }
}

test();
  • path path 模块提供了一些工具函数,用于处理文件与目录的路径

path.normalize()
path.normalize() 方法会规范化给定的 path,并解析 '..' 和 '.' 片段。

当发现多个连续的路径分隔符时(如 POSIX 上的 / 与 Windows 上的 或 /),它们会被单个的路径分隔符(POSIX 上是 /,Windows 上是 )替换。 末尾的多个分隔符会被保留。

如果 path 是一个长度为零的字符串,则返回 '.',表示当前工作目录。
例如,在 POSIX 上:

path.normalize('/foo/bar//baz/asdf/quux/..');
// 返回: '/foo/bar/baz/asdf'

在 Windows 上:

path.normalize('C:\\temp\\\\foo\\bar\\..\\');
// 返回: 'C:\\temp\\foo\\'

path.join([...paths])
path.join() 方法使用平台特定的分隔符把全部给定的 path 片段连接到一起,并规范化生成的路径。

长度为零的 path 片段会被忽略。 如果连接后的路径字符串是一个长度为零的字符串,则返回 '.',表示当前工作目录。
如果任一路径片段不是一个字符串,则抛出 TypeError

例子:

path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
// 返回: '/foo/bar/baz/asdf'

path.join('foo', {}, 'bar');
// 抛出 'TypeError: Path must be a string. Received {}'

path.resolve([...paths])
path.resolve() 方法会把一个路径或路径片段的序列解析为一个绝对路径。

给定的路径的序列是从右往左被处理的,后面每个 path 被依次解析,直到构造完成一个绝对路径。 例如,给定的路径片段的序列为:/foo、/bar、baz,则调用 path.resolve('/foo', '/bar', 'baz') 会返回 /bar/baz

如果没有传入 path 片段,则 path.resolve() 会返回当前工作目录的绝对路径
如果任何参数不是一个字符串,则抛出 TypeError
例子:

path.resolve('/foo/bar', './baz');
// 返回: '/foo/bar/baz'

path.resolve('/foo/bar', '/tmp/file/');
// 返回: '/tmp/file'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// 如果当前工作目录为 /home/myself/node,
// 则返回 '/home/myself/node/wwwroot/static_files/gif/image.gif'

path.basename(path[, ext])
path <string>
ext <string> 可选的文件扩展名

path.basename() 方法返回一个 path 的最后一部分,类似于 Unix 中的 basename 命令
例子:

path.basename('/foo/bar/baz/asdf/quux.html');
// 返回: 'quux.html'

path.basename('/foo/bar/baz/asdf/quux.html', '.html');
// 返回: 'quux'

path.extname(path)
path.extname() 方法返回 path 的扩展名,即从 path 的最后一部分中的最后一个 .(句号)字符到字符串结束。 如果 path 的最后一部分没有 . 或 path 的文件名(见 path.basename())的第一个字符是 .,则返回一个空字符串。

如果 path 不是一个字符串,则抛出 TypeError
例子:

path.extname('index.html');
// 返回: '.html'

path.extname('index.coffee.md');
// 返回: '.md'

path.extname('index.');
// 返回: '.'

path.extname('index');
// 返回: ''

path.extname('.index');
// 返回: ''

path.dirname(path)
path.dirname() 方法返回一个 path 的目录名,类似于 Unix 中的 dirname 命令
例子:

path.dirname('/foo/bar/baz/asdf/quux');
// 返回: '/foo/bar/baz/asdf'

path.parse(path)
path.parse() 方法返回一个对象,对象的属性表示 path 的元素
返回的对象有以下属性:

dir <string>
root <string>
base <string>
name <string>
ext <string>
例如,在 POSIX 上:

path.parse('/home/user/dir/file.txt');
// 返回:
// { root: '/',
//   dir: '/home/user/dir',
//   base: 'file.txt',
//   ext: '.txt',
//   name: 'file' }

在 Windows 上:

path.parse('C:\\path\\dir\\file.txt');
// 返回:
// { root: 'C:\\',
//   dir: 'C:\\path\\dir',
//   base: 'file.txt',
//   ext: '.txt',
//   name: 'file' }

path.format(pathObject)

pathObject <Object>
dir <string>
root <string>
base <string>
name <string>
ext <string>

path.format() 方法会从一个对象返回一个路径字符串。 与 path.parse() 相反。

当 pathObject 提供的属性有组合时,有些属性的优先级比其他的高:

如果提供了 pathObject.dir,则 pathObject.root 会被忽略
如果提供了 pathObject.base 存在,则 pathObject.ext 和 pathObject.name 会被忽略

例如,在 POSIX 上:

// 如果提供了 `dir`、`root` 和 `base`,则返回 `${dir}${path.sep}${base}`。
// `root` 会被忽略。
path.format({
  root: '/ignored',
  dir: '/home/user/dir',
  base: 'file.txt'
});
// 返回: '/home/user/dir/file.txt'

// 如果没有指定 `dir`,则 `root` 会被使用。
// 如果只提供了 `root` 或 `dir` 等于 `root`,则平台的分隔符不会被包含。
// `ext` 会被忽略。
path.format({
  root: '/',
  base: 'file.txt',
  ext: 'ignored'
});
// 返回: '/file.txt'

// 如果没有指定 `base`,则 `name` + `ext` 会被使用。
path.format({
  root: '/',
  name: 'file',
  ext: '.txt'
});
// 返回: '/file.txt'

在 Windows 上:

path.format({
  dir: 'C:\\path\\dir',
  base: 'file.txt'
});
// 返回: 'C:\\path\\dir\\file.txt'

path.sep
提供了平台特定的路径片段分隔符:

Windows 上是 \
POSIX 上是 /
例如,在 POSIX 上:

'foo/bar/baz'.split(path.sep);
// 返回: ['foo', 'bar', 'baz']

在 Windows 上:

'foo\\bar\\baz'.split(path.sep);
// 返回: ['foo', 'bar', 'baz']

path.delimiter
提供平台特定的路径分隔符:

Windows 上是 ;
POSIX 上是 :
例如,在 POSIX 上:

console.log(process.env.PATH);
// 输出: '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin'

process.env.PATH.split(path.delimiter);
// 返回: ['/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/bin']

在 Windows 上:

console.log(process.env.PATH);
// 输出: 'C:\Windows\system32;C:\Windows;C:\Program Files\node\'

process.env.PATH.split(path.delimiter);
// 返回: ['C:\\Windows\\system32', 'C:\\Windows', 'C:\\Program Files\\node\\']

path.win32
path.win32 属性提供了 path 方法针对 Windows 的实现

path.posix
path.posix 属性提供了 path 方法针对 POSIX 的实现

__dirname__filename总是返回文件的绝对路径
process.cwd()总是返回执行node命令所在文件夹
./:在require方法中总是相对于当前文件所在文件夹
在其他地方和process.cwd()一样,相对于node启动文件夹
  • Buffer (缓冲)

在 ECMAScript 2015 引入 TypedArray 之前,JavaScript 语言没有读取或操作二进制数据流的机制。 Buffer 类被引入作为 Node.js API 的一部分,使其可以在 TCP 流或文件系统操作等场景中处理二进制数据流。

TypedArray 现已被添加进 ES6 中,Buffer 类以一种更优化、更适合 Node.js 用例的方式实现了 Uint8Array API。

Buffer 类的实例类似于整数数组,但 Buffer 的大小是固定的、且在 V8 堆外分配物理内存。 Buffer 的大小在被创建时确定,且无法调整。

Buffer 类在 Node.js 中是一个全局变量,因此无需使用 require('buffer').Buffer。

Buffer.alloc(size[, fill[, encoding]])
size <integer> 新建的 Buffer 期望的长度
fill <string> | <Buffer> | <integer> 用来预填充新建的 Buffer 的值。 默认: 0
encoding <string> 如果 fill 是字符串,则该值是它的字符编码。 默认: 'utf8'

分配一个大小为 size 字节的新建的 Buffer 。 如果 fill 为 undefined ,则该 Buffer 会用 0 填充。
例子:

const buf = Buffer.alloc(5);

// 输出: <Buffer 00 00 00 00 00>
console.log(buf);

如果同时指定了 fill 和 encoding ,则会调用 buf.fill(fill, encoding) 初始化分配的 Buffer 。

const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');

// 输出: <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
console.log(buf);

调用 Buffer.alloc() 会明显地比另一个方法 Buffer.allocUnsafe() 慢,但是能确保新建的 Buffer 实例的内容不会包含敏感数据。

Buffer.allocUnsafe(size)
分配一个大小为 size 字节的新建的 Buffer 。 如果 size 大于 buffer.constants.MAX_LENGTH 或小于 0,则抛出 [RangeError] 错误。 如果 size 为 0,则创建一个长度为 0 的 Buffer。

以这种方式创建的 Buffer 实例的底层内存是未初始化的。 新创建的 Buffer 的内容是未知的,且可能包含敏感数据。 可以使用 buf.fill(0) 初始化 Buffer 实例为0。

const buf = Buffer.allocUnsafe(10);

// 输出: (内容可能不同): <Buffer a0 8b 28 3f 01 00 00 00 50 32>
console.log(buf);

buf.fill(0);

// 输出: <Buffer 00 00 00 00 00 00 00 00 00 00>
console.log(buf);

Buffer.byteLength(string[, encoding])
返回一个字符串的实际字节长度。 这与 String.prototype.length 不同,因为那返回字符串的字符数

Buffer.isBuffer(obj)
如果 obj 是一个 Buffer 则返回 true ,否则返回 false。

Buffer.concat(list[, totalLength])
list <Array> 要合并的 Buffer 或 Uint8Array 实例的数组
totalLength <integer> 合并时 list 中 Buffer 实例的总长度

返回一个合并了 list 中所有 Buffer 实例的新建的 Buffer 。

如果 list 中没有元素、或 totalLength 为 0 ,则返回一个新建的长度为 0 的 Buffer 。

const buf1 = Buffer.alloc(10);
const buf2 = Buffer.alloc(14);
const buf3 = Buffer.alloc(18);
const totalLength = buf1.length + buf2.length + buf3.length;

// 输出: 42
console.log(totalLength);

const bufA = Buffer.concat([buf1, buf2, buf3], totalLength);

// 输出: <Buffer 00 00 00 00 ...>
console.log(bufA);

// 输出: 42
console.log(bufA.length);

buf.length
返回 buf 在字节数上分配的内存量。

const buf = Buffer.alloc(1234);

// 输出: 1234
console.log(buf.length);

buf.toString([encoding[, start[, end]]])
encoding <string> 解码使用的字符编码。默认: 'utf8'
start <integer> 开始解码的字节偏移量。默认: 0
end <integer> 结束解码的字节偏移量(不包含)。 默认: buf.length

根据 encoding 指定的字符编码解码 buf 成一个字符串。 start 和 end 可传入用于只解码 buf 的一部分。

const buf1 = Buffer.allocUnsafe(26);

for (let i = 0; i < 26; i++) {
  // 97 是 'a' 的十进制 ASCII 值
  buf1[i] = i + 97;
}

// 输出: abcdefghijklmnopqrstuvwxyz
console.log(buf1.toString('ascii'));

// 输出: abcde
console.log(buf1.toString('ascii', 0, 5));

buf.fill(value[, offset[, end]][, encoding])
value <string> | <Buffer> | <integer> 用来填充 buf 的值。
offset <integer> 开始填充 buf 前要跳过的字节数。默认: 0。
end <integer> 结束填充 buf 的位置(不包含)。默认: buf.length。
encoding <string> 如果 value 是一个字符串,则这是它的字符编码。默认: 'utf8'。

buf.equals(otherBuffer)
如果 buf 与 otherBuffer 具有完全相同的字节,则返回 true,否则返回 false。

const buf1 = Buffer.from('ABC');
const buf2 = Buffer.from('414243', 'hex');
const buf3 = Buffer.from('ABCD');

// 输出: true
console.log(buf1.equals(buf2));

// 输出: false
console.log(buf1.equals(buf3));

buf.indexOf(value[, byteOffset][, encoding])
buf 中 value 首次出现的索引,如果 buf 没包含 value 则返回 -1
value <string> | <Buffer> | <Uint8Array> | <integer> 要搜索的值
byteOffset <integer> buf 中开始搜索的位置。默认: 0
encoding <string> 如果 value 是一个字符串,则这是它的字符编码。 默认: 'utf8'

const buf = Buffer.from('this is a buffer');

// 输出: 0
console.log(buf.indexOf('this'));

// 输出: 2
console.log(buf.indexOf('is'));

// 输出: 8
console.log(buf.indexOf(Buffer.from('a buffer')));

// 输出: 8
// (97 是 'a' 的十进制 ASCII 值)
console.log(buf.indexOf(97));

// 输出: -1
console.log(buf.indexOf(Buffer.from('a buffer example')));

buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
target <Buffer> | <Uint8Array> 要拷贝进的 Buffer 或 Uint8Array。
targetStart <integer> target 中开始拷贝进的偏移量。 默认: 0
sourceStart <integer> buf 中开始拷贝的偏移量。 默认: 0
sourceEnd <integer> buf 中结束拷贝的偏移量(不包含)。 默认: buf.length
拷贝 buf 的一个区域的数据到 target 的一个区域,即便 target 的内存区域与 buf 的重叠。

buf 解决中文字符串乱码问题
const StringDecoder = require('string_decoder').StringDecoder;
const decoder = new StringDecoder('utf8');
const buf = Buffer.from('中文字符串!');

//for (let i = 0; i < buf.length; i+= 5) {
//    const b = Buffer.allocUnsafe(5);
//    buf.copy(b,0,i);

//    console.log(b.toString()); 
//}

for (let i = 0; i < buf.length; i+= 5) {
    const b = Buffer.allocUnsafe(5);
    buf.copy(b,0,i);

    console.log(decoder.write(b)); 
}
  • process - 进程

process 对象是一个全局变量,它提供当前 Node.js 进程的有关信息,以及控制当前 Node.js 进程。 因为是全局变量,所以无需使用 require()。

process.stdin
process.stdin 属性返回连接到 stdin (fd 0)的流。 它是一个net.Socket(它是一个Duplex流),除非 fd 0指向一个文件,在这种情况下它是一个Readable流。
process.stdout
process.stdout 属性返回连接到 stdout (fd 1)的流。 它是一个net.Socket (它是一个Duplex流), 除非 fd 1 指向一个文件,在这种情况下它是一个[可写][]流。
例1: 将输入流数据输出到输出流,即输出到终端。

process.stdin.pipe(process.stdout);

Cookie和Session

Cookie

● HTTP是无状态协议。简单地说,当你浏览了一个页面,然后转到同一个网站的另一个页面,服务器无法认识到,这是同一个浏览器在访问同一个网站。每一次的访问,都是没有任何关系的。
那么世界就乱套了,比如我上一次访问,登陆了,下一次访问,又让我登陆,不存在登陆这事儿了。
● Cookie是一个简单到爆的想法:当访问一个页面的时候,服务器在下行HTTP报文中,命令浏览器存储一个字符串;浏览器再访问同一个域的时候,将把这个字符串携带到上行HTTP请求中。

● 第一次访问一个服务器,不可能携带cookie。 必须是服务器得到这次请求,在下行响应报头中,携带cookie信息,此后每一次浏览器往这个服务器发出的请求,都会携带这个cookie。

特点
● cookie是不加密的,用户可以自由看到;
● 用户可以删除cookie,或者禁用它
● cookie可以被篡改
● cookie可以用于攻击
● cookie存储量很小。未来实际上要被localStorage替代,但是后者IE9兼容。

express中的cookie,你肯定能想到。 res负责设置cookie, req负责识别cookie。
图片描述

cookie实例

var express  = require('express');
var cookieParser = require('cookie-parser');
 
var app = express();
//使用cookie必须要使用cookie-parser中间件
app.use(cookieParser());

app.get("/",function(req,res){
    res.send("猜你喜欢" + req.cookies.mudidi);
});

//查询一个地方的攻略,URL语法: http://127.0.0.1/gonglue?mididi=北京
//此时北京就能记录在cookie中
app.get("/gonglue",function(req,res){
    //得到get请求,用户查询的目的地
    var mudidi = req.query.mudidi;
    //记录用户喜好
    //先读取用户的喜好,然后把新的数据push进入数组,然后设置新的cookie
    var mudidiarry = req.cookies.mudidi || [];
    mudidiarry.push(mudidi);
    //maxAge在Express中以毫秒为单位
    res.cookie("mudidi",mudidiarry,{maxAge: 900000, httpOnly: true});
    res.send(mudidi + "旅游攻略");
});

app.listen(3000);

Session

session依赖cookie,当一个浏览器禁用cookie的时候,登陆效果消失; 或者用户清除了cookie,登陆也消失。
session比cookie不一样在哪里呢? session下发的是乱码,并且服务器自己缓存一些东西,下次浏览器的请求带着乱码上来,此时与缓存进行比较,看看是谁。
所以,一个乱码,可以对应无限大的数据。
任何语言中,session的使用,是“机理透明”的。他是帮你设置cookie的,但是足够方便,让你感觉不到这事儿和cookie有关。
session实例

var express = require("express");
var app = express();
var session = require("express-session");

app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true
}));

app.get("/",function(req,res){
    if(req.session.login == "1"){
        res.send("欢迎" + req.session.username);
    }else{
        res.send("没有成功登陆");
    }
});

app.get("/login",function(req,res){
    req.session.login = "1";    //设置这个session
    req.session.username = "考拉";
    res.send("你已经成功登陆");
});

app.listen(3000);

加密(MD5加密)

MD5加密是函数型加密。就是每次加密的结果一定相同,没有随机位。
特点:
● 不管加密的文字,多长多短,永远都是32位英语字母、数字混合。
● 哪怕只改一个字,密文都会大变。
● MD5没有反函数破解的可能,网上的破解工具,都是通过字典的模式,通过大量列出明-密对应的字典,找到明码。两次加密网上也有对应的字典。所以我们不要直接用一层md5,这样对黑客来说和明码是一样。

MD5常用于作为版本校验。可以比对两个软件、文件是否完全一致。

node中自带了一个模块,叫做crypto模块,负责加密。

首先创建hash,然后update和digest:

var crypto = require("crypto");
module.exports = function(mingma){
    var md5 = crypto.createHash('md5');
    var password = md5.update(mingma).digest('base64');
    return password;
}

node框架之express

关于express

一个基于node的开发框架
express,不会在node上进行2次抽象,而是在node本身上提供扩展
完全由路由和中间件组成,从本质上来说express就是根据路由对前后端进行桥接
中间件(middleware)指的就是一个函数,可以访问请求对象,也可以访问响应对象,可以访问请求和响应循环中的下一个中间件

中间件的特性

  • 可以执行任何代码
  • 可以访问请求或者响应对象
  • 可以终结请求响应流程
  • 可以调用堆栈中的下一个中间件(next)

安装指令

npm init
npm i express -g
npm i express-generator -g //专门供windows使用
切换到指定项目目录
npm i express --save-dev
npm i express-generator --save-dev

Hello world~~

var express = require("express");
var app = express();

app.get("/",(req,res) => {
    res.write("this is homePage")
})

app.get("/login",(req,res) => {
    res.send("this is loginPage")
})


var server = app.listen(3000,"127.0.0.1",() => {
    console.log("express hello_world")
})

路由响应方法

方法 描述
res.download() 提示下载文件
res.end() 终结响应处理流程
res.json() 发送JSON响应
res.jsonp() 发送一个支持JSONP的JSON格式的响应
res.redirect() 重定向请求
res.send() 发送各种类型的响应
res.sendFile() 以八位字节流的形式发送文件
res.sendStatus() 设置响应状态代码并将其以字符串形式作为响应体的一部分发送。

express 脚手架

利用express中内置的脚手架构建项目
在指定目录下 express -e project_name
cd project_name
npm install
npm install

图片描述

body-parser 帮助对请求体进行解析
cookie-parser 给cookie提供解析
debug 帮助在控制台上输出debug信息
ejs javascript 一个模板引擎
morgan 帮助在控制台上反馈request的信息
serve-favicon 主要是为了解决初始化请求favicon图标问题
app.set(event, str) 设定一些参数
__dirname 绝对路径
app.use(path,callback) 接受中间件并执行
res.sendFile(absolutePath--绝对路径)

ejs语法

<%= %> 输出内容标签
<%- %> 输出html内容标签
<% %> 流程标签 可以执行js代码
<%# %> 注释标签
<%- include(path) %> 引入标签

express实战

利用express+mongodb实现一个从登录、注册、注销到评论页、详情页的微型前后端电商平台,具体代码见github:https://github.com/Jack-cool/expressPro

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 session = require('express-session');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var commentRouter = require('./routes/comment');
// var async = require('async');
var app = express();

// 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(session({
  secret: 'recommend 128 bytes random string', // 对session id 相关的cookie 进行签名
  cookie: { maxAge: 20*60*1000 }, // // 设置 session 的有效时间,单位毫秒
  resave: true,
  saveUninitialized: true // 是否保存未初始化的会话
}))
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/comment', commentRouter);


// 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) {
  res.render('index', { title: 'Home', username: req.session.username });
});

router.get('/register', function(req, res, next) {
  res.render('register', { title: '注册页' });
});

router.get('/login', function(req, res, next) {
  res.render('login', { title: '登录页'})
});

router.get('/logout', function(req,res,next) {
  // req.session.username = undefined;
  // res.redirect('/');
  req.session.destroy(function(err) {
    if (err) {
      console.log(err);
    } else {
      res.redirect("/");
    }
  })
});

router.get('/comment', function(req,res,next) {
  res.render('comment', { title: '评论页' })
})

module.exports = router;

routes/users.js

var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var DB_CONNECT_STR = "mongodb://localhost:27017/users"

var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

router.post('/register',function(req,res){
  // res.send('注册成功');
  console.log('注册信息',req.body);
  var username = req.body.reg_name;
  var nickname = req.body.reg_nickname;
  var pwd = req.body.reg_pwd;
  var insertData = function(db,callback) {
    // 关联集合
    var data = [{ username:username,nickname:nickname,pwd:pwd }]
    var conn = db.collection('front');
    conn.insert(data,function(err,result) {
      if (err) {
        console.log(err)
      } else {
        callback(result);
      }
    })
  }
  // 连接数据库
  MongoClient.connect(DB_CONNECT_STR,function(err,db) {
    if (err) {
      console.log('连接数据库失败');
    } else {
      console.log('连接数据库成功');
      insertData(db,function(result) {
        res.send('哈哈,你已经注册成功了呦~~~')
        db.close();
      })
    }
  })
})

router.post('/login', function(req,res,next) {
  var username = req.body.log_name;
  var nickname = req.body.log_nickname;
  var pwd = req.body.log_pwd;

  var findData = function(db,callback) {
    // 查询条件
    var data = { username:username,nickname:nickname,pwd:pwd }
    // 关联集合
    var conn = db.collection('front');
    conn.find(data).toArray(function(err,result) {
      if (err) {
        console.log(err);
      } else {
        callback(result);
      }
    })
  }
  // 连接数据库
  MongoClient.connect(DB_CONNECT_STR,function(err,db) {
    if (err) {
      console.log('连接数据库失败');
    } else {
      findData(db,function(result) {
        console.log('登陆数据~~',result);
        if (result.length > 0) {
          req.session.username = result[0].username;
          res.redirect('/');
        } else {
          res.send('登陆失败');
        }
        db.close();
      })
    }
  })
})

module.exports = router;

routes/comment.js

var express = require('express');
var async = require('async');
var MongoClient = require('mongodb').MongoClient;
var DB_CONNECT_STR = "mongodb://localhost:27017/users";
var router = express.Router();

/* GET home page. */
router.post('/save', function(req, res, next) {
  // res.send('发布成功');
  console.log('评论信息',req.body);
  var title = req.body.comment_title;
  var content = req.body.comment_content;
  // var insertData = function(db,callback) {
  //   // 关联集合
  //   var data = [{ title:title,content:content }];
  //   var conn = db.collection('comments');
  //   conn.insert(data,function(err,result) {
  //     if (err) {
  //       console.log(err);
  //     } else {
  //       callback(result);
  //     }
  //   })
  // } 

  var updateData = function(db,callback) {
    var conn = db.collection('comments');
    var ids = db.collection('ids');
    async.waterfall([function(callback){
      ids.findAndModify({name:'comments'},[["_id","desc"]],{$inc:{id:1}},function(err,result){
        callback(null,result.value.id)
      })
    },function(id,callback){
      var data = [{uid:id,title:title,content:content,username:req.session.username}];
      conn.insert(data,function(result){
        callback(result)
      })
    }],function(err,result){
      if (err) {
        console.log(err);
      } else {
        callback(result);
      }
    })
  }


  MongoClient.connect(DB_CONNECT_STR,function(err,db) {
    if (err) {
      console.log('连接数据库失败');
    } else {
      console.log('连接数据库成功');
      // insertData(db,function(result) {
      //   res.send('嘻嘻嘻,你发布成功了呦~~~');
      //   db.close();
      // })
      updateData(db,function(result) {
        // res.send('嘻嘻嘻,你发布成功了呦~~~');
        res.redirect('/comment/list');
        db.close();
      })
    }
  })
});

router.get('/list',function(req,res) {
  var findData = function(db,callback) {
    var conn = db.collection('comments');
    conn.find({}).toArray(function(err,result) {
      if (err) {
        console.log(err);
      } else {
        callback(result);
      }
    })
  }
  MongoClient.connect(DB_CONNECT_STR,function(err,db){
    if (err) {
      console.log(err);
    } else {
      findData(db,function(result) {
        if (result.length > 0) {
          console.log('评论列表页信息',result);
          res.render('list',{ title:'列表页',list:result });
        } else {
          res.send('亲,没有评论信息~~');
        }
      })
    }
  })
})

router.get('/detail',function(req,res) {
  // res.send('列表页');
  var uid = parseInt(req.query.uid);
  MongoClient.connect(DB_CONNECT_STR,function(err,db) {
    var conn = db.collection('comments');
    conn.find({uid:uid}).toArray(function(err,result) {
      if (err) {
        console.log(err);
      } else {
        console.log('详情页信息',result);
        res.render('detail',{ title:'详情页',mes:result });
      }
    })
  })
})

module.exports = router;

node框架之koa2

文档持续更新中~~~

欢迎关注我的微信公众号~前端森林

前端森林.jpeg

阅读 2.7k

推荐阅读
web进阶
用户专栏

从前端菜鸟到全栈大牛的学习进阶之路

733 人关注
32 篇文章
专栏主页