# 路由
在 BS
架构中,路由的概念都是一样的,可理解为根据客户端请求的 URL
映射到不同的方法实现,更多的一般都是针对 URL
中的路径,或者是参数,又或者是锚点这些信息进行映射。
# 场景
- 注册一个账户 --> [post] --> http://localhost:88/register
- 注册成功的情况下跳转到登录界面进行登录 --> [post] --> http://localhost:88/login
- 登录成功进行获取学生信息 --> [get] --> http://localhost:88/students
- 同时可以获取订单信息 --> [get] --> http://localhost:88/orders
- 如何访问不存在的路由则抛出错误信息。
const http = require('http')
const url = require('url')
const qs = require('querystring');
const util = require('util');
http.createServer((request, response) => {
let urlObj = url.parse(request.url, true);
let pathname = urlObj.pathname;
let method = request.method.toUpperCase();
let params = urlObj.query;
if(method == 'POST'){
let postData = '';
request.on('data', (_data) => {
postData += '_data';
})
request.on('end', () => {
postData = qs.parse(postData);
let result = {};
switch(pathname){
case '/login':
//连接数据库,实现登陆逻辑
result = {status: true};
break;
case '/register':
//连接数据库,实现注册逻辑
result = {status: true};
break;
default :
result = {status: false, message: '没有对应的请求'};
break;
}
response.end(util.inspect(result))
})
} else {
let result = {};
switch(pathname){
case '/students':
//连接数据库,获取学生信息
result = {status: true, data: [], params};
break;
case '/orders':
//连接数据库,获取订单信息
result = {status: true, data: [], params};
break;
default :
result = {status: false, message: '没有对应的请求', params};
break;
}
response.end(util.inspect(result))
}
}).listen(88)
# Demo
const http = require('http')
const url = require('url')
const qs = require('querystring');
const util = require('util');
http.createServer((request, response) => {
let urlObj = url.parse(request.url, true);
let pathname = urlObj.pathname;
let method = request.method.toUpperCase();
let params = urlObj.query;
if(method == 'POST'){
let postData = '';
request.on('data', (_data) => {
postData += '_data';
})
request.on('end', () => {
postData = qs.parse(postData);
let result = {};
switch(pathname){
case '/login':
//连接数据库,实现登陆逻辑
result = {status: true};
break;
case '/register':
//连接数据库,实现注册逻辑
result = {status: true};
break;
default :
result = {status: false, message: '没有对应的请求'};
break;
}
response.end(util.inspect(result))
})
} else {
let result = {};
switch(pathname){
case '/students':
//连接数据库,获取学生信息
result = {status: true, data: [], params};
break;
case '/orders':
//连接数据库,获取订单信息
result = {status: true, data: [], params};
break;
default :
result = {status: false, message: '没有对应的请求', params};
break;
}
response.end(util.inspect(result))
}
}).listen(88)
# request
一个第三方的模块,可用于发起 http 或 https 请求,可理解成服务端的 ajax 请求。可用于代简单的服务器代理,用法和 ajax 类似。
在使用前需要先安装 npm install request --save
# GET 请求
request.get('https://cnodejs.org/api/v1/topics?page=1&limit=10', (error, response, body) => {
console.log(body)
})
//or
request('https://cnodejs.org/api/v1/topics?page=1&limit=10', (error, response, body) => {
console.log(body)
})
# 多参数设置
exports.get = function(url, options) {
options = options || {};
var httpOptions = {
url: url,
method: 'get',
timeout: options.timeout || 10000,
headers: options.headers || default_post_headers,
proxy: options.proxy || '',
agentOptions: agentOptions,
params: options.params || {}
}
if(options.userAgent){
httpOptions.headers = {
'User-Agent': userAgents[options.userAgent],
}
}
try{
request.get(httpOptions, function(err, res, body) {
if (err) {
options.callback({status: false, error: err})
} else {
options.callback({status: res.statusCode == 200, error: res, data: body})
}
}).on('error', logger.error);
} catch(err){
console.log('http error');
}
}
# POST 请求
request支持application/x-www-form-urlencoded和multipart/form-data实现表单上传。
# application/x-www-form-urlencoded (URL-Encoded Forms)
request.post('http://service.com/upload', {form:{key:'value'}})
// or
request.post('http://service.com/upload').form({key:'value'})
// or
request.post({url:'http://service.com/upload', form: {key:'value'}}, function(err,httpResponse,body){ /* ... */ })
# multipart/form-data (Multipart Form Uploads)
var formData = {
// Pass a simple key-value pair
my_field: 'my_value',
// Pass data via Buffers
my_buffer: new Buffer([1, 2, 3]),
// Pass data via Streams
my_file: fs.createReadStream(__dirname + '/unicycle.jpg'),
// Pass multiple values /w an Array
attachments: [
fs.createReadStream(__dirname + '/attachment1.jpg'),
fs.createReadStream(__dirname + '/attachment2.jpg')
],
// Pass optional meta-data with an 'options' object with style: {value: DATA, options: OPTIONS}
// Use case: for some types of streams, you'll need to provide "file"-related information manually.
// See the `form-data` README for more information about options: https://github.com/form-data/form-data
custom_file: {
value: fs.createReadStream('/dev/urandom'),
options: {
filename: 'topsecret.jpg',
contentType: 'image/jpeg'
}
}
};
request.post({url:'http://service.com/upload', formData: formData}, function optionalCallback(err, httpResponse, body) {
if (err) {
return console.error('upload failed:', err);
}
console.log('Upload successful! Server responded with:', body);
});
# 常用多参数设置
exports.form_post = function(url, postdata, options) {
// console.log(`${moment().format()} HttpFormPost: ${url}`)
return new Promise((resolve, reject) => {
options = options || {};
var httpOptions = {
url: url,
form: postdata,
method: 'post',
timeout: options.timeout || 3000,
headers: options.headers || default_post_headers,
proxy: options.proxy || '',
agentOptions: agentOptions
};
request(httpOptions, function(err, res, body) {
if (err) {
reject(err);
} else {
if (res.statusCode == 200) {
resolve(body);
} else {
reject(res.statusCode);
}
}
}).on('error', logger.error);
});
};
# 流
request('http://img.zcool.cn/community/018d4e554967920000019ae9df1533.jpg@900w_1l_2o_100sh.jpg').pipe(fs.createWriteStream('test.png'))
request('https://cnodejs.org/api/v1/topics?page=1&limit=10').pipe(fs.createWriteStream('cnodejs.json'))
# http 小爬虫
又被称为网页蜘蛛,网络机器人,主要是在服务端去请求外部的 url 拿到对方的资源,然后进行分析并抓取有效数据。
这里用 request 实现一个简单的图片抓取的小爬虫
const request = require('request');
const fs = require('fs');
const cheerio = require('cheerio');
request('http://www.lanrentuku.com/', (error, response, body) => {
let $ = cheerio.load(body);
$('img', '.in-ne').each((i, e) => {
let src = $(e).attr('src');
let name = src.substr(src.lastIndexOf('/') + 1);
request(src).pipe(fs.createWriteStream(name))
})
})
# async
Node.js 是一个异步机制的服务端语言,在大量异步的场景下需要按顺序执行,那正常做法就是回调嵌套回调,回调嵌套太多的问题被称之回调地狱。
Node.js 为解决这一问题推出了异步控制流 ———— Async
# Async/Await
Async/Await 就 ES7 的方案,结合 ES6 的 Promise 对象,使用前请确定 Node.js 的版本是 7.6 以上。
Async/await的主要益处是可以避免回调地狱(callback hell),且以最接近同步代码的方式编写异步代码。
# 基本规则
- async 表示这是一个async函数,await只能用在这个函数里面。
- await 表示在这里等待promise返回结果了,再继续执行。
- await 后面跟着的应该是一个promise对象
# 对比使用
场景:3秒后返回一个值
# 原始时代
let sleep = (time, cb) => {
setTimeout(() => {
cb('ok');
}, 3000);
}
let start = () => {
sleep(3000, (result) => {
console.log(result)
})
}
start()
# Promise 时代
let sleep = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok') ;
}, time);
})
}
let start = () => {
sleep(3000).then((result) => {
console.log(result)
})
}
start()
# Async/Await 时代
let sleep = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok') ;
}, time);
})
}
let start = async () => {
let result = await sleep(3000);
console.log(result)
}
start();
# 捕捉错误
let sleep = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('error') ;
}, time);
})
}
let start = async () => {
try{
let result = await sleep(3000);
console.log(result)
} catch(err) {
console.log('error')
}
}
start();
# 在循环中使用
let sleep = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok') ;
}, time);
})
}
let start = async () => {
for (var i = 1; i <= 3; i++) {
console.log(`当前是第${i}次等待..`);
await sleep(1000);
}
}
start();
# 爬虫中使用
const request = require('request');
const fs = require('fs');
const cheerio = require('cheerio');
let spider = (url) => {
return new Promise((resolve, reject) => {
request(url, (error, response, body) => {
resolve(body);
})
})
}
let start = async () => {
let dom = await spider('http://www.lanrentuku.com/');
let $ = cheerio.load(dom);
$('img', '.in-ne').each((i, e) => {
let src = $(e).attr('src');
let name = src.substr(src.lastIndexOf('/') + 1);
request(src).pipe(fs.createWriteStream(name))
})
}
start();