- 服务端基础概念
- 网站组成
- URL
- 开发过程中客户端和服务器端说明
- 创建web服务器
- HTTP协议
- HTTP请求与响应处理
- Node.js异步编程
- 同步API 异步API
- Node.js全局对象global
- 总结
服务端基础概念
网站组成
网站应用程序主要分为两大部分:客户端和服务器端
URL
URL统一资源定位符,又叫URL,专为标识Internet网上资源位置而设的一种编址方式,就是我们平时所说的网页地址;
URL 的组成:
传输协议://服务器IP或域名:端口/资源所在位置标识
http://www.itcast.cn/news/20181018/09152238514.html http:超文本传输协议,提供了一种发布和接收HTML页面的方法;
开发过程中客户端和服务器端说明
开发阶段客户端和服务器端使用同一台电脑,自己电脑地址 localhost
创建web服务器
创建web服务器代码示例, 在power Shell窗口中执行nodemon app.js,再在浏览器中输入localhost:3000就可以看到res.end()里面的内容:
//用于创建网站服务器的模块
const http = require('http');
//app对象就是网站服务器对象
const app = http.createServer();
//当客户端有请求来的时候 监听客户端请求
app.on('request', (req, res) => {
res.end('<h2>hello user</h2>');
});
//监听3000端口 其他号也可以只要没被占用
app.listen(3000);
console.log('网站服务器启动成功');
HTTP协议
HTTP 协议就是客户端和服务器端沟通的规范;规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作。
报文:
在HTTP请求和响应过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式
- 请求方式
req.method
GET 请求数据
POST 发送数据 可以通过表单的方式指定请求方式是post或者get
地址相同的时候根据这个请求方式不同可以响应不同 的内容
代码示例如下
app.on('request', (req, res) => {
//获取请求方式 req.method
console.log(req.method);
if (req.method == 'POST') {
res.end('post')
} else if (req.method == 'GET') {
res.end('get');
}
// res.end('<h2>hello user</h2>');
});
- 获取请求地址
req.url
req.url代码示例
if (req.url == '/index' || req.url == '/') {
res.end('welcome to homepage');
} else if (req.url == '/list') {
res.end('welcome to listpage');
} else {
res.end('sorry not found');
}
- 获取请求报文信息
req.headers
console.log(req.headers['accept']);
- 响应报文
HTTP状态码:
200请求成功
404请求资源没有被找到
500服务器端错误
400客户端请求有语法错误
内容类型:
text/html
text/css
application/javascript
image/jpeg
application/json
以上内容通过以下代码设置:可以解析html文件,注意要设置文本编码方式charset 否则中文会报错
res.writeHead(200, {
'content-type': 'text/html;charset=utf8',
});
HTTP请求与响应处理
- 请求参数
客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作;
get请求参数
参数被放置在浏览器地址栏中:例如:http://localhost:3000/?name=zhangsan&age=20
需要通过req.url得到这个输入的地址,然后使用url.parse(第一个参数,第二个参数)解析
代码如下:
//1.第一个参数是要解析的url
//2.第二个参数是将查询参数解析成对象形式
console.log(url.parse(req.url, true));
解析结果
在解析的时候出现了问题,上面代码有null prototype,解决方法nodejs中文网
为了解决上面废弃的url.parse方法使用新的
const params = new URL(req.url, 'http://localhost:3000/');
// console.log(params);
console.log(params.searchParams.get('name'));//打印zhangsna
console.log(params.searchParams.get('age'));//打印20
904
利用解构上面代码又可以更改为:
//利用解构写出下面的代码
const { searchParams, pathname } = new URL(req.url, 'http://localhost:3000/');
// console.log(params);
console.log(searchParams.get('name'));
console.log(searchParams.get('age'));
if (pathname == '/index' || req.url == '/') {
res.end('<h2>欢迎来到首页</h2>');
} else if (pathname == '/list') {
res.end('welcome to listpage');
} else {
res.end('sorry not found');
}
POST请求参数
数据放在了form data里面
- post参数通过事件的方式接收 不是一次接收完的
- data 当请求参数传递的时候触发事件
- end 当参数传递完成的时候触发end事件
post获取参数
querystring.parse()遇到的问题
console.log(querystring.parse(postParams));
输出的是:
改正以后:参考博客
let params1 = JSON.stringify(querystring.parse(postParams));
let obj = JSON.parse(params1);
console.log(obj);
post请求参数被放置在请求体(报文)中进行传输,而get请求参数把参数放在了地址url中进行获取
获取POST参数需要使用data和end事件
使用querystring系统模块将参数转换为对象格式,但是官方文档,
所以需要使用JSON路由
http://localhost:3000/index http://localhost:3000/login 路由概念:客户端请求地址与服务器端程序代码对应关系,即请求什么就响应什么。
路由代码示例,post请求方式没有写
// 1. 引入系统模块http
// 2.创建网站服务器
// 3.为网站服务器对象添加请求事件
// 4.实现路由功能
//获取客户端请求方式
//获取客户端请求地址
const http = require('http');
const url = require('url');
const app = http.createServer();
app.on('request', (req, res) => {
//获取请求方式 toLowerCase()转换为小写
const method = req.method.toLowerCase();
//获取请求地址
const { searchParams, pathname } = new URL(req.url, 'http://localhost:3000/');
//响应报文编码方式
res.writeHead(200, {
'content-type': 'text/html;charset=utf8'
});
if (method == 'get') {
if (pathname == '/' || pathname == '/index') {
res.end('欢迎来到首页index');
} else if (pathname == '/list') {
res.end('欢迎来到列表页list');
} else {
res.end('您访问的页面不存在哦!请返回');
}
} else if (method == 'post') {
}
});
app.listen(3000);
console.log('网站服务器启动成功');
//获取请求地址
const { searchParams, pathname } = new URL(req.url, 'http://localhost:3000/');
静态资源
服务器端不需要处理,可以直接响应给客户端的资源叫静态资源;例如CSS、JavaScript、image文件。
http://www.itcast.cn/images/logo.png 静态资源访问:
获取用户请求路径 __dirname
获取绝对路径
将用户请求路径转换为实际的服务器硬盘路径let realPath = path.join(__dirname, 'public' + pathname);
//获取用户请求路径
// const { searchParams, pathname } = new URL(req.url, 'http://localhost:3000/');
let pathname = new URL(req.url, 'http://localhost:3000/').pathname;
res.end(path.join(__dirname, 'public' + pathname));
系统读取实际路径成功后会调用箭头函数,如果文件读取成功error就是空result是文件内容;失败的话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);
});
使用第三方模块mime
获取当前文件类型html css
等类型,然后返回
let type = mime.getType(realPath);
res.writeHead(200, {
'content-type': type
});
动态资源
相同的请求地址不同的响应资源,叫动态资源
http://www.itcast.cn/article?id=1 http://www.itcast.cn/article?id=2
Node.js异步编程
同步API 异步API
同步API:只有当前API执行完成后,才能继续执行下一个API
代码如下
console.log('before');
console.log('after');
异步API:当前API执行不会阻塞后续代码执行
代码示例
console.log('before');
setTimeout(() => {
console.log('last');
}, 2000);
console.log('after');
定时器就是异步API
同步API,异步API区别:
异步API无法通过返回值方式拿到API 执行结果,是通过回调函数拿到异步API执行结果的;
function getMsg() {
setTimeout(function() {
return { msg: 'hello node.js' }
}, 2000);
}
const msg = getMsg();
console.log(msg); //打印undedined
以下代码通过回调函数拿到异步API执行结果示例1
function getData(callback) {
callback('1234');
};
getData(function(n) {
console.log('callback函数被调用了');
console.log(n);
});
//程序执行结果打印:
// callback函数被调用了
// 1234
以下代码通过回调函数拿到异步API执行结果示例2
function getMsg(callback) {
setTimeout(function() {
callback({
msg: 'hello node.js'
});
}, 2000);
}
getMsg(function(data) {
console.log(data);
});
代码执行顺序不一样
同步API从上到下依次执行,前面代码会阻塞后面代码
同步API执行
for (var i = 0; i < 10000; i++) {
console.log(i);
}
console.log('for循坏后面的代码');
上面代码执行结果
异步API不会等待API执行完成后再执行后面的代码
异步API执行
console.log('代码开始执行');
setTimeout(function() {
console.log('2s');
}, 2000);
setTimeout(function() {
console.log('0s');
}, 0);
console.log('代码结束执行');
上面代码执行结果
异步API中的一些问题:
异步API:
fs.readFile('./demo.txt',(err,result)=>{});
var server=http.createServer();
server.on('request',(req,res)=>{});
问题来了,如果异步API后面代码的执行依赖当前的API执行结果该怎么办呢???
fs.readFile('./demo.txt',(err,result)=>{});
consloe.log('文件读取结果');
现在就有一个需求:依次读取文件A、文件B、文件C。
第一种解决方法示例,但是不采用这种方法不方便代码维护
const fs = require('fs');
fs.readFile('./1.txt', 'utf8', (err, result1) => {
console.log(result1);
fs.readFile('./2.txt', 'utf8', (err, result2) => {
console.log(result2);
fs.readFile('./3.txt', 'utf8', (err, result3) => {
console.log(result3);
})
})
});
以上代码运行结果如下
使用Promise解决Node.js异步编程中回调地狱(也叫回调陷阱)的问题;promise参考文献 1 2第2种解决方法示例Promise
1.首先是使用Promise 然后用函数把Promise实例化的对象包装成一个函数
function P1() {
return new Promise((resolve, reject) => {
fs.readFile('./1.txt', 'utf8', (err, result1) => {
resolve(result1);
});
});
}
2.使用return P2()使得后面可以继续使用then(), 利用链式编程
P1().then((r1) => {
console.log(r1);
return P2();
})
promise方法完整代码示例
const fs = require('fs');
//3个异步API 所以需要三个promise
function P1() {
return new Promise((resolve, reject) => {
fs.readFile('./1.txt', 'utf8', (err, result1) => {
resolve(result1);
});
});
}
function P2() {
return new Promise((resolve, reject) => {
fs.readFile('./2.txt', 'utf8', (err, result2) => {
resolve(result2);
});
});
}
function P3() {
return new Promise((resolve, reject) => {
fs.readFile('./3.txt', 'utf8', (err, result3) => {
resolve(result3);
});
});
}
P1().then((r1) => {
console.log(r1);
return P2();
})
.then((r2) => {
console.log(r2);
return P3();
})
.then((r3) => {
console.log(r3);
})
异步函数
异步函数是异步编程语法的终极解决方案,可以将异步代码写成同步形式,让代码不再又回调函数嵌套,使代码更加清晰;
async关键字
- 普通函数定义前加async关键字 普通函数便异步函数
- 异步函数默认返回promise对象
- 在异步函数内部使用return关键字进行结果返回,结果会被包裹的promise对象中 return关键字代替了resolve方法
- 在异步函数内部使用throw关键字进行错误抛出 执行完throw便不再执行后面代码
- await关键字 只能出现在异步函数中 await promise可以暂停异步函数执行 等待promise对象返回结果再向下执行
代码示例
const fn=async()=>{};
async function fn(){};
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();
await关键字
- await只出现在异步函数中
- await promise await 后面只能写promise对象 写其他类型的API是不可以的
- await关键字使得暂停异步函数向下执行直到返回promise返回结果;
使用异步函数实现依次读取 a b c三个文件
const fs = require('fs');
//让现有的异步函数API让其返回promise对象,从而支持执行异步函数语法;
const promisify = require('util').promisify;
//readFile为promisify对象 所以可以使用await关键字
const readFile = promisify(fs.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();
Node.js全局对象global
global.console.log('我是');
global.setTimeout(function() {
console.log('123');
}, 2000)
总结