Node.js -- http模块

发布于:2024-04-28 ⋅ 阅读:(19) ⋅ 点赞:(0)


在这节内容之前我们主要学习前端开发,从现在开始我们就要学习后端开发。

1. 前置知识

1.1 创建HTTP 模块

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

在这里插入图片描述
通过上面图片显示的访问方法加上fiddler 的使用就可以得到相应的请求报文和响应报文。

1.2 HTTP 服务的相关注意事项

在这里插入图片描述

  1. 停止HTTP 服务
    在哪个命令行启动的HTTP 服务就在那个命令行停止HTTP 服务

  2. 更新代码必须重新启动HTTP 服务

  3. 响应体为汉字会出现乱码

// 2.创建服务对象
const server = http.createserver((request,response) => {
// response.end('Hello HTTP');// 设置响应体
response.setHeader('content-type','text/html;charset=utf-8');// 设置响应头
response.end('你好');// 设置响应体
);
  1. 端口号被占用
  • 关闭当前正在运行监听端口的服务(使用较多)
  • 修改为其他端口号
  1. HTTP协议默认端口是80。HTTPS协议的默认端口是443,HTTP服务开发常用端口有3000,8080,8090,9000等

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

注:资源监视器在utools 中可以搜索到,在侦听端口找到相应端口正在运行的程序的PID ,再在vscode 中右键打开任务管理器通过PID 关闭相应程序。

1.3 浏览器查看HTTP 报文

在这里插入图片描述

  1. 查看请求报文

查看请求头:标头 --> 请求标头(里面全部内容均为请求头)

查看请求头:请求表头右侧查看源代码可以查看请求行和请求头

查看请求体:
get 请求的请求体一般都是为空的,所以我们可以改为post 请求
在这里插入图片描述
表单的提交时发出的请求就是post 请求。

在这里插入图片描述
载荷除了能够查询请求体内容之外,还可以快速查询URL 中查询字符串的内容!
在这里插入图片描述

  1. 查看响应结果
    与查看请求报文方式类似;
    但在查询响应体是要点击响应而不是载荷

2. 获取请求报文内容

获取请求报文是为了能够返回正确的响应结果。

2.1 获取请求行

// 2.创建服务对象
const server = http.createserver((request,response) => {
// 获取请求的方法
console.log(request.method);
// 获取请求的url
console.log(request.url);// 只包含url中的路径与查询字符串
// 获取HTTP协议的版本号
console.log(request.httpVersion);
// 获取HTTP的请求头
console.log(request.headers);// 返回一个对象
console.log(request.headers.host);// 获取请求头的某个解析值
response.end('http');// 设置响应体
});

2.2 获取请求体

(这种方法了解即可,之后会再进行学习)

// 2.创建服务对象
const server =  http.createServer((request,response) => { 
// 1.声明一个变量
let body = '';
// 2.绑定data事件
request.on('data',chunk => {
body += chunk;
// body += chunk.toString();
})
// 3.绑定end事件
request.on('end',() => { 
console.log(body);
// 响应
response.end('Hello HTTP');
})
})

2.3 获取请求路径与查询字符串(重要)

(1) 方式一

// 导入http模块
const http = require('http');
// 1.导入ur1模块
const url = require('ur1');// Node.js 内置模块 可以对URL 进行解析
// 2.创建服务对象
const server =  http.createServer((request,response) => { 
// 2.解析request.url
// console.log(request.url);// 字符串形式(路径+查询字符串)
let res = url.parse(request.url,true);// query 转化为了对象形式
// 路径
let pathname = res.pathname;
// 查询字符串
let keyword = res.query.keyword;
console.log(keyword);
response.end('url');
)
// 3.监听端口,启动服务
server.listen(9000,() => {
console.log('服务已经启动...')
})

(2) 方式二

// 导入http模块
const http require('http');
// 不需要导入URL 模块

// 创建服务对象
const server = http.createserver((request,response) => {
// 实例化URL的对象
// let url = new URL('http://127.0.0.1:9000/search?a= 100&b=2081');
// let url = new URL('/search?a= 100&b=2081', 'http://127.0.0.1:9000');
let url = new URL(request.url, 'http://127.0.0.1');// 端口号无所谓
// 路径
console.log(url.pathname);
// 输出keyword查询字符串 
console.log(url.searchParams.get('keyword')) // url.searchParams 是对象形式
response.end('url new');
)}

// 监听端口,启动服务
server.listen(9000,() => {
console.log('服务已经启动...')
});

3. HTTP 请求练习

练习要求:
当请求类型为get 且请求路径为/login 就显示登录页面;当请求类型为get 且请求路径为/reg 就显示注册页面。
在这里插入图片描述

const http = require('http')

const server = http.createServer((request, response) => {
    let { method } = request;
    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') {
        response.end('注册页面')
    } else {
        // 如果某个请求一直没有响应,持续占用资源只会一直浪费,所以需要对这种情况进行响应
        response.end('Not Found')
    }
})

server.listen(9000, () => {
    console.log('go');
})

4. 设置HTTP 响应报文

在这里插入图片描述

4.1 设置响应报文讲解

const http = require('http')

const server = http.createServer((request, response) => {
    // 1.设置响应状态码
response.statusCode = 203;
response.statusCode = 404;// 后者会覆盖前者
// 2.响应状态的描述
response.statusMessage = 'iloveyou'// 几乎不使用 因为一般响应状态码有对应的响应状态描述
// 3.响应头
// response.setHeader('content-type','text/html;charset=utf-8');
// response.setHeader('Server','Node.js');
// response.setHeader('myHeader','test test test');// 设置自定义响应头
response.setHeader('test',['a','b','c']);// 设置多级同名响应头
})
// 4.响应体的设置
response.write('love');
response.write('love');// write() 是可以多次调用的 但是end() 不可以
// response.end();// 设置响应体 一般来说我们使用了write() 设置响应体就不会再去使用end()

server.listen(9000, () => {
    console.log('go');
})

在这里插入图片描述

4.2 练习

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

const http = require('http')
const fs = require('fs')

const server = http.createServer((request, response) => {
    let table = fs.readFileSync(__dirname + '/12-table.html')
    response.end(table) // end() 中既能传入字符串也能传入Buffer
})

server.listen(9000,() => {
    console.log('go');
})

5. 网页资源加载基本过程

5.1 讲解

请求一般都是并行的,不需要等到服务器响应上一个请求就可以进行下一个请求的发送。在这里插入图片描述
比如说下面一个页面:
在这里插入图片描述

  1. 首先发送的请求就是index.html;
  2. 接着可能遇到外联CSS 文件,就需要再次发送请求;
  3. 遇到图片再次发送请求;
  4. 再接着遇到JS 文件,就再次发送请求;
  5. 其中还有浏览器自行发起的favicon.ico 请求,以及vscode Live Server 插件发送的请求

5.2 实现网页引入外部资源

这部分内容可以让上面4.2 练习更完善一点。

对于上面4.2 练习的内容我们如果将html 文件中css 部分和js 部分使用外联式引入,那么只靠我们写出的那部分代码是无法成功显示处我们想要的页面的,我们需要对每个请求都作出相应的响应。

let { pathname } = new URL(request.url, 'http://127.0.0.1');
    if (pathname === '/') {
        let table = fs.readFileSync(__dirname + '/12-table.html')
        response.end(table)
    }
    else if (pathname === '/12-table.css') {
        let css = fs.readFileSync(__dirname + '/12-table.css')
        response.end(css)
    }
    else if (pathname === '/12-table.js') {
        let js = fs.readFileSync(__dirname + '/12-table.js')
        response.end(js)
    } else {
        response.statusCode = 404;
        response.end('<h1>404 Not Found</h1>')
    }

现在看来如果一个html 文件引入多个css 文件或者js 文件,那么就需要写多个else if ,这是很麻烦的,但是我们后面会改进这种写法。

6. 静态资源和动态资源

6.1 介绍

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

这些资源都是相对于项目上线之后来说的。

6.2 搭建静态资源服务

创建一个HTTP服务,端口为900,满足如下需求
GET /index.html 响应 page/index.html的文件内容
GET /css/app.css 响应 page/css/app,css的文件内容
GET /images/logo.png 响应page/images/logo.png的文件内容

在这里插入图片描述
如果我们按照上面这种方式边写代码是很麻烦的,但是我们发现pathname 问年路径与我们读取的文件的路径有相似之处,我们可以利用这点对我们的代码进行简化。

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')
    let filePath = __dirname + pathname
    // 这个地方我们先使用异步读取文件 
    // 因为我们还没有学习同步读取文件的错误处理方式
    fs.readFile(filePath, (err, data) => {
        response.setHeader('content-type','text/html;charset=utf-8');
        if (err) {
            response.statusCode = 500
            response.end(err.toString())
            return;
        }
        response.end(data)
    })
})

