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

// 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. 响应内容中文乱码的解决办法
response.setHeader('content-type','text/html;charset=utf-8');
  1. 端口号被占用
Error: listen EADDRINUSE: address already in use :::9000
  5. 关闭当前正在运行监听端口的服务(`使用较多`)
  6. 修改其它端口号
  1. HTTP 协议默认端口是 80,HTTPS 协议的默认端口是443。HTTP 服务开发常用的端口有 3000,8080,8090,9000 等

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

浏览器中查看 HTTP 报文

浏览器控制台,网络

提取 HTTP 请求报文

node 学习 - HTTP模块_第1张图片

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:这个请求是属于浏览器自动发送的请求

获取请求体

//这种方法了解就行,有更好用的办法
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

绝对路径

node 学习 - HTTP模块_第2张图片

相对路径

相对路径不太可靠,和页面路径相关,参照页面 URL
node 学习 - HTTP模块_第3张图片

使用场景

包括但不限于如下场景:

  • 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 请求情况

  • 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 请求则没有大小限制

你可能感兴趣的:(学习,http,node.js)