1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 05-Node.js—http模块

05-Node.js—http模块

时间:2024-04-02 01:55:24

相关推荐

05-Node.js—http模块

目录

1、HTTP 协议1.1 概念1.2 请求报文的组成1.3 HTTP 的请求行1.4 HTTP 请求头1.5 HTTP 的请求体1.6 响应报文的组成 2、创建 HTTP 服务2.1 操作步骤2.2 测试2.3 注意事项 3、获取 HTTP 请求报文3.1 请求方法 request.method3.2 请求版本 request.httpVersion3.3 请求路径 request.url3.4 URL 路径 require('url').parse(request.url).pathname3.5 URL 查询字符串 require('url').parse(request.url, true).query3.6 实例化 URL 的对象3.7 请求版本 request.headers3.8 请求体3.9 小案例 4、设置 HTTP 响应报文4.1 设置响应状态码 response.statusCode4.2 设置响应状态描述 response.statusMessage ( 用的非常少 )4.3 设置响应头信息 response.setHeader('头名', '头值')4.4 设置响应体response.write('xx')、response.end('xxx')4.5 小案例 5、网页资源的基本加载过程6、静态资源服务6.1 什么是静态资源6.2 网站根目录或静态资源目录6.3 网页中的 URL6.3.1 绝对路径6.3.2 相对路径6.3.3网页中使用 URL 的场景小结 6.4 设置资源类型(mime类型) 7、GET 和 POST 请求7.1 场景小结7.2 GET和POST请求的区别 参考

在介绍http模块之前先来了解一下什么是HTTP

1、HTTP 协议

1.1 概念

HTTP(hypertext transport protocol)协议;中文叫超文本传输协议

是一种基于TCP/IP的应用层通信协议

这个协议详细规定了浏览器和万维网服务器之间互相通信的规则。

协议中主要规定了两个方面的内容

客户端:用来向服务器发送数据,可以被称之为请求报文服务端:向客户端返回数据,可以被称之为响应报文

报文:可以简单理解为就是一堆字符串

1.2 请求报文的组成

请求行请求头空行请求体

1.3 HTTP 的请求行

