Node.js学习笔记

1、文件操作

①文件的基本操作

import * as fs from 'fs';

// 回调函数一般是异步的
// 文件操作一般都是错误优先回调函数

fs.mkdir('made-by-fs', err => {
    if (err) console.log('文件夹已经存在');
    else console.log('文件夹创建成功');
})

fs.rename('./made-by-fs', './log', err=>{
    if (err) console.log('文件夹名字已经存在');
    else console.log('文件夹名字修改成功');
})

fs.rmdir('./log', err => {
    console.log(err);
})

// 错误优先回调函数
fs.readdir('./logs', ((err, files) => {
    console.log(files);
}))

// 写文件
fs.writeFile('./logs/log1.txt', 'hello\nworld', err => {
    console.log(err);
})

// 追加文本
fs.appendFile('./logs/log1.txt', '!!!', err => {
    console.log(err);
})

// 判断文件是否存在
fs.exists('./logs/log', exists => {
    console.log(exists);
})

// 删除文件
fs.unlink('./logs/log1.txt', err => {
    console.log(err);
})

// 读取文件
fs.readFile('./logs/log1.txt','utf-8', (err, data) => {
    console.log(data);
})
fs.readFile('./logs/log1.txt', (err, data) => {
    console.log(data.toString());
})

// 每一个都添加了一个sync表示同步代码
const fileHandleData = fs.readFileSync('./log/log1.txt', 'utf-8');
console.log(fileHandleData);

// 基于promise的文件读取
const promiseFs = require('fs').promise;
async function main_read_file(){
    const data = await promiseFs.readFile('./logs/log1.txt');
    console.log(data);
}
main_read_file();

②文件的基本练习

import * as fs from 'fs';

// 循环创建文件
for (let i = 0; i < 3; i++) {
    fs.writeFile('./log' + i + '.txt', 'log-' + i, 'utf-8', () => {
        console.log(`第${i}次创建成功!`);
    });
}


// 遍历文件夹
fs.readdir('./', 'utf-8', (err, files) => {
    console.log(files);
    // [ 'app.js', 'fs_test.js', 'log', 'log.txt', 'practice.js' ]
    files.forEach((value, index) => {
        // 读取文件或者文件夹,判断当前读取的是哪一种类型
        fs.stat('./' + value, (err1, stats) => {
            if (stats.isDirectory()) {
                // 这里可以调用递归
                console.log('文件夹', value);
            } else {
                // 如果是一个文件的话,那么就读取这个文件
                console.log('文件', value);
            }
        })
    })
})

// 监视watch,与监视文件
fs.watch('./log/log.log', 'utf-8', (eventType, filename) => {
    console.log('文件被修改了');
})
fs.watchFile('./log.txt', ((curr, prev) => {
    console.log(curr, prev);
}))

③文件压缩

import * as fs from 'fs';
import * as zlib from 'zlib';

// 将文件变成压缩包

const gzip = zlib.createGzip();

const readStream = fs.createReadStream('./logs.txt');
const writeStream = fs.createWriteStream('./logs.gzip');

readStream.pipe(gzip).pipe(writeStream);

二、模块和包

①es6中的模块和包

// in test1.js 文件
import {a, b, c, sayHello} from './test2.js';
console.log(a, b, c);

sayHello();



// in test2.js 文件
export let a = 1;


let b = 10;
let c = 20;
export {b, c};


export function sayHello(){
    console.log('hello');
}

// 表示默认暴露一个
// export default a;

②commonjs中的模块和包

// test1.js
const {b, c, sayHello} = require('./test2')
console.log(b, c);

sayHello();


// test2.js
let b = 10;
let c = 20;
function sayHello(){
    console.log('hello');
}

// 表示默认暴露一个
// export default a;
module.exports.b = b;
module.exports.c = c;
module.exports.sayHello = sayHello;

三、内置模块url

// url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
const url = require('url')
const urlString = 'https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110'
const parsedStr = url.parse(urlString)
console.log(parsedStr)


// url.format(urlObject)
const url = require('url')
const urlObject = {
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'www.baidu.com:443',
  port: '443',
  hostname: 'www.baidu.com',
  hash: '#tag=110',
  search: '?id=8&name=mouse',
  query: { id: '8', name: 'mouse' },
  pathname: '/ad/index.html',
  path: '/ad/index.html?id=8&name=mouse',
  href: 'https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110'
}
const parsedObj = url.format(urlObject)
console.log(parsedObj)


