http模块
# 创建HTTP服务端
// 1. 导入http模块
const http = require('http')
// 2. 创建服务对象
// request 意为请求,是对请求报文的封装对象,通过 request 对象可以获得请求报文的数据
// response 意为响应,是对响应报文的封装对象,通过 response 对象可以设置响应报文
const server = http.createServer((request, response) => {
// 当接收到HTTP请求的时候,就会执行
response.end('Hello HTTP Server') // 设置响应体
})
// 3. 监听端口,启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# HTTP服务的注意事项
- 命令行 ctrl + c 停止服务
- 当服务启动后,更新代码
必须重启服务才能生效
- 响应内容中文乱码的解决办法
response.setHeader('content-type','text/html;charset=utf-8') - 端口号被占用 Error: listen EADDRINUSE: address already in use ::: 9000
- 关闭当前正在运行监听端口的服务(使用较多)
- 修改其他端口号
- HTTP协议默认端口是80。HTTPS协议的默认端口是443,HTTP服务开发常用端口有 3000, 8080, 8090, 9000等
::: tips
如果端口被其他程序占用,可以使用资源监视器找到占用端口的程序,然后使用任务管理器关闭对应的程序
:::
# 获取HTTP请求报文
想要获取请求的数据,需要通过 request 对象
含义 | 语法 |
---|---|
请求方法 | request.method |
请求版本 | request.httpVersion |
请求路径 | request.url |
URL路径 | request('url').parse(request.url).pathname |
URL查询字符串 | request('url').parse(request.url, true).query |
请求头 | request.headers |
请求体 | request.on('data',function(chunk){}) |
request.on('end'),funciton(){} |
注意
- reuqest.url 只能获取路径以及查询字符串,无法获取URL中的域名以及协议的内容
- request.headers将请求信息转化成一个对象,并将属性名都转化成了【小写】
- 关于路径:如果访问网站的时候,只填写了IP地址或者是域名信息,此时请求的路径为【/】
- 关于favicon.ico: 这个请求是属于浏览器自动发送的请求
# 获取请求体
// 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')
})
response.end('http') // 设置响应体
})
// 3. 监听端口,启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 获取请求路径与查询字符串
// 导入http模块
const http = require('http')
// 1. 导入url模块
const url = require('url')
// 创建服务对象
const server = http.createServer((request, response) => {
// 2. 解析 request.url
// 路径
// let pathname = res.pathname
// 查询字符串
let res = url.parse(request.url, true)
console.log(res.query)
response.end('url') // 设置响应体
})
// 监听端口,启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 导入http模块
const http = require('http')
// 创建服务对象
const server = http.createServer((request, response) => {
let url = new URL(request.url, 'http://127.0.0.1')
console.log(url.searchParams.get('keyword'))
response.end('url new') // 设置响应体
})
// 监听端口,启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
# HTTP请求练习
::: success 按照以下要求搭建HTTP服务
请求类型 | 请求地址 | 响应体结果 |
---|---|---|
get | /login | 登录页面 |
get | /reg | 注册页面 |
:::
// 导入http模块
const http = require('http')
const url = require('url')
// 创建服务对象
const server = http.createServer((request, response) => {
response.setHeader('content-type','text/html;charset=utf-8')
let {pathname} = url.parse(request.url)
let {method} = request
if(method === 'GET' && pathname === '/login') {
response.end('登录页面')
}else if(method === 'GET' && pathname === '/reg') {
response.end('注册页面')
}else{
response.end('404')
}
})
// 监听端口,启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 设置HTTP响应报文
作用 | 语法 |
---|---|
设置响应状态码 | response.statusCode |
设置响应状态描述 | response.statusMessage(用得非常少) |
设置响应头信息 | response.setHeader('头名','头值') |
设置响应体 | response.write('xxx') |
response.end('xxx') |
write和end的两种使用情况:
// 1. write 和 end 的结合使用, 响应体相对分散
response.write('xx')
response.write('xx')
response.write('xx')
response.end(); // 每一个请求,在处理的时候必须要执行end方法
// 2. 单独使用end方法 响应体相对集中
response.end('xxx')
2
3
4
5
6
7
8
// 导入http模块
const http = require('http')
// 创建服务对象
const server = http.createServer((request, response) => {
// 设置响应状态码
response.statusCode = 203
// 响应状态的描述
response.statusMessage = 'iloveyou'
// 响应头
response.setHeader('content-type','text/html;charset=utf-8')
response.end('response')
})
// 监听端口,启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# HTTP响应练习
搭建HTTP服务,响应一个4行3列的表格,并且要求表格有隔行换色效果,且点击单元格能高亮显示。
::: success 答案 将表格写在单独的html中,然后:
// 导入http模块
const http = require('http')
const fs = require('fs')
// 创建服务对象
const server = http.createServer((request, response) => {
// 读取文件内容
let html = fs.readFileSync(__dirname + '/form.html')
response.end(html)
})
// 监听端口,启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
:::
# 实现网页引入外部资源
// 导入http模块
const http = require('http')
const fs = require('fs')
// 创建服务对象
const server = http.createServer((request, response) => {
// 获取请求url的路径
let {pathname} = new URL(request.url, 'http://127.0.0.1')
if(pathname === '/') {
let html = fs.readFileSync(__dirname + '/form.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 = 404
response.end('<h1>404 Not Found</h1>')
}
})
// 监听端口,启动服务
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 静态资源与动态资源
静态资源是指内容长时间不发生改变的资源,例如图片,视频,CSS文件,JS文件,HTML文件,字体文件等
动态资源指的是内容经常更新的资源,例如百度首页,网易首页,京东搜索列表页面等
# 搭建静态资源服务
/**
* 创建一个 HTTP 服务,端口为 9000,满足如下需求:
* GET /index.html 响应 page/index.html的文件内容
* GET /css/app.css 响应 page/css/app.css的文件内容
* GET /images/logo.png 响应 page/images/logo.png的文件内容
*/
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 + '/page' + pathname
fs.readFile(filePath, (err, data) => {
if(err) {
response.statusCode = 500
response.end('文件读取失败')
return
}
response.end(data)
})
})
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 静态资源目录与网站根目录
HTTP服务在哪个文件夹中寻找静态资源,那个文件夹就是静态资源目录,也称之为网站根目录
# 网页URL之绝对路径
绝对路径可靠性强,而且相对容易理解,在项目中运用较多
形式 | 特点 |
---|---|
http://atguigu.com/web | 直接向目标资源发送请求,容易理解。网站的外链会用到此形式 |
//atgui.com/web | 与页面URL的协议拼接形成完整URL再发送请求。大型网站用的比较多 |
/web | 与页面URL的协议,主机名,端口拼接形成完整URL再发送请求。中小型网站 |
# 网页URL之相对路径
相对路径在发送请求时,需要与当前页面URL路径进行计算,得到完整URL后,再发送请求,学习阶段用的较多,例如当前网页url为 http://www.atguigu.com/course/h5.html
|形式|最终的URL|
|./css/app.css|http://www.atguigu.com/course/css/app.css|
|js/app.js|http://www.atguigu.com/course/js/app.js|
|../img/logo.png|http://ww.atguigu.com/img/logo.png|
|../../mp4/show.mp4|http://www.atguigu.com/mp4/show.mp4|
# 网页URL的使用场景
包括但不限于如下场景:
- a标签href
- link标签href
- script标签src
- img标签src
- video audio 标签 src
- form中的action
- AJAX请求中的URL
# 设置mime类型
媒体类型(通常称为 Multipurpose Internet Mail 或 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 服务,端口为 9000,满足如下需求:
* GET /index.html 响应 page/index.html的文件内容
* GET /css/app.css 响应 page/css/app.css的文件内容
* GET /images/logo.png 响应 page/images/logo.png的文件内容
*/
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) => {
let {pathname} = new URL(request.url, 'http://127.0.0.1')
let filePath = __dirname + '/page' + pathname
fs.readFile(filePath, (err, data) => {
if(err) {
response.setHeader('content-type','text/html;charset=utf-8')
response.statusCode = 500
response.end('文件读取失败')
return
}
// 获取文件的后缀名
let ext = path.extname(filePath).slice(1)
// 获取对应的类型
let type = mimes[ext]
if(type) {
response.setHeader('content-type', type)
}else{
response.setHeader('content-type', 'application/octet-stream')
}
response.end(data)
})
})
server.listen(9000, () => {
console.log('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 解决乱码问题 ,完善错误处理
/**
* 创建一个 HTTP 服务,端口为 9000,满足如下需求:
* GET /index.html 响应 page/index.html的文件内容
* GET /css/app.css 响应 page/css/app.css的文件内容
* GET /images/logo.png 响应 page/images/logo.png的文件内容
*/
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
}
let {pathname} = new URL(request.url, 'http://127.0.0.1')
let filePath = __dirname + '/page' + pathname
fs.readFile(filePath, (err, data) => {
if(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>')
}
return
}
// 获取文件的后缀名
let ext = path.extname(filePath).slice(1)
// 获取对应的类型
let type = mimes[ext]
if(type) {
if(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('服务已经启动....')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 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协议请求的两种方式
- GET主要用来获取数据,POST主要用来提交数据
- GET带参数请求是将参数缀到URL之后,在地址栏中输入url访问网站就是GET请求,POST带参数请求是将参数放到请求体中
- POST请求相对GET安全一些,因为在浏览器中参数会暴露在地址栏
- GET请求大小有限制,一般为2K,而POST请求则没有大小限制