请求方法(getpostputdelete等)请求 URL(统一资源定位器

例如::80/index.html?a=100&b=200#logohttp:协议(https、ftp、ssh等)域名80端口号/index.html路径a=100&b=200查询字符串#logo 哈希(锚点链接)HTTP协议版本号

1.4 HTTP 请求头

格式:『头名:头值』常见的请求头有:

1.5 HTTP 的请求体

请求体内容的格式是非常灵活的,

(可以是空)==> GET请求,(也可以是字符串,还可以是JSON)===> POST请求

例如:

字符串:keywords=手机&price=2000JSON:{“keywords”:“手机”,“price”:2000}

1.6 响应报文的组成

响应行

HTTP/1.1 200 OK

HTTP/1.1:HTTP协议版本号200:响应状态码404 Not Found 500 Internal Server Error还有一些状态码,参考:/zh-CN/docs/Web/HTTP/Status

OK:响应状态描述

响应状态码和响应字符串关系是一一对应的。

响应头

Cache-Control: 缓存控制 private 私有的,只允许客户端缓存数据

Connection:链接设置

Content-Type:text/html;charset=utf-8 设置响应体的数据类型以及字符集,响应体为html,字符集utf-8

Content-Length:响应体的长度,单位为字节

空行响应体 响应体内容的类型是非常灵活的,常见的类型有 HTML、CSS、JS、图片、JSON

2、创建 HTTP 服务

2.1 操作步骤

//1. 导入 http 模块const http = require('http')//2. 创建服务对象 create 创建 server 服务// request 意为请求. 是对请求报文的封装对象, 通过 request 对象可以获得请求报文的数据// response 意为响应. 是对响应报文的封装对象, 通过 response 对象可以设置响应报文const server = http.createServer((request, response) => {response.end('hello world')})//3. 监听端口, 启动服务server.listen(9000,()=>{console.log('服务已经启动, 端口 9000 监听中...');})

http.createServer里的回调函数的执行时机: 当接收到HTTP 请求的时候,就会执行。

2.2 测试

浏览器请求对应端口http://127.0.0.1:9000

127.0.0.1是 回送地址 ,指本地机,一般用来测试使用。

浏览器返回结果

2.3 注意事项

命令行ctrl + c停止服务当服务启动后,更新代码 必须重启服务才能生效响应内容中文乱码的解决办法

//设置响应头response.setHeader('content-type', 'text/html;charset=utf-8')

端口号被占用

Error: listen EADDRINUSE: address already in use :::9000

关闭当前正在运行监听端口的服务 ( 使用较多 )修改其他端口号在资源管理器中的侦听端口中找到9000端口,然后记下它的PID值;打开任务管理器,在详细信息中找到对应的PID值,右键结束任务

5.HTTP协议默认端口是 80 。HTTPS协议的默认端口是 443, HTTP 服务开发常用端口有 3000,8080,8090,9000 等

3、获取 HTTP 请求报文

想要获取请求的数据,需要通过request对象

3.1 请求方法 request.method

//1. 导入 http 模块const http = require('http');//2. 创建服务对象const server = http.createServer((request, response) => {//获取请求的方法console.log(request.method);response.end('http'); //设置响应体});//3. 监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回的结果:

3.2 请求版本 request.httpVersion

//1. 导入 http 模块const http = require('http');//2. 创建服务对象const server = http.createServer((request, response) => {//获取 HTTP 协议的版本号console.log(request.httpVersion);response.end('http'); //设置响应体});//3. 监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回结果:

3.3 请求路径 request.url

//1. 导入 http 模块const http = require('http');//2. 创建服务对象const server = http.createServer((request, response) => {//获取请求的 urlconsole.log(request.url);// 只包含 url 中的路径与查询字符串response.end('http'); //设置响应体});//3. 监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回的结果:

测试地址:

http://127.0.0.1:9000/search?name=hh&password=123

注意:

request.url只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容

关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为/

关于favicon.ico:这个请求是属于浏览器自动发送的请求

3.4 URL 路径 require(‘url’).parse(request.url).pathname

//导入 http 模块const http = require('http');//1. 导入 url 模块const url = require('url');//创建服务对象const server = http.createServer((request, response) => {let res = url.parse(request.url);console.log(res.pathname);response.end('url');});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

测试地址:

http://127.0.0.1:9000/search?name=hh&password=123

3.5 URL 查询字符串 require(‘url’).parse(request.url, true).query

我们先来看看url.parse(request.url)不加第二个参数true的结果

//导入 http 模块const http = require('http');//1. 导入 url 模块const url = require('url');//创建服务对象const server = http.createServer((request, response) => {let res = url.parse(request.url);console.log(res);response.end('url');});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回的结果:

query的值是一个字符串,这样对我们是不友好的。我们再来看看加上true的结果:

let res = url.parse(request.url,true);console.log(res);

query的值变成了一个对象,这样我们就可以通过对象.的形式拿到里面对应的值了

比如我们想拿到name的值,可以这样:

console.log(res.query.name);

返回结果:

3.6 实例化 URL 的对象

这种方法相对于之前的parse(request.url)来说是优化的。我们先来看一下它返回的url是怎样的对象。

//导入 http 模块const http = require('http');//创建服务对象const server = http.createServer((request, response) => {//实例化 URL 的对象let url = new URL(request.url, 'http://127.0.0.1');console.log(url);});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回的结果:

URL {href: 'http://127.0.0.1/search?name=hh&password=123',origin: 'http://127.0.0.1',protocol: 'http:',username: '',password: '',host: '127.0.0.1',hostname: '127.0.0.1',port: '',pathname: '/search',search: '?name=hh&password=123',searchParams: URLSearchParams {'name' => 'hh', 'password' => '123' },hash: ''}

相比 之前的方法多了一条searchParams: URLSearchParams我们就可以通过这个对象来拿到我们的查询字符串路径的获取和之前是一样。

//导入 http 模块const http = require('http');//创建服务对象const server = http.createServer((request, response) => {//实例化 URL 的对象let url = new URL(request.url, 'http://127.0.0.1');console.log(url);//输出路径console.log(url.pathname);//输出 keyword 查询字符串console.log(url.searchParams.get('name')); response.end('url new');});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回结果:

3.7 请求版本 request.headers

//1. 导入 http 模块const http = require('http');//2. 创建服务对象const server = http.createServer((request, response) => {//获取 HTTP 的请求头console.log(request.headers);response.end('http'); //设置响应体});//3. 监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回的是一个对象

{host: '127.0.0.1:9000',connection: 'keep-alive','cache-control': 'max-age=0','sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"','upgrade-insecure-requests': '1','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36', accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9','sec-fetch-site': 'none','sec-fetch-mode': 'navigate','sec-fetch-user': '?1','sec-fetch-dest': 'document','accept-encoding': 'gzip, deflate, br','accept-language': 'zh-CN,zh;q=0.9'}{host: '127.0.0.1:9000',connection: 'keep-alive','sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"','sec-ch-ua-mobile': '?0','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36', 'sec-ch-ua-platform': '"Windows"',accept: 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8','sec-fetch-site': 'same-origin','sec-fetch-mode': 'no-cors','sec-fetch-dest': 'image',referer: 'http://127.0.0.1:9000/search?name=hh&password=123','accept-encoding': 'gzip, deflate, br','accept-language': 'zh-CN,zh;q=0.9'}

注意事项:

request.headers将请求信息转化成一个对象,并将属性名都转化成了小写

既然返回的是一个对象,我们就可以通过对象.的形式获取其中的值。

console.log(request.headers.host);

返回的结果:

3.8 请求体

request.on('data', function(chunk){})request.on('end', function(){});

//1. 导入 http 模块const http = require('http');//2. 创建服务对象const server = http.createServer((request, response) => {//1. 声明一个变量let body = '';//2. 绑定 data 事件request.on('data', chunk => {body += chunk;})//3. 绑定 end 事件request.on('end', () => {console.log(body);//响应response.end('Hello HTTP');});});//3. 监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回结果:

由于我们默认是GET请求,所以请求体默认是空的,我们通过form表单设置为POST在请求一次。

3.9 小案例

按照以下要求搭建 HTTP 服务

//1. 导入 http 模块const http = require('http');//2. 创建服务对象const server = http.createServer((request, response) => {//获取请求的方法let {method} = request;//获取请求的 url 路径let {pathname} = new URL(request.url, 'http://127.0.0.1');response.setHeader('content-type','text/html;charset=utf-8');//判断if(method === 'GET' && pathname === '/login'){//登录的情形response.end('登录页面');}else if(method === 'GET' && pathname === '/reg'){// register 注册response.end('注册页面');}else{response.end('Not Found');}});//3. 监听端口 启动服务server.listen(9000, () => {console.log('服务已经启动.. 端口 9000 监听中....');})

案例效果图:

4、设置 HTTP 响应报文

4.1 设置响应状态码 response.statusCode

//导入 http 模块const http = require('http');//创建服务对象const server = http.createServer((request, response) => {//1. 设置响应状态码response.statusCode = 203response.end('hh')});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回结果:

4.2 设置响应状态描述 response.statusMessage ( 用的非常少 )

//导入 http 模块const http = require('http');//创建服务对象const server = http.createServer((request, response) => {//1. 设置响应状态码response.statusCode = 404;//2. 响应状态的描述 response.statusMessage = 'I love you'response.end('hh')});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回的结果:

4.3 设置响应头信息 response.setHeader(‘头名’, ‘头值’)

//导入 http 模块const http = require('http');//创建服务对象const server = http.createServer((request, response) => {//1. 设置响应状态码// response.statusCode = 404;// //2. 响应状态的描述 // response.statusMessage = 'I love you'//3. 响应头response.setHeader('content-type', 'text/html;charset=utf-8')response.setHeader('Server','Node,js')response.end('哈哈')});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回的结果:

注:有时候我们的头名有多个不同的头值,那我们该如何设置呢?

示例:

response.setHeader('coke','one,tow,three')

这样子设置是不行的,我们必须通过数组的形式来完成。

response.setHeader('coke',['one','tow','three'])

4.4 设置响应体response.write(‘xx’)、response.end(‘xxx’)

//导入 http 模块const http = require('http');//创建服务对象const server = http.createServer((request, response) => {//1. 设置响应状态码// response.statusCode = 404;// //2. 响应状态的描述 // response.statusMessage = 'I love you'//3. 响应头// response.setHeader('content-type', 'text/html;charset=utf-8')// response.setHeader('Server','Node,js')// response.setHeader('coke',['one','tow','three'])//4. 响应体的设置response.write('I')response.write('love')response.write('you')response.end('hh')});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

返回结果:

注意:

write 和 end 的两种使用情况://1. write 和 end 的结合使用 响应体相对分散response.write('xx');response.write('xx');response.write('xx');response.end(); //每一个请求,在处理的时候必须要执行 end 方法的//2. 单独使用 end 方法 响应体相对集中response.end('xxx');

4.5 小案例

搭建 HTTP 服务,响应一个 4 行 3 列的表格,并且要求表格有 隔行换色效果 ,且 点击 单元格能 高亮显示。

const http = require('http');//创建服务对象const server = http.createServer((request, response) => {response.end(`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>table {border-collapse: collapse;}table td {padding: 20px 60px;}table tr:nth-child(odd) {background-color: rgb(73, 31, 177);}table tr:nth-child(even) {background-color: pink;}.active {background-color: black;}</style></head><body><table border="1"><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr></table><script>document.querySelectorAll('td').forEach(item => {item.addEventListener('click', function () {let od = document.querySelector('table .active')if (od) {od.classList.remove('active')}this.classList.add('active')})})</script></body></html>`)//设置响应体});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

显示效果:

大家应该也发现了,响应体的内容太多,而且这样写没有代码高亮和提示,对我们来说是不友好的。

解决方法:

将响应体内容放在一个HTML文件中,通过fs模块来对此文件进行读取。

const http = require('http');const fs = require('fs');//创建服务对象const server = http.createServer((request, response) => {// 读取文件内容let html = fs.readFileSync(__dirname + '/table1.html')response.end(html)//设置响应体});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

如果我们将css和js各种单独放在一个文件当中,我们这样写是有问题的。

显示效果:

没有css样式js行为,为什么会这样呢?

我们先来看看浏览器响应体返回的结果是什么

css和js文件返回的依旧是HTML文件在服务器返回响应给我们的时候会执行下面这个回调函数,而回调函数里面只返回了一个html,所以会导致这样的结果发生。

const server = http.createServer((request, response) => {// 读取文件内容let html = fs.readFileSync(__dirname + '/table1.html')response.end(html)//设置响应体});

我们该如何解决? 我们只需要给每一个路径进行判断,如果是这个文件,就返回该文件的内容。

// 导入模块const http = require('http')const fs = require('fs')const server = http.createServer((request, response) => {// 获取路径let {pathname } = new URL(request.url, 'http://127.0.0.1')if (pathname === '/') {let html = fs.readFileSync(__dirname + '/index.html')response.end(html)} else if (pathname === '/index.css') {let css = fs.readFileSync(__dirname + '/index.css')response.end(css)} else if (pathname === '/index.js') {let js = fs.readFileSync(__dirname + '/index.js')response.end(js)} else {response.statusCode = 404response.end('404,NOT FOUND')}})server.listen(9000, () => {console.log('服务已启动');})

显示效果:

说到这其实又引申出另外一个问题,如果我们有很多个文件,那我们岂不是要对每个文件进行判断,这样是很麻烦的。所以我们又想到了另外一种解决办法。
通过搭建静态服务资源来解决这个问题目录结构:

const http = require('http')const fs = require('fs')const path = require('path')// 创建服务对象const server = http.createServer((request, response) => {//获取请求url的路径let {pathname } = new URL(request.url, 'http://127.0.0.1')//声明一个变量let root = __dirname + '/page'//拼接文件路径let filePath = path.resolve(root + pathname)//读取文件 fs 异步 APIfs.readFile(filePath, (err, data) => {if (err) {response.statusCode = 500response.end('文件读取失败···')return}response.end(data)})})server.listen(9000, () => {console.log('服务已启动');})

5、网页资源的基本加载过程

网页资源的加载都是循序渐进的,首先获取HTML的内容, 然后解析 HTML 在发送其他资源的请求,如 CSS,Javascript,图片等。

6、静态资源服务

6.1 什么是静态资源

静态资源是指 内容长时间不发生改变的资源 ,例如图片,视频,CSS 文件,JS文件,HTML文件,字体文件等动态资源是指 内容经常更新的资源 ,例如百度首页,网易首页,京东搜索列表页面等

6.2 网站根目录或静态资源目录

HTTP 服务在哪个文件夹中寻找静态资源,那个文件夹就是静态资源目录,也称之为 网站根目录

思考:vscode 中使用 live-server 访问 HTML 时, 它启动的服务中网站根目录是谁?

答案:以打开的文件夹作为网站根目录

6.3 网页中的 URL

网页中的 URL 主要分为两大类:相对路径绝对路径

6.3.1 绝对路径

绝对路径可靠性强,而且相对容易理解,在项目中运用较多

我们最常用的是第三种如果将来我们要改变域名ip,用前两种方法会比较麻烦,一但改变,里面的链接路径全部要更改。第三种方法则随便你域名ip更改我都不受影响

6.3.2 相对路径

相对路径在发送请求时,需要与当前页面 URL 路径进行 计算,得到完整 URL后,再发送请求,学习阶 段用的较多。例如当前网页 url 为/course/h5.html

在实际工作当中我们都是使用绝对路径,因为相对路径不可靠,体现在于页面url的路径相关,它是参照页面url的,当页面url不正常的时候,我们使用相对路径去获取资源就会异常

6.3.3网页中使用 URL 的场景小结

包括但不限于如下场景: a 标签 hreflink 标签 hrefscript 标签 srcimg 标签 srcvideo audio 标签 srcform 中的 actionAJAX 请求中的 URL

6.4 设置资源类型(mime类型)

媒体类型( 通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档文件字节流的性质和格式。

mime 类型结构: [type]/[subType]

例如: text/html text/css image/jpeg image/png application/json

HTTP 服务可以设置响应头 Content-Type 来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理资源下面是常见文件对应的 mime 类型

html: 'text/html',css: 'text/css',js: 'text/javascript',png: 'image/png',jpg: 'image/jpeg',gif: 'image/gif',mp4: 'video/mp4',mp3: 'audio/mpeg',json: 'application/json'

对于未知的资源类型,可以选择application/octet-stream类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的 下载 效果

我们来对之前的代码进行改进一下:

//导入 http 模块const http = require('http');const fs = require('fs');const path = require('path');//声明一个变量let mimes = {html: 'text/html',css: 'text/css',js: 'text/javascript',png: 'image/png',jpg: 'image/jpeg',gif: 'image/gif',mp4: 'video/mp4',mp3: 'audio/mpeg',json: 'application/json'}//创建服务对象const server = http.createServer((request, response) => {if(request.method !== 'GET'){response.statusCode = 405;response.end('<h1>405 Method Not Allowed</h1>');return;}//获取请求url的路径let {pathname} = new URL(request.url, 'http://127.0.0.1');//声明一个变量let root = __dirname + '/page';// let root = __dirname + '/../';//拼接文件路径let filePath = root + pathname;//读取文件 fs 异步 APIfs.readFile(filePath, (err, data) => {if(err){console.log(err);//设置字符集response.setHeader('content-type','text/html;charset=utf-8');//判断错误的代号switch(err.code){case 'ENOENT':response.statusCode = 404;response.end('<h1>404 Not Found</h1>');case 'EPERM':response.statusCode = 403;response.end('<h1>403 Forbidden</h1>');default:response.statusCode = 500;response.end('<h1>Internal Server Error</h1>');}return;}//获取文件的后缀名let ext = path.extname(filePath).slice(1);//获取对应的类型let type = mimes[ext];if(type){//匹配到了text/html;charset=utf-8if(ext === 'html'){response.setHeader('content-type', type + ';charset=utf-8');}else{response.setHeader('content-type', type);}}else{//没有匹配到response.setHeader('content-type', 'application/octet-stream');}//响应文件内容response.end(data);})});//监听端口, 启动服务server.listen(9000, () => {console.log('服务已经启动....')});

注:

1.响应头里面的字符集权重比HTML页面中meta标签字符集权重要高。

2.当css、js、img等资源进入到网页当中去,会根据网页的字符集进行处理,所以没必要给它们设置字符集。

7、GET 和 POST 请求

7.1 场景小结

GET 请求的情况:

在地址栏直接输入 url 访问

点击 a 链接

link 标签引入 css

script 标签引入 js

img 标签引入图片

form 标签中的 method 为 get (不区分大小写)

ajax 中的 get 请求

POST 请求的情况: form 标签中的 method 为 post(不区分大小写)AJAX 的 post 请求

7.2 GET和POST请求的区别

GETPOST是 HTTP 协议请求的两种方式。 GET 主要用来获取数据,POST 主要用来提交数据GET 带参数请求是将参数缀到 URL 之后,在地址栏中输入 url 访问网站就是 GET 请求, POST带参数请求是将参数放到请求体中POST 请求相对 GET 安全一些,因为在浏览器中参数会暴露`在地址栏GET 请求大小有限制,一般为 2K,而 POST 请求则没有大小限制

参考

尚硅谷版Node.js零基础视频教程,nodejs新手到高手

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。