// url.resolve(from, to)
const url = require('url')
var a = url.resolve('/one/two/three', 'four')
var b = url.resolve('http://example.com/', '/one')
var c = url.resolve('http://example.com/one', '/two')
console.log(a + "," + b + "," + c)

四、内置模块queryString

// querystring.parse(str[, sep[, eq[, options]]])
const querystring = require('querystring')
var qs = 'x=3&y=4'
var parsed = querystring.parse(qs)
console.log(parsed)
// [Object: null prototype] { x: '3', y: '4' }


// querystring.stringify(obj[, sep[, eq[, options]]])
const querystring = require('querystring')
var qo = {
    x: 3,
    y: 4
}
var parsed = querystring.stringify(qo)
console.log(parsed)
// x=3&y=4


// escape
const querystring = require('querystring')
var str = 'id=3&city=北京&url=https://www.baidu.com'
var escaped = querystring.escape(str)
console.log(escaped)
// id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fwww.baidu.com


// /unescape
const querystring = require('querystring')
var str = 'id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fwww.baidu.com'
var unescaped = querystring.unescape(str)
console.log(unescaped)
// id=3&city=北京&url=https://www.baidu.com

五、内置模块https

①跨域请求cors




    
    Title








import * as http from 'http';

const server = http.createServer((req, res) => {
    let url = req.url;
    switch (url) {
        // 所谓的反向代理,就是将
        case '/api/data': {
            res.writeHead(200, {
                'content-type': 'application/json',
                // 设置这个表示可以跨域,或者说我可以把数据给任何想要我的数据的请求
                'Access-Control-Allow-Origin': '*',
            });
            res.write(JSON.stringify({id: 12, name: 'alex'}));
            break;
        }
        default: {
            res.write('page not found');
            break;
        }
    }
    res.end();
})

server.listen(8080, () => {
    console.log('http://localhost:8080');
})

当尝试通过浏览器从一个源请求到其他的源时,会发生跨域请求行为,此时这个请求开始是可以发出去的,但是当服务端没有做跨域处理时,浏览器是不承认该数据是合法的,所以在返回数据的请求头里面加入以下元素:Access-Control-Allow-Origin': '*',可以解决,表是给任何想要向我请求数据的客户端,当然也可以实现白名单来实现指定源来访问。

②jsonp实现跨域请求




    
    Jsonp









import * as http from 'http';

const server = http.createServer((req, res) => {
    let url = req.url;
    console.log(url);
    // 如果是携带了参数的get请求,那么url就是 /path/to/script?callback=getData
    // 然后将url解析为query字符以及path请求路径,根据查询字符窜拼接script,然后就会将script返回给前端
    // 前端识别到这个函数之后就会执行,也就是通过get请求携带回调函数名称来实现加载调用
    // 类似于这种方式就是jsonp跨域,但不一定一定要回调函数参数
    switch (url){
        case '/api/data':{
            res.write('alert(\'hello\'):getData(\'hello\')');
            break;
        }
        default:{
            res.write('page not found');
            break;
        }
    }
    res.end();
})

server.listen(8080, ()=>{
    console.log('http://localhost:8080');
})

jsonp实现跨域请求的方式就是指定scrpt标签里面的scr的属性来实现跨域,著名的引用就是cdn

③中间件代理实现跨域请求

// npm install http-proxy-middleware, 下载中间键
import * as proxyMiddleware from 'http-proxy-middleware';
import * as http from 'http';


const server = http.createServer((req, res) => {
    const url = req.url;
    // 当访问到我的请求之后,服务器就会执行代理,
    // 所谓的代理就是将ajax以后的请求数据不变,然后将起始前面的域名更改
    // 实现服务器帮我转发请求,服务器拿到数据之后,返回给我的请求,这个就是正向代理
    // 我在浏览器里面输入 http://localhost:8080/ajax?id=name&age=18
    // 实际请求的网址是 https://lady.vip.com/ajax?id=name&age=18
    // 然后将实际请求完成之后的数据发给我请求的网址
    if (/^\/ajax/.test(url)){
        // https://lady.vip.com/ajax, ajax; 上下文接口起始名称
        let proxy = proxyMiddleware.createProxyMiddleware('/ajax', {
            target: 'https://lady.vip.com',
            changeOrigin: true
        })
        proxy(req, res);
    }else {
        console.log('not a proxy');
    }
})