server.listen(9000, () => {
    console.log('go');
})

6.3 静态资源目录和网站根目录

静态资源目录和网站根目录其实所指的是同一个东西,只是不同的叫法而已。

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

// 之前的写法
let { pathname } = new URL(request.url, 'http://127.0.0.1')
    let filePath = __dirname + pathname
// 使用网站根目录 之后也方便根据需求修改
let { pathname } = new URL(request.url, 'http://127.0.0.1')
let root = __dirname +'/网站根目录/'
    let filePath = root + pathname

思考:
vxcode 中使用Live Server 访问html 时,它启动的服务网站根目录是谁?

就是以所打开的文件夹作为网站根目录

7. 网页URL 补充内容

网页URL 主要分为绝对路径和相对路径。

7.1 绝对路径

在这里插入图片描述
其中我们使用第一种形式比较多。

在这里插入图片描述

7.2 相对路径

在这里插入图片描述
相对路径在平常用的多,但是在项目运行阶段时用的很少;
因为相对路径不可靠,当页面URL 不正常的时候我们再去使用相对路径去获取资源就会出现异常。

7.3 页面URL 使用场景

包括但不限于如下场景:

  • a 标签href
  • link标签href
  • script 标签src
  • img 标签src
  • video audio 标签src
  • form 中的action
  • AJAX 请求中的URL

