03 请求响应原理及HTTP协议

1. 服务器端基础概念

1.1 网站的组成

网站应用程序主要分为两大部分:客户端和服务器端。

客户端:在浏览器中运行的部分,就是用户看到并与之交互的界面程序。使
用HTML、CSS、JavaScript构建。

服务器端:在服务器中运行的部分,负责存储数据和处理应用逻辑。

1.2 Node网站服务器

能够提供网站访问服务的机器就是网站服务器,它能够接收客户端的请求,能够对请求做出响应。

1.3 IP地址

互联网中设备的唯一标识。

IP是Internet Protocol Address的简写,代表互联网协议地址.

1.4 域名

由于IP地址难于记忆,所以产生了域名的概念,所谓域名就是平时上网所使用的网址。

http://www.itheima.com => http://124.165.219.100/

虽然在地址栏中输入的是网址, 但是最终还是会将域名转换为ip才能访问到指定的网站服务器。

1.5 端口

端口是计算机与外界通讯交流的出口,用来区分服务器电脑中提供的不同的服务。

1.6 URL

统一资源定位符,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式,我们平时所说的网页地址指的即是URL。

url的组成

传输协议://服务器IP或域名:端口/资源所在位置标识

http://www.baidu.cn/news/20181018/09152238514.html

http:超文本传输协议,提供了一种发布和接收HTML页面的方法。

1.7 开发过程中客户端和服务器端说明

在开发阶段,客户端和服务器端使用同一台电脑,即开发人员电脑。

客户端 (浏览器)

服务器端(Node)

本机域名:localhost
本地IP :127.0.0.1

2.创建web服务器

浏览器输入localhost:3000 访问