server.listen(8080, ()=>{
    console.log('http://localhost:8080');
})

既然浏览器有同源策略的限制,那么就从服务器那边实现请求,这样就不会有限制,通过这种方式实现的跨域请求就是代理。

④get请求的实现

var http = require('http')
var https = require('https')

// 1、接口 2、跨域
const server = http.createServer((request, response) => {
  var url = request.url.substr(1)

  var data = ''

  response.writeHeader(200, {
    'content-type': 'application/json;charset=utf-8',
    'Access-Control-Allow-Origin': '*'
  })

  https.get(`https://m.lagou.com/listmore.json${url}`, (res) => {

    res.on('data', (chunk) => {
      data += chunk
    })

    res.on('end', () => {
      response.end(JSON.stringify({
        ret: true,
        data
      }))
    })
  })

})

server.listen(8080, () => {
  console.log('localhost:8080')
})

⑤post请求的实现(服务器提交攻击)

const https = require('https')
const querystring = require('querystring')

const postData = querystring.stringify({
  province: '上海',
  city: '上海',
  district: '宝山区',
  address: '同济支路199号智慧七立方3号楼2-4层',
  latitude: 43.0,
  longitude: 160.0,
  message: '求购一条小鱼',
  contact: '13666666',
  type: 'sell',
  time: 1571217561
})

const options = {
  protocol: 'https:',
  hostname: 'ik9hkddr.qcloud.la',
  method: 'POST',
  port: 443,
  path: '/index.php/trade/add_item',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Length': Buffer.byteLength(postData)
  }
}

function doPost() {
  let data

  let req = https.request(options, (res) => {
    res.on('data', chunk => data += chunk)
    res.on('end', () => {
      console.log(data)
    })
  })

  req.write(postData)
  req.end()
}

// setInterval(() => {
//   doPost()
// }, 1000)

⑥server端的基本搭建

import * as http from 'http';
import * as querystring from 'querystring';

const server = http.createServer((req, res) => {
    // 获取请求地址
    const url = req.url;
    // console.log(url);   // --> /
    let data = '';
    req.on('data', chunk => {
        data += chunk;
        // 如果是post请求:并且是原生表单的形式 x-www-form-urlencoded, 提交的数据
        // 那么返回的形式为 data = id=33&name=tom
    })
    req.on('end', () => {

        console.log(JSON.stringify(querystring.parse(data)));
        // {"id":"33","name":"tom"}

        // 返回数据, 返回的状态码只是一个数字而已,具体返回什么还是要看自己
        res.writeHead(200, {
            'content-type': 'application/json;charset=utf8'
            // text/plain : 表示的就是一个纯文本,
            // application/json;charset=utf8 : 表示json
            // text/html : 表示html
        });
        // 将数据发送给前端
        res.write(JSON.stringify({x: url}));
        res.end();
    })
})

server.listen(8080, () => {
    console.log('http://localhost:8080');
})

// node 浏览器调试模式
// node --inspect --inspect-brk 文件名.js

// node 进程管理工具
// supervisor
// nodemon
// forever
// pm2

// 注意,必须是指定被执行文件的路径,下执行才会有效
// 当代码改变时,会从新启动服务器
// Usage: nodemon [options] [script.js] [args]
// 这里用的是nodemon, 安装完成之后执行命令 nodemon 文件名.js
// 然后就会监视进程

此时用到的模块为nodemon,是专门监视文件变化的一个第三方模块,当文件变化时,会重新启动文件服务,用法:odemon server.js;

⑦爬虫的简单实现

import * as http from 'http';
import * as https from 'https';
// 下载cheerio, 可以将text/plain/转化为虚拟html,dom
import * as cheerio from 'cheerio';

function filterData(data){
    const $ = cheerio.load(data);
    $('.section-item-box p').each((index, ele)=>{
        console.log($(ele).text());
    })
}


const server = http.createServer((req, res) => {
    let data = '';
    https.get('https://www.meizu.com', result=>{
        // 拿到数据
        result.on('data', chunk=>{
            data += chunk;
        })
        // 分析数据, 这里拿到的是一个html文本文档
        result.on('end', ()=>{
            filterData(data);
        })
    })
})

server.listen(8080, ()=>{
    console.log('http://localhost:8080')
})

六、内置模块events事件绑定于提交

import * as events from 'events';

class MyEventEmitter extends events.EventEmitter {
}

const event = new MyEventEmitter();

