node 学习 - HTTP模块

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 服务

javascript 复制代码
// 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 服务注意事项

  1. 命令行ctrl + c停止服务
  2. 当服务器启动后,更新代码必须重启服务后才能生效
  3. 响应内容中文乱码的解决办法
javascript 复制代码
response.setHeader('content-type','text/html;charset=utf-8');
  1. 端口号被占用
javascript 复制代码
Error: listen EADDRINUSE: address already in use :::9000
复制代码
  5. 关闭当前正在运行监听端口的服务(`使用较多`)
  6. 修改其它端口号
  1. HTTP 协议默认端口是 80,HTTPS 协议的默认端口是443。HTTP 服务开发常用的端口有 3000,8080,8090,9000 等

如果端口被其它程序占用,可以使用资源监视器找到占用端口的程序,然后使用任务管理器关闭对应的程序

浏览器中查看 HTTP 报文

浏览器控制台,网络

提取 HTTP 请求报文

javascript 复制代码
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('你好,世界!') //设置响应体,并结束服务
})

注意事项

  1. request.url 只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容
  2. request.headers 将请求信息转化成一个对象,并将属性名都转化成了『小写』
  3. 关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为『 / 』
  4. 关于 favicon.ico:这个请求是属于浏览器自动发送的请求

获取请求体

javascript 复制代码
//这种方法了解就行,有更好用的办法
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 报文中的请求路径及字符串

javascript 复制代码
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 响应报文

javascript 复制代码
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 文件,那在请求的时候,会调用多次接口,分开请求外部资源

所以,需要区分开请求的资源,不然都会返回同样的数据

javascript 复制代码
//方法一:在 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文件,字体文件等

动态资源是指内容经常更新的资源,例如百度首页,网易首页,京东搜索列表页面等

搭建静态资源服务

javascript 复制代码
//方法二:搭建静态资源服务
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 类型)是一种标准,用来表示文档、文件或字节流的性质和格式。

javascript 复制代码
mime 类型结构:[type] / [subType]
例如:text/html  text/css  image/jpeg  image/png  application/json

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

下面是常见文件对应的 mime 类型

javascript 复制代码
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 类型会更规范一点

javascript 复制代码
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,例如
javascript 复制代码
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 请求情况

  • form 标签中的 method 为 post (不区分大小写)
  • AJAX 中的 post 请求

GET 和 POST 请求的区别

GET 和 POST 是 HTTP 协议请求的两种方式,主要有如下几个区别

  1. 作用。GET 主要用来获取数据,POST 主要用来提交数据(并不是绝对的)
  2. 参数位置。GET 带参数请求是将参数缀到 URL 之后,POST 带参数请求是将参数放到请求体中(也不是绝对的)
  3. 安全性。POST 请求相对GET 安全一些,因为在浏览器中 GET 请求参数会暴露在地址栏
  4. GET 请求大小有限制,一般为 2K,而 POST 请求则没有大小限制
相关推荐
癫狂的兔子1 小时前
【Python】【NumPy】学习笔记
python·学习·numpy
YJlio1 小时前
Streams 学习笔记(12.2):看见 NTFS 隐藏的备用数据流(ADS)
服务器·笔记·学习
im_AMBER1 小时前
Leetcode 81 【滑动窗口(定长)】
数据结构·笔记·学习·算法·leetcode
崇山峻岭之间1 小时前
Matlab学习笔记03
笔记·学习·matlab
崇山峻岭之间1 小时前
Matlab学习记录08
开发语言·学习·matlab
山土成旧客2 小时前
【Python学习打卡-Day25】从程序崩溃到优雅处理:掌握Python的异常处理艺术
人工智能·python·学习
Bruce_Liuxiaowei2 小时前
网站敏感文件_目录大全(分类记忆+风险标注)
运维·网络·网络协议·http·网络安全·https
程序员笨鸟2 小时前
[特殊字符] React 高频 useEffect 导致页面崩溃的真实案例:从根因排查到彻底优化
前端·javascript·学习·react.js·面试·前端框架
大布布将军3 小时前
⚡️ 后端工程师的护甲:TypeScript 进阶与数据建模
前端·javascript·程序人生·typescript·前端框架·node.js·改行学it
YJlio3 小时前
Strings 学习笔记(12.1):从二进制里“扒”出明文信息的瑞士军刀
服务器·笔记·学习