1. 创建web服务器
// 创建web服务器
// 用于创建网站服务器的模块
// 引用系统模块
const http = require('http');
// 创建web服务器
// app对象就是网站服务器对象
const app = http.createServer();
// 当客户端发送请求的时候
app.on('request', (req, res) => {
// 响应
res.end('<h1>hi ,user</h1>');
});
// 监听3000端口
app.listen(3000); // 这里的端口号可以随便写,不一定是3000
console.log('服务器已启动,监听3000端口,请访问localhost:3000');
// 在powerShall中输入 nodemon app.js启动服务器,再在浏览器中输入localhost:3000
2. HTTP协议
2.1 http协议的概念
超文本传输协议(http)规定了如何从网站服务器传输超文本到本地浏览器,它是基于客户端服务器架构工作,是客户端(用户)和服务器(网站)请求和应答的标准。
2.2 报文
在HTTP请求和响应的过程中传递的数据块就是报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式
2.3 请求报文
获取请求方式:req.method
- 请求方式(Request Method)
-
GET 请求数据
在浏览器中输入网址使用的是get请求
-
POST 发送数据
如何发送post请求:使用表单方式
<body>
<!--
method: 指定当前表单提交的方式
action:指定当前表单提交的地址
-->
<form method="post" action="http://localhost:3000">
<input type="submit">
</form>
</body>
- 请求地址(Request URL)
// 获取请求地址
// req.url
2.4 响应报文
- HTTP状态
- 200 请求成功
- 404 请求的资源没有被找到
- 500 服务器端错误
- 400 客户端请求有语法错误
3. HTTP请求与响应处理
3.1 请求参数
客户端向服务器发送请求时,有时需要携带一些卡用户信息,客户信息需要通过请求参数的形式传递服务器端,比如登录操作。
3.2 GET请求参数
- 参数被放置在浏览器地址栏中,例如:http://localhost:3000/index?name=zhangshan&&age=19
- 参数获取需要借助系统模块url, url 模块用来处理url地址
// 用于创建网站服务器的模块
// 引用系统模块
const http = require('http');
// 用于处理url地址
const url = require('url');
// 创建web服务器
// app对象就是网站服务器对象
const app = http.createServer();
// 当客户端发送请求的时候
app.on('request', (req, res) => {
console.log(req.url);
// url.parse的参数:1)要解析的url地址 2)将查询参数解析成对象形式
let { query, pathname } = url.parse(req.url, true);
console.log(query.name);
console.log(query.age);
if (pathname == '/index' || pathname == '/') {
return res.end('<h2>欢迎来到首页</h2>');
} else if (pathname == '/list') {
return res.end('welcome to listpage');
} else {
return res.end('no find');
}
});
// 监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问localhost:3000');
// 在powerShall中输入 nodemon app.js启动服务器,再在浏览器中输入localhost:3000
3.3 POST请求参数
- 参数被放置在请求体中进行传输
- 获取POST参数需要使用data事件和end事件
- 使用querystring系统模块将参数转换为对象格式
// 导入系统模块querystring 用于将HTTP参数转换为对象格式
const querystring = require('querystring');
app.on('request', (req, res) => {
let postData = '';
// 监听参数传输事件
// post参数是通过事件的方式接受的
// data: 但请求参数传递的时候触发data事件
// end:当参数传递完成的时候触发end事件
req.on('data', (chunk) => postData += chunk;);
// 监听参数传输完毕事件
req.on('end', () => {
console.log(querystring.parse(postData));
});
});
3.4 路由
http://localhost:3000/index 首页
http://localhost:3000/login 登录页面
路由是指客户端请求地址与服务器端程序代码的对应关系。简单来说,就是请求什么响应什么
// 1. 引入系统模块http
// 2. 创建网站服务器
// 3. 为网站服务器对象添加请求事件
// 4. 实现路由功能
// 1) 获取客户端的请求方式
// 2) 获取客户端的请求地址
const http = require('http');
const url = require('url')
const app = http.createServer();
app.on('request', (req, res) => {
// 获取请求方式
const method = req.method.toLowerCase();
// 获取请求地址
const pathname = url.parse(req.url).pathname;
res.writeHead(200, {
'content-type': 'text/html;charset=utf8'
})
if (method == 'get') {
if (pathname == '/' || pathname == '/index') {
return res.end('欢迎来到首页')
} else if (pathname == '/list') {
return res.end('欢迎来到列表页');
} else {
return res.end('您访问的页面不存在');
}
} else if (method == 'post') {
if (pathname == '/' || pathname == '/index') {
return res.end('欢迎来到首页')
} else if (pathname == '/list') {
return res.end('欢迎来到列表页');
} else {
return res.end('您访问的页面不存在');
}
}
});
app.listen(3000);
console.log('服务器启动成功');
3.5 静态资源
服务器端不需要处理,可以直接响应客户端的资源就是静太资源,例如CSS、JavaScript、image文件。
3.6 动态资源
相同的请求地址不同的响应资源,这种资源就是动态资源。
例如:
http:www.itcast.cn/article?id=1
http:www.itcast.cn/article?id=2
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('mime');
const app = http.createServer();
app.on('request', (req, res) => {
// 获取用户的请求路劲
let pathname = url.parse(req.url).pathname;
pathname = pathname == '/' ? 'default.html' : pathname;
// res.writeHead(200, {
// 'content-type': 'text/html;charset=utf8'
// })
// 将用户的请求路劲转换为实际的服务器硬盘路劲
let realPath = path.join(__dirname, 'public' + pathname);
let type = mime.getType(realPath);
// 读取文件
// 如果文件读取成功 error为空,result 就是文件的内容
// 如果文件读取失败 error里面存储的是失败信息, result为空
fs.readFile(realPath, (error, result) => {
// 文件读取失败
if (error != null) {
res.writeHead(404, {
'content-type': 'text/html;charset=utf8'
})
res.end('文件读取失败');
return;
}
res.writeHead(200, {
'content-type': type
});
res.end(result);
})
});
app.listen(3000);
console.log('服务器启动成功');
4. Node.js异步编程
4.1 同步API,异步API
同步API:只有当前API执行完成后,才能继续执行下一个API
异步API:当前API的执行不会阻塞后续代码的执行
console.log('before');
setTimeout(function() {
console.log('last');
}, 2000);
console.log('after');
// 输出结果为:before after last
4.2 同步API,异步API的区别(获取返回值)
同步API可以返回值中拿到API执行的结果,但是异步API是不可以的
// 同步
function sum(n1, n2) {
return n1 + n2;
};
console.log(sum(2, 3));
// 返回值为:5
// 异步
function getMsg() {
setTimeout(function() {
return {
msg: 'hello node.js'
}
}, 2000);
// 由于在执行getMsg时,遇到定时器所以直接跳到定时器的后面,添加return undefined执行,将其值返回给msg
// return undefined
}
const msg = getMsg();
console.log(msg);
// 返回值为undefined
4.3 回调函数
自己定义函数让别人去调用
// 异步
function getMsg(callback) {
setTimeout(function() {
callback({
msg: 'hello node.js'
});
}, 2000);
}
getMsg(function(date) {
console.log(date);
});
4.4 同步API,异步API的区别(代码执行顺序)
同步API从上到下依次执行,前面代码会阻塞后面代码的执行
for (var i = 0; i < 10000; i++) {
console.log(i);
}
console.log('执行后面的代码');
异步API不会等待API执行完成后再向下执行代码
console.log('代码开始执行');
setTimeout(() => {
console.log('2秒后执行的代码');
}, 2000);
setTimeout(() => {
console.log('0秒后执行的代码');
}, 0);
console.log('代码结束执行');
// 代码的执行结果
// 代码开始执行
// 代码结束执行
// 0秒后执行的代码
// 2秒后执行的代码
4.5 代码执行顺序分析
console.log('代码开始执行');
setTimeout(() => {
console.log('2秒后执行的代码');
}, 2000);
setTimeout(() => {
console.log('"0秒"后执行的代码');
}, 0);
console.log('代码结束执行');
4.6 Node.js中的异步API
const fs = require('fs');
fs.readFile('1.txt', 'utf-8', (err, result1) => {
console.log(result1);
fs.readFile('2.txt', 'utf-8', (err, result2) => {
console.log(result2);
fs.readFile('3.txt', 'utf-8', (err, result3) => {
console.log(result3);
})
})
})
如果异步API后面代码的执行依赖当前异步API的执行结果,但实际上后续代码在执行的时候异步API还没有返回结果,这个问题怎么解决呢?
const fs = require('fs');
let promise = new Promise((resolve, reject) => {
fs.readFile('100.txt', 'utf8', (err, result) => {
if (err != null) {
return reject(err);
} else {
return resolve(result);
}
});
});
promise.then((result) => {
console.log(result);
})
.catch((err) => {
console.log(err);
})
4.7 Promise
Promise出现的目的是解决Node.js异步编程中回调地狱的问题。
4.8 异步函数
异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。
const fn = async () => {}
async function fn() {}
async关键字
- 普通函数定义前家async关键字 普通函数变成异步函数
- 异步函数默认返回promise对象
- 在一部函数内部使用return关键字进行结果返回,结果会包裹的promise对象中return关键字代替了resolve方法
- 在一部函数内部使用throw关键字抛出程序异常
- 调用异步函数再链式调用then方法获取函数执行结果
- 调用异步函数再链式调用catch方法获取异步函数执行的错误信息
await
- await关键字只能出现在异步函数中
- await promise await后面只能写promise对象 写其他类型的API是不不可以的
- await关键字可是暂停异步函数向下执行 直到promise返回结果
// 1. 在普通函数定义的前面加上async关键字 普通函数就变成了异步函数
// 2. 异步函数默认的返回值使promise对象
// 3. 在异步函数内保护使用throw关键字进行错误抛出
// 4. await关键字
// 它只能出现在异步函数中
// awite后面跟promise对象 它可以暂停异步函数的执行,等待promise对象返回后再向下执行函数
// async function fn() {
// throw '发生了一些错误';
// return 123;
// }
// // console.log(fn());
// fn().then(function(data) {
// console.log(data);
// })
// .catch(function(err) {
// console.log(err);
// })
async function p1() {
return 'p1';
}
async function p2() {
return 'p2';
}
async function p3() {
return 'p3';
}
async function run() {
let r1 = await p1();
let r2 = await p2();
let r3 = await p3();
console.log(r1);
console.log(r2);
console.log(r3);
}
run();
const fs = require('fs');
const promisify = require('util').promisify;
const readFile = promisify(fs.readFile);
async function run() {
let r1 = await readFile('1.txt', 'utf8');
let r2 = await readFile('2.txt', 'utf8');
let r3 = await readFile('3.txt', 'utf8');
console.log(r1);
console.log(r2);
console.log(r3);
}
run();
4.9 Node.js全局对象global
在浏览器中全局对象是window, 在Node中全局对象是global。
Node中全局对象有以下方法, 在任何地方使用, global可以省略。
-
- console.log(); 在控制台中输出
-
- setTimeout() 设置超时定时器
-
- clearTimeout() 清除超时时定时器
-
- setInterval() 设置间歇定时器
-
- clearInterval() 清除间歇定时器