// 绑定事件
event.on('play', value=>{
    console.log(value);
})

// 执行事件, 每一个事件可以从重复定义,然后提交一次就会执行两次
event.emit('play', 'movie');



七、控制台输入模块readline

import * as readline from 'readline';

// readline 逐行读写

// 用于控制台输入输出
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.question('你好啊,好啊?', answer => {
    console.log('thanks for your:' + answer);
    rl.close();
})

八、加密模块crypto

import * as crypto from 'crypto';

const password = '1234alex';

const hash = crypto
// 指定加密方式
    .createHash('sha1')
// 指定加密的字符与编码格式
    .update(password, 'utf-8')
// 指定编码之后的输出类型,这里时十六进制
    .digest('hex');

console.log(hash);

九、server服务器以及路由与静态文件

文件目录树:在windows下输出目录树有两种方式,第一种执行‘tree'返回的是该目录下的所有的文件夹树结构,如果是'tree /f'表示输出包括扩展名的文件,如果再在后面添加’>tree.txt'表示输出到当前目录的tree.txt文件,如果没有的话就会创建,完整的输入为'tree /f >tree.txt';

// 文件的目录树结构

D:.
│  fileStatic.js
│  server.js
│  tree.txt
│  
└─public
    │  index.html
    │  
    ├─css
    │      index.css
    │      
    ├─imgs
    │      img.png
    │      
    └─script
            common.js
            

fileStatic.js

import * as path from 'path';
// 获取文件的类型,例如 .txt 返回的是 text/plain

