HTTP 协议
初识 HTTP 协议
Hypertext Transfer Protocol (超文本传输协议)
互连网应用最广泛的协议之一
协议:双方必须共同遵从的一组约定
http 协议对浏览器和服务器之间的通信进行约束
请求 => 请求报文
响应 => 响应报文
HTTP报文
请求报文结构:
- 请求行:请求方法 + URL + HTTP版本号
GET [https://www.baidu.com/](https://www.baidu.com/) HTTP/1.1
- 请求方法:常用的有 GET/POST/PUT/PATCH/DELETE,还有一些使用相对比较少的,了解即可,比如:HEAD/OPTIONS/CONNECT/TRACE
- URL(Uniform Resource Locator 的缩写,统一资源定位符):其本身也是一个字符串,定位资源
- 协议名
- 主机名
- 端口号
- 路径
- 查询字符串
- HTTP版本号
- 1.0:1996年发布
- 1.1:1999年发布
- 2:2015年发布
- 3:2018年发布
- 请求头:有一系列的键值对组成(MDN HTTP Header)
- 空行
- 请求体:请求头的内容格式非常灵活,可以设置任意内容,只要和后端商量好
响应报文
- 响应行
- HTTP版本号
- 响应状态码
- 1xx:信息响应
- 2xx:成功响应(200: 请求成功)
- 3xx:重定向响应
- 4xx:客户端错误响应(403:禁止请求/404:找不到资源)
- 5xx:服务端错误响应(500:服务器内部错误)
- 响应状态描述,一般来说是字符串,保持和状态码一一对应(HTTP响应状态码)
- 200: OK
- 403:Forbidder
- 404:Not Found
- 500:Internal Server Error
- 响应头(https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers)
- 空行
- 响应体 -> 响应体的内容格式是非常灵活的,常见的响应体格式有:
- HTML
- CSS
- JavaScript
- 图片
- 视频
- JSON
网络基础概念
IP的介绍
IP 也被称为【IP地址】,本身是一个数字标识(32 Bit 二进制的数字),将其拆分分组转为10进制并用.分割,例如:192.168.1.3
IP用来标识网络设备,用于设备通信
IP的分类
IP为32位的二进制数,即最大为2的32次方,IP不够用
共享IP:共享公网 IP
- 区域共享
- 家庭共享
- 同一个路由器:局域网 IP 或 私网IP,设备间可以相互通信
本机回环IP地址:
- 127.0.0.1 ~127.255.255.254,这个区间的IP地址都是回环地址,指向当前本机
局域网 IP (私网 IP):
- 192.168.0.0 ~ 192.168.255.255
- 172.16.0.0 ~ 172.31.255.255
- 10.0.0.0 ~ 10.255.255.255
广域网 IP (公网 IP)
- 除上述之外
IP 地址分类
端口
应用程序的数字标识
一台现代计算机有 65536 个端口(0 ~ 65535)
一个应用程序可以使用一个或多个端口
作用:实现不同主机应用程序之间的通信
创建 HTTP 服务
// 1. 引入 http 模块
const http = require('http')
// 2. 创建服务对象
const service = http.createServer((request, response) => {
// request=>请求报文的封装对象
// response=>对响应报文的封装
response.end('hello world') //设置响应体,并结束服务
})
// 3. 监听端口,启动服务
service.listen(9000, () => {
// 服务启动成功后才会执行
console.log('服务启动成功')
})
HTTP 服务注意事项
- 命令行
ctrl + c
停止服务 - 当服务器启动后,更新代码
必须重启服务后才能生效
- 响应内容中文乱码的解决办法
response.setHeader('content-type','text/html;charset=utf-8');
- 端口号被占用
Error: listen EADDRINUSE: address already in use :::9000
5. 关闭当前正在运行监听端口的服务(`使用较多`)
6. 修改其它端口号
- HTTP 协议默认端口是 80,HTTPS 协议的默认端口是443。HTTP 服务开发常用的端口有 3000,8080,8090,9000 等
如果端口被其它程序占用,可以使用
资源监视器
找到占用端口的程序,然后使用任务管理器关闭对应的程序
浏览器中查看 HTTP 报文
浏览器控制台,网络
提取 HTTP 请求报文
const http = require('http')
const service = http.createServer((request, response) => {
// 1. 获取请求的方法
// console.log(' request.method :>> ', request.method);
// 2. 获取请求的 url
// console.log('request.url :>> ', request.url); // 只包含 url 中的路径与查询字符串
// 3. 获取 HTTP 协议的版本号
// console.log('request.httpVersion :>> ', request.httpVersion);
// 4. 获取 HTTP 的请求头
// console.log('request.headers :>> ', request.headers);
response.end('你好,世界!') //设置响应体,并结束服务
})
注意事项
- request.url 只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容
- request.headers 将请求信息转化成一个对象,并将属性名都转化成了『小写』
- 关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为『 / 』
- 关于 favicon.ico:这个请求是属于浏览器自动发送的请求
获取请求体
//这种方法了解就行,有更好用的办法
const service = http.createServer((request, response) => {
let body = ''
request.on('data', chunk => {
body += chunk
})
request.on('end', () => {
console.log('body :>> ', body);
response.end('htllo http')
})
})
service.listen('9000', () => {
console.log('服务请求中……')
})
提取 HTTP 报文中的请求路径及字符串
const url = require('url');
//方法一,使用 url.parse, 但是该语法已被废弃
const service = http.createServer((request, response) => {
// url.parse 的第二个参数为 true 时,查询字符串 query 将会转换为对象
const res = url.parse(request.url, true)
console.log('pathname :>> ', res.pathname);
console.log('query :>> ', res.query);
console.log('query :>> ', res.query.isTest);
response.end('url')
})
//方法二:使用 new URL()
const service = http.createServer((request, response) => {
const res = new URL(request.url, 'http://127.0.0.1')
const keyword = res.searchParams.get('search')
console.log('res :>> ', res);
console.log('keyword :>> ', keyword);
response.end('url')
})
service.listen('9000', () => {
console.log('服务请求中……')
})
设置 HTTP 响应报文
const http = require('http');
const service = http.createServer((request, response) => {
// 1. 设置响应状态码
response.statusCode = 203
// 2. 设置响应状态描述,几乎不用,响应状态码一般和响应状态描述一一对应
// response.statusMessage = 'test'
// 3. 设置响应头, 响应头可以设置多个,也可以使用数组设置同名的响应头
response.setHeader('test', ['a','b','c'])
response.setHeader('test2', 'test-2')
// 4. 设置响应体, 响应体可以设置多个,多个的响应体会自动拼接
// 一般使用 write 设置了响应体后,不会再在 end 中设置响应体
response.write('test')
response.write('test2')
// 每一个请求必须有一个,且只有一个 end
response.end()
})
service.listen('9000', () => {
console.log('服务启动中……')
})
引入网页外部资源
如果在 html 文件中引入外部资源,比如 css 和 js 文件,那在请求的时候,会调用多次接口,分开请求外部资源
所以,需要区分开请求的资源,不然都会返回同样的数据
//方法一:在 createServer 回调函数中区分需要获取的资源
//这种区分方法并不好,如果有大量外部资源就很繁琐,需要优化
//可以通过搭建静态资源服务的形式优化
const http = require('http');
const fs = require('fs');
const path = require('path')
const service = http.createServer((request, response) => {
const { pathname } = new URL(request.url, 'http://127.0.0.1')
if(pathname === '/'){
const filePath = path.resolve(__dirname , 'table/index.html')
const html = fs.readFileSync(filePath)
response.end(html)
}else if(pathname === '/index.css'){
const filePath = path.resolve(__dirname , 'table/index.css')
const css = fs.readFileSync(filePath)
response.end(css)
}
else if(pathname === '/index.js'){
const filePath = path.resolve(__dirname , 'table/index.js')
const js = fs.readFileSync(filePath)
response.end(js)
}
else{
response.end('404 not found')
}
})
service.listen('9000', () => {
console.log('服务启动中……')
})
静态资源服务
静态资源是指内容长时间不发生改变的资源
,例如图片,视频,css文件,HTML文件,字体文件等
动态资源是指内容经常更新的资源
,例如百度首页,网易首页,京东搜索列表页面等
搭建静态资源服务
//方法二:搭建静态资源服务
const http = require('http');
const fs = require('fs');
const path = require('path')
const service = http.createServer((request, response) => {
const { pathname } = new URL(request.url, 'http://127.0.0.1')
const filePath = path.resolve(__dirname , 'table' + pathname)
fs.readFile(filePath, (err, data) => {
if(err){
response.statusCode = 500;
response.end('404 not found')
return
}
response.end(data)
})
})
service.listen('9000', () => {
console.log('服务启动中……')
})
静态资源目录与网站根目录
HTTP 服务在哪个文件夹中寻找静态资源,那个文件夹就是静态资源目录
,也被称之为网站根目录
思考:vscode 中使用 live-servier 访问 HTML 时,它启动的服务网站根目录是谁?是vscode打开的文件夹
网页中的 URL
绝对路径
相对路径
相对路径不太可靠,和页面路径相关,参照页面 URL
使用场景
包括但不限于如下场景:
- a 标签 href
- link 标签 href
- script 标签 src
- img 标签 src
- video audio 标签 src
- form 中的 action
- AJAX 请求中的 URL
设置资源类型(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',
map4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json',
对于未知的类型,可以选择
application/octet-stream
类型,浏览器在遇到该类型的响应时,会对响应体内容进行对立存储,也就是我们常见的下载
效果
浏览器一般具有嗅探的功能,会自动识别请求的资源类型,但是我们设置响应头 Content-Type 来表明响应体的 MIME 类型会更规范一点
const http = require('http');
const fs = require('fs');
const path = require('path')
const mimes = {
html: 'text/html',
css:'text/css',
js: 'text/javascript',
png : 'image/png',
jpg: 'image/jpeg',
gif: 'image/gif',
map4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json',
}
const service = http.createServer((request, response) => {
const { pathname } = new URL(request.url, 'http://127.0.0.1')
const filePath = path.resolve(__dirname , 'table' + pathname)
fs.readFile(filePath, (err, data) => {
if(err){
response.statusCode = 500;
response.end('404 not found')
return
}
const extname = path.extname(filePath).slice(1)
const type = mimes[extname]
if(type){
response.setHeader('content-type',type)
}else{
response.setHeader('content', 'application/octet-stream')
}
response.end(data)
})
})
service.listen('9000', () => {
console.log('服务启动中……')
})
解决乱码问题
- 在设置资源类型时加上
charset=utf-8
,例如
response.setHeader('content-type','text/html;charset=utf-8')
- html 文件会在 meta 标签中设置 charset 类型,但是优先级没有在响应头中设置高
- 资源文件会根据页面的字符集进行解析,所以js\css等文件不设置字符集,在网页上显示也是ok的
完善错误处理
Error 错误
常见的有:
- ENOEN: 没有这样的文件或目录, 404
- EPERM: 不允许操作, 403
- 405:请求方法不被允许
- 500: 服务器内部错误
GET 和 POST 请求场景小结
场景小结
GET 请求情况
- 在地址栏中直接输入 url 访问
- 点击 a 链接
- link 标签引入 css
- script 标签引入 js
- video 与 audio 引入多媒体
- img 标签引入图片
- form 标签中的 method 为 get(不区分大小写)
- ajax 中的 get 请求
POST 请求情况文章来源:https://www.toymoban.com/news/detail-727926.html
- form 标签中的 method 为 post (不区分大小写)
- AJAX 中的 post 请求
GET 和 POST 请求的区别
GET 和 POST 是 HTTP 协议请求的两种方式,主要有如下几个区别文章来源地址https://www.toymoban.com/news/detail-727926.html
- 作用。GET 主要用来获取数据,POST 主要用来提交数据(并不是绝对的)
- 参数位置。GET 带参数请求是将参数缀到 URL 之后,POST 带参数请求是将参数放到请求体中(也不是绝对的)
- 安全性。POST 请求
相对
GET 安全一些,因为在浏览器中 GET 请求参数会暴露在地址栏 - GET 请求大小有限制,一般为 2K,而 POST 请求则没有大小限制
到了这里,关于node 学习 - HTTP模块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!