HTTP响应原理

1. 服务器端基础概念


1.1 网站的组成

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

客户端:在浏览器中运行的部分,用户看到并与之交互的界面程序

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

逻辑


1.2 Node网站服务器

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

网站服务器


1.3 IP地址

互联网中设备的唯一标识

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


1.4 域名

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

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

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


1.5 端口

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

端口


1.6 URL

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

URL的组成:

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

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

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


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

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

本机域名:localhost

本地IP:127.0.0.1

电脑


2. 创建 web 服务器

const http = require('http'); // 用于创建网站服务器的模块
const url = require('url'); // 用于处理 url 地址模块
const app = http.createServer(); // app 对象就是网站服务器对象
// request 当客户端有请求来时
// 请求消息 req 响应消息 res
app.on('request', (req, res) => {
    res.end('<h2>hello user</h2>');
});
app.listen(3000); // 监听端口
console.log('网站服务器启动成功');

注意:每次请求服务器如果没有响应就一直处于等待当中(转圈)


3. HTTP 协议


3.1 HTTP 协议的概念

超文本传输协议:

英文:HyperText Transfer Protocol,缩写:HTTP

规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准

HTTP


3.2 报文

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

报文


3.3 请求报文

  1. 请求方式 (Request Method)

    • GET:请求数据
    • POST:发送数据
  2. 请求地址 (Request URL)

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

3.4 响应报文

  1. HTTP状态码
    • 200 请求成功(默认)
    • 400 客户端请求有语法错误
    • 404 请求的资源没有被找到
    • 500 服务器端错误
  2. 内容类型
    • text/plain(默认纯文本)
    • 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/index?name=lisi&age=18

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

const url = require('url'); // 用于处理 url 地址模块
const http = require('http'); // 用于创建网站服务器的模块
const app = http.createServer(); // app 对象就是网站服务器对象
app.on('request', (req, res) => {
    // 参数1:要解析的 url 地址
    // 参数2:是否将查询参数解析成对象形式
    let { query, pathname } = url.parse(req.url, 1);
    console.log(query.name); // lisi
    console.log(query.age); // 18
});
app.listen(3000); // 监听端口

4.3 POST请求参数

  • 参数被放置在请求体(报文)中进行传输
  • 获取 POST 参数需要使用 data 事件和 end 事件
  • 使用 querystring 系统模块将字符串格式转换为对象格式
const http = require('http'); // 用于创建网站服务器的模块
const app = http.createServer(); // app 对象就是网站服务器对象
const querystring = require('querystring'); // 处理请求参数模块
app.on('request', (req, res) => {
    let postParams = ''; // 因参数传递可能不是一次性传完的,需要累加收集
    // data 当请求参数传递的时候触发事件
    req.on('data', params => {
        postParams += params;
    });
    // end 当参数传递完成的时候触发事件
    req.on('end', () => {
        console.log(postParams); // username=wzq&password=123
        console.log(querystring.parse(postParams)); // 处理成对象格式的
    })
    res.end('ok');
});
app.listen(3000); // 监听端口
<!-- method: 指定当前表单提交的方式 -->
<!-- action: 指定当前表单提交的地址 -->
<form method="post" action="http://localhost:3000">
    <!-- name 属性用于对提交到服务器后的表单数据进行标识 -->
    <input type="text" name="username">
    <input type="password" name="password">
    <input type="submit">
    <!-- 注意:只有设置了 name 属性的表单元素才能在提交表单时传递它们的值 -->
</form>

4.4 路由

路由是指客户端请求地址与服务器端程序代码的对应关系

简述:就是请求什么响应什么

http://localhost:3000/index

http://localhost:3000/login

对应关系


4.5 静态资源

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

例:http://www.itcast.cn/images/logo.png


4.6 动态资源

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

  • http://www.itcast.cn/article?id=1
  • http://www.itcast.cn/article?id=2

可能就是两个页面


4.7 客户端请求途径

  1. GET 方式
    • 浏览器地址栏
    • link 标签的 href 属性
    • script 标签的 src 属性
    • img 标签的 src 属性
    • Form 表单提交
  2. POST 方式
    • Form 表单提交

图图图


5. Node.js 异步编程


5.1 同异步 API

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

console.log('1'); 
console.log('2');

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

console.log('1');
setTimeout(
   () => { console.log('3');
}, 2000);
console.log('2');

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

// 同步:可以从返回值中拿到 API 执行的结果
function sum (n1, n2) { 
    return n1 + n2;
} 
const result = sum (10, 20);
// 异步:不能直接拿到返回值(想要拿到结果必须通过回调函数返回)
function getMsg () { 
    setTimeout(function () { 
        return { msg: 'Hello' }
    }, 2000);
}
const msg = getMsg ();
console.log(msg); // undefined

5.3 回调函数

自己定义函数让别人去调用

// getData 函数定义
function getData(callback) {
    callback('123')
}
// getData 函数调用
getData(function(n) {
    console.log('callback函数被调用了')
    console.log(n) // 123
});

5.4 使用回调函数获取异步 API 执行结果

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

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

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

for (var i = 0; i < 100000; i++) { 
    console.log(i);
}
console.log('最后输出的代码');

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

console.log('1'); // 1
setTimeout(() => { console.log('4')}, 2000);
setTimeout(() => { console.log('3')}, 0);
console.log('2'); // 2

5.6 代码执行顺序分析

图图图


5.7 Node.js 中的异步 API

// 第一种:读取文件
fs.readFile('./demo.txt', (err, result) => {});
// 第二种:事件回调
var server = http.createServer();
server.on('request', (req, res) => {});

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

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

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

fs.readFile('./1.txt', 'utf8', (err, result1) => {
    console.log(result1) // 1
    fs.readFile('./2.txt', 'utf8', (err, result2) => {
        console.log(result2) // 2
        fs.readFile('./3.txt', 'utf8', (err, result3) => {
            console.log(result3) // 3
        })
    })
});

多次回调函数嵌套会让代码很难维护,称为回调地狱


5.8 Promise

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

Promise 是个构造函数,参数内有以下两个函数,当异步 API 执行:

  • resolve:把成功结果通过参数传递出去
  • reject:把失败结果通过参数传递出去

Promise 有两个方法获取异步函数执行结果,调用异步函数再链式调用:

  • then 方法:获取读取成功的内容
  • catch 方法: 获取错误信息
let promise = new Promise((resolve, reject) => {
    fs.readFile('1.txt', 'utf8', (err, result) => {
        if (err != null) { // 如果有错误信息
            reject(err); // 传入错误信息
        } else { // 如果读取成功
            resolve(result); // 传入读取内容
        }
    });
});
promise.then((result) => { // 获取读取成功的内容
        console.log(result);
    })
    .catch((err) => {
        console.log(err); // 获取读取失败的内容
    });

注意:获取时要设置形参接收传出的内容


5.9 异步函数 async

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

async 关键字

  1. 普通函数定义前加 async 关键字,就变成异步函数
  2. 异步函数默认返回 promise 对象
  3. return 关键字代替了 resolve 方法,在异步函数内部使用 return 关键字进行结果返回,结果会被包裹到 promise 对象中
  4. 在异步函数内部使用 throw 关键字抛出程序异常(遇到后不会再向下执行)

await 关键字

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

posted @ 2020-11-12 18:55  今夜星河漫漫  阅读(342)  评论(0)    收藏  举报