export function readStaticFile(staticFilePath, fileHandle) {
    let ext = path.parse(staticFilePath).ext;
    // 这里自己写一个
    let map = new Map([
        ['.html', 'text/html'],
        ['.txt', 'text/plain'],
        ['.js', 'application/x-javascript'],
        ['.css', 'text/css'],
        ['.png', 'image/png']]);
    // 如果是一个文件的话
    if (map.has(ext)){
        // 如果是文件类型但是又不是文件
        try {
            return [fileHandle.readFileSync(staticFilePath), map.get(ext)];
        }catch (e){
            return ['

404 Not Found File

', map.get('.html')]; } }else { // 如果不是文件类型但是又是一个文件夹 try { return [fileHandle.readFileSync(path.join(staticFilePath, 'index.html')), map.get('.html')]; }catch (e){ return ['

404 Not Found File

', map.get('.html')]; } } }

server.js

import * as http from 'http';
import * as path from 'path';
import {readStaticFile} from "./fileStatic.js";
import * as fs from 'fs';


const server = http.createServer((req, res) => {
    let urlString = req.url;
    // 返回的是一个跟路径,就是 当前文件的目录
    const __dirname = path.resolve();
    // 拼接文件路径
    const publicFilePath = path.join(__dirname, 'public', urlString);
    // 判断当前是否存在路径
    if (fs.existsSync(publicFilePath)){
        let [fileData, fileType] = readStaticFile(publicFilePath, fs);
        res.writeHead(200, {
            'content-type': fileType
        })
        res.write(fileData);
        res.end();
    }else {
        res.write('nono');
    }
    res.end();
})


server.listen(8080, ()=>{
    console.log('http://localhost:8080');
    // http://localhost:8080, 之后的都算是url路径

})

index.html




    
    Title
    



hello world

img
在自己的文件下 ./ 表示当前文件所在的路径, 在网站中, .\表示域名下的路径
例如,自己文件夹写的是:./script/common.js
实际请求中:http://localhost:8080/script/common.js

common.js ---> 里面什么都没有

index.css ---> 随便写的一个样式

h3{
    background-color: pink;
}

 十、express框架的基本使用

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

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

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

十一、express中的路由

①路由方法

// GET method route
// 对网站首页的访问返回 "Hello World!" 字样
app.get('/', function (req, res) {
  res.send('Hello World!')
})

// 网站首页接受 POST 请求
app.post('/', function (req, res) {
  res.send('Got a POST request')
})

// /user 节点接受 PUT 请求
app.put('/user', function (req, res) {
  res.send('Got a PUT request at /user')
})

// /user 节点接受 DELETE 请求
app.delete('/user', function (req, res) {
  res.send('Got a DELETE request at /user')
})

/*
app.all() 是一个特殊的路由方法,没有任何 HTTP 方法与其对应,它的作用是对于一个路径上的所有请求加载中间件。
*/
app.all('/secret', function (req, res, next) {
  console.log('Accessing the secret section ...')
  next(); // pass control to the next handler
})

 ②路由路径

// 匹配根路径的请求
app.get('/', function (req, res) {
  res.send('root');
});

// 匹配 /about 路径的请求
app.get('/about', function (req, res) {
  res.send('about');
});

// 匹配 /random.text 路径的请求
app.get('/random.text', function (req, res) {
  res.send('random.text');
});

// 匹配 acd 和 abcd
app.get('/ab?cd', function(req, res) {
  res.send('ab?cd');
});

// 匹配 abcd、abbcd、abbbcd等
app.get('/ab+cd', function(req, res) {
  res.send('ab+cd');
});

// 匹配 abcd、abxcd、abRABDOMcd、ab123cd等
app.get('/ab*cd', function(req, res) {
  res.send('ab*cd');
});

// 匹配 /abe 和 /abcde
app.get('/ab(cd)?e', function(req, res) {
 res.send('ab(cd)?e');
});

// 匹配任何路径中含有 a 的路径:
app.get(/a/, function(req, res) {
  res.send('/a/');
});

// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等
app.get(/.*fly$/, function(req, res) {
  res.send('/.*fly$/');
});

③路由句柄

// 使用一个回调函数处理路由:
app.get('/example/a', function (req, res) {
  res.send('Hello from A!');
});

// 使用多个回调函数处理路由(记得指定 next 对象):
app.get('/example/b', function (req, res, next) {
  console.log('response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from B!');
});

// 使用回调函数数组处理路由:
var cb0 = function (req, res, next) {
  console.log('CB0')
  next()
}

var cb1 = function (req, res, next) {
  console.log('CB1')
  next()
}

var cb2 = function (req, res) {
  res.send('Hello from C!')
}

app.get('/example/c', [cb0, cb1, cb2])

// 混合使用函数和函数数组处理路由:
var cb0 = function (req, res, next) {
  console.log('CB0')
  next()
}

var cb1 = function (req, res, next) {
  console.log('CB1')
  next()
}

app.get('/example/d', [cb0, cb1], function (req, res, next) {
  console.log('response will be sent by the next function ...')
  next()
}, function (req, res) {
  res.send('Hello from D!')
})

 十二、express中响应方法response

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

十三、路由模块express.Router

①创建路由模块

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

// 该路由使用的中间件
router.use(function timeLog(req, res, next) {
  console.log('Time: ', Date.now());
  next();
});
// 定义网站主页的路由
router.get('/', function(req, res) {
  res.send('Birds home page');
});
// 定义 about 页面的路由
router.get('/about', function(req, res) {
  res.send('About birds');
});

module.exports = router;

②加载路由模块(使用中间件)

var birds = require('./birds')
...
app.use('/birds', birds)

// 应用即可处理发自 /birds 和 /birds/about 的请求,并且调用为该路由指定的 timeLog 中间件。

十四、通过express中间件实现静态路由

/*
将静态资源文件所在的目录作为参数传递给 express.static 中间件
就可以提供静态资源文件的访问了。例如,假设在 public 目录放置了
图片、CSS 和 JavaScript 文件,你就可以:
*/
app.use(express.static('public'))

/*
http://localhost:3000/images/kitten.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js
http://localhost:3000/images/bg.png
http://localhost:3000/hello.html
*/

// 如果你的静态资源存放在多个目录下面,你可以多次调用 express.static 中间件:
app.use(express.static('public'))
app.use(express.static('files'))

/*
如果你希望所有通过 express.static 访问的文件都存放
在一个“虚拟(virtual)”目录(即目录根本不存在)下面,
可以通过为静态资源目录指定一个挂载路径的方式来实现,如下所示:
*/
app.use('/static', express.static('public'))
/*
http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/images/bg.png
http://localhost:3000/static/hello.html
*/

十五、express中的中间件的使用

①应用级中间件

应用级中间件绑定到 app 对象 使用 app.use() 和 app.METHOD(), 其中, METHOD 是需要处理的 HTTP 请求的方法,例如 GET, PUT, POST 等等,全部小写。例如:

var app = express()

// 没有挂载路径的中间件,应用的每个请求都会执行该中间件
app.use(function (req, res, next) {
  console.log('Time:', Date.now())
  next()
})

// 挂载至 /user/:id 的中间件,任何指向 /user/:id 的请求都会执行它
app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method)
  next()
})

// 路由和句柄函数(中间件系统),处理指向 /user/:id 的 GET 请求
app.get('/user/:id', function (req, res, next) {
  res.send('USER')
})

下面这个例子展示了在一个挂载点装载一组中间件。

// 一个中间件栈,对任何指向 /user/:id 的 HTTP 请求打印出相关信息
app.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl)
  next()
}, function (req, res, next) {
  console.log('Request Type:', req.method)
  next()
})