8. 设置MIME 类型

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

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类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的下载效果.

const path = require('path')
if (err) {
            response.statusCode = 500;
            // response.setHeader('content-type','text/html;charset=utf-8');
            response.end(err.toString())
            return;
        }
        // 获取文件的后缀名
        let ext = path.extname(filePath)
        // 获取对应的类型
        let type = mimes[ext]
        if (type) {
            // 匹配到了
            response.setHeader('content-type',type)
        } else {
            // 没有匹配到
            response.setHeader('content-type','application/octet-stream')
        }
        // 响应文件内容
        response.end(data)

9. 解决乱码问题

一般来说,我们只需要给html 文件设置字符集,因为相应的css / js 文件会根据html 文件的字符集进行解析。

if(type){
if(html){
response.setHeader('content-type',type+';charset=utf-8')
}else{
response.setHeader('content-type',type)
}

10. 完善错误处理

  1. 资源找不到错误
// 判断错误的代号
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>');
// 其他错误
defalt:
response.statusCode = 500;
response.end('<h1>Interval Server Error</h1>');
}

  1. 请求方法错误
if(request.method !=='GET'){
response.statusCode = 405;
response.end('<h1>405 Method Not Allowed</h1>');
return;
}

11. GET / POST 请求

11.1 场景小结

GET请求的情况:

  • 在地址栏直接输入url 访问
  • 点击a 链接
  • link 标签引入css
  • script 标签引入js
  • video 与audio 引入多媒体
  • img 标签引入图片
  • form 标签中的method 为get(不区分大小写)
  • ajax 中的get 请求

POST请求的情况:

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

11.2 两种请求的区别

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

  1. 作用。GET主要用来获取数据,PoST主要用来提交数据
  2. 参数位置。GET带参数请求是将参数缀到URL之后,POST带参数请求是将参数放到请求体中
  3. 安全性。POST请求相对GET安全一些,因为在浏览器中参数会暴露在地址栏
  4. GET请求大小有限制,一般为2K,而P0ST请求则没有大小限制