// 引用系统模块
const http = require('http');
// 创建web服务器
const app = http.createServer();
// 当客户端发送请求的时候
app.on('request', (req, res) => {
// 响应
res.end('

hi, user

');
});
// 监听3000端口
app.listen(3000);
console.log('服务器已启动,监听3000端口,请访问 localhost:3000')

3.HTTP协议

3.1 HTTP协议概念

超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准。

3.2 报文

在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式。

3.3 请求报文

  1. 请求方式(request method)

GET 请求数据

POST 发送数据

app.on('request', (req, res) => {
req.headers // 获取请求报文
req.url // 获取请求地址
req.method // 获取请求方法
});

演示

// 创建网站服务器的模块
const http = require('http')

// app 对象就是网站服务对象
const app = http.createServer()

// 当客户端有请求来的时候
app.on('request', (req, res) => {

// 获取请求报文信息
// req.headers
console.log(req.headers);
console.log(req.headers['accept']);

// 获取请求地址
// req.url
// console.log(req.url);
if (req.url == '/index' || '/') {
res.end('index')
} else if (req.url == '/list') {
res.end('list')
} else {
res.end('not found')
}

// 获取请求方式
// req.method

if (req.method == "POST") {
res.end('post')
} else if (req.method == "GET") {
res.end('get')

}

})

// 监听端口
app.listen(8080)
console.log("网站服务器启动成功");

3.4 响应报文

1.HTTP状态码

200 请求成功
404 请求的资源没有被找到
500 服务器端错误
400 客户端请求有语法错误

2.内容类型

text/html
text/css
application/javascript
image/jpeg
application/json

app.on('request', (req, res) => {
// 设置响应报文
res.writeHead(200, { '
Content-Type': 'text/html;charset=utf8‘
});
});

4. HTTP请求与响应处理

4.1 请求参数

客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作。

4.2 GET请求参数

  • 参数被放在浏览器地址中 http://localhost:3000/?name=zhangsan&age=20

  • 参数获取需要借助系统模块url,url模块用来处理url地址

    // 创建网站服务器的模块
    const http = require('http')

    // 处理url地址
    const url = require('url')

    // app 对象就是网站服务对象
    const app = http.createServer()

    // 当客户端有请求来的时候
    app.on('request', (req, res) => {
    // 设置相应报文
    res.writeHead(200, {
    // 返回类型
    'content-type': 'text/html;charset=utf8' // 纯文本 设置中文编码
    })

    // url解析
    console.log('url ' + req.url);
    // 第一个参数解析URl,第二个参数查询参数转换为对象形式保存
    let { query, pathname } = url.parse(req.url, true)
    console.log(query.name);
    console.log(query.age);

    // 获取请求地址
    // req.url
    // console.log(req.url);
    if (pathname == '/index' || pathname == '/') {
    res.end('

    来到index

    ')
    } else if (pathname == '/list') {
    res.end('list')
    } else {
    res.end('not found')
    }

    // 获取请求方式
    // req.method

    if (req.method == "POST") {
    res.end('post')
    } else if (req.method == "GET") {
    res.end('get')
    }
    })

    // 监听端口
    app.listen(8080)
    console.log("网站服务器启动成功");

4.3 POST请求参数

  • 参数被放置在请求体中进行传输

  • 获取POST参数需要使用data事件和end事件

  • 使用querystring系统模块将参数转换为对象格式

// 创建网站服务器的模块
const http = require('http')

// app 对象就是网站服务对象
const app = http.createServer()

// 处理请求参数模块
const querystring = require('querystring')

html文件





Document








js文件

// 当客户端有请求来的时候
app.on('request', (req, res) => {
// post参数是通过事件的方式接收
// data 请求参数传递的时候触发data事件
// end 请求参数传递完成的时候触发end事件

let postParams = ''

// params:当前传递过来的参数
req.on('data', params => {
postParams += params
})
req.on('end', () => {
// 参数转换字符串对象
console.log(querystring.parse(postParams));
})

// 给客户端一个响应内容
res.end('ok')
})

// 监听端口
app.listen(8080)
console.log("网站服务器启动成功");

4.4 路由

http://localhost:3000/index
http://localhost:3000/login
路由是指客户端请求地址与服务器端程序代码的对应关系。简单的说,就是请求什么响应什么。

4.5 静态资源

服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、image文件。

4.6 动态资源

相同的请求地址不同的响应资源,这种资源就是动态资源。

// 1.创建网站服务器的模块
const http = require('http')
const url = require('url')
const path = require('path')
const fs = require('fs')
const mime = require('mime')

// 2.创建网站服务器
const app = http.createServer()

// 3.监听请求事件
app.on('request', (req, res) => {

// 获取用户请求路径
let pathname = url.parse(req.url).pathname
pathname = pathname == '/' ? '/default.html' : pathname

// 根据路径返回资源类型
let type = mime.getType(pathname)

// __dirname:获取当前文件所在路径
// 将用户请求路径转换为实际的服务器硬盘路径
resPath = path.join(__dirname, 'pulic', pathname)
// res.end(resPath)
console.log(resPath);

// 读取文件
fs.readFile(resPath, (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(8080)
console.log("网站服务器启动成功");\

5. Node.js异步编程

5.1 同步API, 异步API

同步API:只有当前API执行完成后,才能继续执行下一个API

console.log('before');
console.log('after');

异步API:当前API的执行不会阻塞后续代码的执行

// 路径拼接
const public = path.join(__dirname, 'public');
// 请求地址解析
const urlObj = url.parse(req.url);
// 读取文件
fs.readFile('./demo.txt', 'utf8', (err, result) => {
console.log(result);
});

5.2 同步API, 异步API的区别( 获取返回值 )

同步API可以从返回值中拿到API执行的结果, 但是异步API是不可以的

function getMsg() {
setTimeout(function() {
return {
msg: 'hello'
}
}, 2000)
}
// 执行这个的时候 没过两秒拿不到返回值
const msg = getMsg()
console.log(msg);

5.3 回调函数

怎么才能拿到异步API的返回值,自己定义函数让别人去调用。

function getMsg(callback) {
setTimeout(function() {
callback({
msg: 'hello'
})
}, 2000)
}
getMsg(function(data) {
console.log(data);
})

5.4 5.5 同步API, 异步API的区别(代码执行顺序)

  • 同步API从上到下依次执行,前面代码会阻塞后面代码的执行

演示

for (var i = 0; i < 100000; i++) {
console.log(i);
}
console.log('for循环后面的代码');

  • 异步API不会等待API执行完成后再向下执行代码

演示

console.log('代码开始执行');
setTimeout(() => { console.log('2秒后执行的代码')}, 2000);
setTimeout(() => { console.log('"0秒"后执行的代码')}, 0);
console.log('代码结束执行');

先执行同步,后执行异步(先执行即将开始的)

5.6 Node.js中的异步API

1

fs.readFile('./demo.txt', (err, result) => {});
2

var server = http.createServer();
server.on('request', (req, res) => {});

如果异步API后面代码的执行依赖当前异步API的执行结果,但实际上后续代码在执行的时候异步API还没有返回结果,这个问题要怎么解决呢?

fs.readFile('./demo.txt', (err, result) => {});
console.log('文件读取结果');

需求:依次读取A文件、B文件、C文件

const fs = require('fs')
// 回调地狱
fs.readFile('./1.txt', 'utf8', (err, res1) => {
console.log(res1);
fs.readFile('./2.txt', 'utf8', (err, res2) => {
console.log(res2);
fs.readFile('./3.txt', 'utf8', (err, res3) => {
console.log(res3);
})
})
})

5.8 Promise

Promise出现的目的是解决Node.js异步编程中回调地狱的问题。

十分臃肿

const fs = require('fs')
// 回调地狱
// fs.readFile('./1.txt', 'utf8', (err, res1) => {
// console.log(res1);
// fs.readFile('./2.txt', 'utf8', (err, res2) => {
// console.log(res2);
// fs.readFile('./3.txt', 'utf8', (err, res3) => {
// console.log(res3);
// })
// })
// })

function p1() {
return new Promise((reslove, reject) => {
fs.readFile('./1.txt', 'utf8', (err, res) => {
reslove(res)
})
})
}

function p2() {
return new Promise((reslove, reject) => {
fs.readFile('./2.txt', 'utf8', (err, res) => {
reslove(res)
})
})
}

function p3() {
return new Promise((reslove, reject) => {
fs.readFile('./3.txt', 'utf8', (err, res) => {
reslove(res)
})
})
}

p1().then((r1) => {
console.log(r1);
return p2()
}).then((r2) => {
console.log(r2);
return p3()
}).then((r3) => {
console.log(r3);
})

5.9 异步函数

异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。

const fn = async () => {};
async function fn () {}

async关键字

1. 普通函数定义前加async关键字 普通函数变成异步函数
2. 异步函数默认返回promise对象
3. 在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了resolve方法
4. 在异步函数内部使用throw关键字抛出程序异常
5. 调用异步函数再链式调用then方法获取异步函数执行结果
6. 调用异步函数再链式调用catch方法获取异步函数执行的错误信息

await关键字

1. await关键字只能出现在异步函数中
2. await promise await后面只能写promise对象 写其他类型的API是不不可以的
3. await关键字可是暂停异步函数向下执行 直到promise返回结果

异步函数

// 1.在普通函数定义的前面加上async关键字
// 2.异步函数默认的返回值是promise对象
// 3.在异步函数内部使用throw关键字进行错误抛出
// async function fn() {
// throw '发生了一些错误' // 抛出错误异常
// return 123

// // await关键字
// // 1.它只能出现在异步函数中
// // 2.await promise 它可以暂停异步函数的执行 等待promise对象返回结果再向下执行

// }

// 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() // 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
// fs.readFile函数不能返回 promise 对象 所以要给他包装个promisify,能让异步API返回promise 对象
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()

posted @ 2020-06-13 15:51  xujing123  阅读(453)  评论(0)    收藏  举报