作为中间件系统的路由句柄,使得为路径定义多个路由成为可能。在下面的例子中,为指向 /user/:id 的 GET 请求定义了两个路由。第二个路由虽然不会带来任何问题,但却永远不会被调用,因为第一个路由已经终止了请求-响应循环。

// 一个中间件栈,处理指向 /user/:id 的 GET 请求
app.get('/user/:id', function (req, res, next) {
  console.log('ID:', req.params.id)
  next()
}, function (req, res, next) {
  res.send('User Info')
})

// 处理 /user/:id, 打印出用户 id
app.get('/user/:id', function (req, res, next) {
  res.end(req.params.id)
})

如果需要在中间件栈中跳过剩余中间件,调用 next('route') 方法将控制权交给下一个路由。 注意: next('route') 只对使用 app.VERB() 或 router.VERB() 加载的中间件有效。

// 一个中间件栈,处理指向 /user/:id 的 GET 请求
app.get('/user/:id', function (req, res, next) {
  // 如果 user id 为 0, 跳到下一个路由
  if (req.params.id == 0) next('route')
  // 否则将控制权交给栈中下一个中间件
  else next() //
}, function (req, res, next) {
  // 渲染常规页面
  res.render('regular')
});

// 处理 /user/:id, 渲染一个特殊页面
app.get('/user/:id', function (req, res, next) {
  res.render('special')
})

②路由中间件

路由级中间件和应用级中间件一样,只是它绑定的对象为 express.Router()。

var router = express.Router()

路由级使用 router.use() 或 router.VERB() 加载。

上述在应用级创建的中间件系统,可通过如下代码改写为路由级:

var app = express()
var router = express.Router()

// 没有挂载路径的中间件,通过该路由的每个请求都会执行该中间件
router.use(function (req, res, next) {
  console.log('Time:', Date.now())
  next()
})

// 一个中间件栈,显示任何指向 /user/:id 的 HTTP 请求的信息
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl)
  next()
}, function (req, res, next) {
  console.log('Request Type:', req.method)
  next()
})

// 一个中间件栈,处理指向 /user/:id 的 GET 请求
router.get('/user/:id', function (req, res, next) {
  // 如果 user id 为 0, 跳到下一个路由
  if (req.params.id == 0) next('route')
  // 负责将控制权交给栈中下一个中间件
  else next() //
}, function (req, res, next) {
  // 渲染常规页面
  res.render('regular')
})

// 处理 /user/:id, 渲染一个特殊页面
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id)
  res.render('special')
})

// 将路由挂载至应用
app.use('/', router)

③错误处理中间件

错误处理中间件有 4 个参数,定义错误处理中间件时必须使用这 4 个参数。
即使不需要 next 对象,也必须在签名中声明它,否则中间件会被识别为一
个常规中间件,不能处理错误。
错误处理中间件和其他中间件定义类似,只是要使用 4 个参数,而不是 3 个,
其签名如下: (err, req, res, next)。
app.use(function(err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

④内置中间件

/*
从 4.x 版本开始,, Express 已经不再依赖 Connect 了。除了 express.static,
 Express 以前内置的中间件现在已经全部单独作为模块安装使用了。请参考 中间件列表。

express.static(root, [options])

express.static 是 Express 唯一内置的中间件。它基于 serve-static,负责在 Express 
应用中提托管静态资源。

参数 root 指提供静态资源的根目录。

可选的 options 参数拥有如下属性。
属性	描述	类型	缺省值
dotfiles	是否对外输出文件名以点(.)开头的文件。可选值为 “allow”、
“deny” 和 “ignore”	String	“ignore”
etag	是否启用 etag 生成	Boolean	true
extensions	设置文件扩展名备份选项	Array	[]
index	发送目录索引文件,设置为 false 禁用目录索引。	Mixed	“index.html”
lastModified	设置 Last-Modified 头为文件在操作系统上的最后修改日期。
可能值为 true 或 false。	Boolean	true
maxAge	以毫秒或者其字符串格式设置 Cache-Control 头的 max-age 属性。	Number	0
redirect	当路径为目录时,重定向至 “/”。	Boolean	true
setHeaders	设置 HTTP 头以提供文件的函数。	Function
*/
var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now())
  }
}

app.use(express.static('public', options))

十六、express中使用art-template

需要在应用中进行如下设置才能让 Express 渲染模板文件:

views, 放模板文件的目录,比如: app.set('views', './views')
view engine, 模板引擎,比如: app.set('view engine', 'ejs')

1、Install

npm install --save art-template
npm install --save express-art-template

2、Example

var express = require('express')
var app = express()

// view engine setup
app.engine('art', require('express-art-template'))
app.set('view', {
    debug: process.env.NODE_ENV !== 'production'
})
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'art')

// routes
app.get('/', function (req, res) {
    res.render('index.art', {
        user: {
            name: 'aui',
            tags: ['art', 'template', 'nodejs']
        }
    })
})
const express = require('express');
const body_parser = require('body-parser');
const art_template = require('express-art-template');
const path = require('path');


const app = express();
const router = require('./router/index.js');

// 这里可以通过使用body-parser 来解析 post 中的 req.body,
// 专门用来解析 application/x-www-form-urlencoded 发送的表单数据
app.use(body_parser.urlencoded({extended: false}));
// 解析json字符窜
app.use(body_parser.json());
// app.use((req, res, next) => {
//     // do something here
//     // 默认匹配 为 *
// })

// 配置静态资源中间键, 内置中间件
app.use(express.static('./public'));

// npm i art-template express-art-template
// 配置模板渲染引擎, view和views都是默认的key
app.engine('html', art_template);
// 设置配置环境
app.set('view options', {
    debug: process.env.NODE_ENV !== 'production'
})
// 设置模板引擎寻找文件的路径
app.set('views', path.join(__dirname, 'template'));
// 下面的第二个参数 art 为 template 模板下的扩展名
app.set('view engine', 'html');


// 可以使用多个初级路由筛选器,类似于python中的include urls
app.use('/', router);

app.listen(8080, () => {
    console.log('http://localhost:8080');
})

十七、JsonWebToken与加密算法

const jwt = require('jsonwebtoken');

const fs = require("fs");

let payLoad = {data: 'this is a data'};
let privateKey = fs.readFileSync('./key/rsa_private_key.pem', 'utf-8');
const token = jwt.sign(payLoad, privateKey, {algorithm:'RS256'});

// console.log(token)

let publicKey = fs.readFileSync('./key/rsa_public_key.pem', 'utf-8');
let res = jwt.verify(token, publicKey, {algorithm: 'RS256'});
console.log(res);

十八、net-socket网络通信

①基本用法

client.js

const net = require('net');

const client = net.createConnection({port: 6527}, ()=>{
    console.log('connected to server');
    client.write('world!\r\n');
})


client.on('data', data => {
    console.log(data.toString());
    client.end();
})

client.on('end', ()=>{
    console.log('disconnected from server');
})

server.js

const net = require('net');

const server = net.createServer(socket => {
    socket.write('hello world\r\n');
})

server.on('data', (data)=>{
    console.log(data.toString());
})

server.on('error', err=>{
    console.log(err);
})

server.listen('6527',()=>{
    console.log(server.address())
})

②基本用法进阶

client.js

const net = require('net');
const readline = require('readline');

let port = 8080;
let host = 'localhost';

let socket = new net.Socket();

socket.setEncoding('utf-8');

socket.connect(port, host, ()=>{
    socket.write('hello');
    say();
})

socket.on('data', data=>{
    console.log(data.toString());
})

// 用于控制台输入输出
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

function say(){
    rl.question('请输入:\n', answer => {
        if (answer !== 'bye'){
            socket.write(answer + '\n');
        }else {
            socket.destroy();
            rl.close();
        }
    })
}

server.js

const net = require('net');

const server = new net.createServer();

let clients = {};
let clientName = 0;

function broadcast(socket, data){
    for (let client in clients){
        // 聊天室
        // 相当于给群里的每一个人都发一次消息,
        clients[client].write(socket.name + '说:' + data);
    }
}

server.on('connection', socket => {
    // 将创建的连接保存到数组
    socket.name = ++clientName;
    clients[socket.name] = socket;

    // 绑定事件
    socket.on('data', data => {
        broadcast(socket, data.toString());
    })

    socket.on('error', err => {
        console.log('client error' + err);
        socket.end();
    })

    socket.on('close', hadError => {
        delete clients[clients.name];
        console.log(socket.name + '下线了');
    })
})

server.listen({host: 'localhost', port:8080}, ()=>{
    console.log('服务器端已经启动');
})

十九、web-socket通信

目录树


D:.
│  fileStatic.js
│  server.js
│  socketServer.js
│  tree.txt
│  
└─public
    │  index.html
    │  
    ├─css
    │      index.css
    │      
    ├─imgs
    │      img.png
    │      
    └─script
            WsClient.js
            
fileStatic.js
const path = require('path');
// 获取文件的类型,例如 .txt 返回的是 text/plain

function readStaticFile(staticFilePath, fileHandle) {
    let ext = path.parse(staticFilePath).ext;
    // 这里自己写一个
    let map = new Map([
        ['.html', 'text/html'],
        ['.txt', 'text/plain'],
        ['.js', 'application/x-javascript'],
        ['.css', 'text/css'],
        ['.png', 'image/png']]);
    // 如果是一个文件的话
    if (map.has(ext)){
        // 如果是文件类型但是又不是文件
        try {
            return [fileHandle.readFileSync(staticFilePath), map.get(ext)];
        }catch (e){
            return ['

404 Not Found File

', map.get('.html')]; } }else { // 如果不是文件类型但是又是一个文件夹 try { return [fileHandle.readFileSync(path.join(staticFilePath, 'index.html')), map.get('.html')]; }catch (e){ return ['

404 Not Found File

', map.get('.html')]; } } } exports.readStaticFile = readStaticFile;
server.js
const http = require('http');
const path = require('path');
const {readStaticFile} = require('./fileStatic.js');
const fs = require('fs');


const server = http.createServer((req, res) => {
    let urlString = req.url;
    // 返回的是一个跟路径,就是 当前文件的目录
    const __dirname = path.resolve();
    // 拼接文件路径
    const publicFilePath = path.join(__dirname, 'public', urlString);
    // 判断当前是否存在路径
    if (fs.existsSync(publicFilePath)){
        let [fileData, fileType] = readStaticFile(publicFilePath, fs);
        res.writeHead(200, {
            'content-type': fileType
        })
        res.write(fileData);
        res.end();
    }else {
        res.write('nono');
    }
    res.end();
})


server.listen(8080, ()=>{
    console.log('http://localhost:8080');
    // http://localhost:8080, 之后的都算是url路径

})
socketServer.js
// 下载 ws 包,npm i ws
const websocket = require('ws');

const wss = new websocket.Server({port:8000});

wss.on('connection', function connection(ws){
    ws.on('open', function close(){
        console.log('connected !!!');
        ws.send('hello');
    })

    ws.on('message', function incoming(data){
        wss.clients.forEach(function each(client){
            client.send(data.toString());
        })
    })

    ws.on('close', function close(){
        console.log('disconnected !!!');
    })
})

index.html




    
    
    
    WebSocket
    


交流区


index.css

h3{
    background-color: pink;
}

二十、socketIo模块实现web-socket

D:.
    index.html
    socketServer.js

index.html




  Socket.IO chat
  


     socketServer.js

    const express = require("express");
    const {createServer} = require("http");
    const {Server} = require("socket.io");
    
    const app = express();
    const httpServer = createServer(app);
    const io = new Server(httpServer, { /* options */});
    
    app.get('/', (req, res) => {
        res.sendFile(__dirname + '/index.html');
    });
    
    io.on('connection', (socket) => {
        socket.broadcast.emit('hi');
    });
    
    io.on('connection', (socket) => {
        console.log('a user connected');
        socket.on('disconnect', () => {
            console.log('user disconnected');
        });
    });
    
    io.on('connection', (socket) => {
        socket.on('chat message', (msg) => {
            console.log('message: ' + msg);
        });
    });
    
    io.on('connection', (socket) => {
        socket.on('chat message', (msg) => {
            // io.emit('chat message', msg);
            // socket.broadcast.emit('hi');
            socket.emit('chat message', msg);
        });
    });
    
    
    httpServer.listen(3000, () => {
        console.log('http://localhost:3000')
    });
    

    具体实现可以参照socketio官网:Socket.IO

    你可能感兴趣的:(Javascript,node.js,学习,前端)