http模块

更新时间: 2024-10-30 11:45:36

# 创建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('服务已经启动....')
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# HTTP服务的注意事项

  1. 命令行 ctrl + c 停止服务
  2. 当服务启动后,更新代码 必须重启服务才能生效
  3. 响应内容中文乱码的解决办法
    response.setHeader('content-type','text/html;charset=utf-8')
  4. 端口号被占用 Error: listen EADDRINUSE: address already in use ::: 9000
  • 关闭当前正在运行监听端口的服务(使用较多)
  • 修改其他端口号
  1. 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(){}

注意

  1. reuqest.url 只能获取路径以及查询字符串,无法获取URL中的域名以及协议的内容
  2. request.headers将请求信息转化成一个对象,并将属性名都转化成了【小写】
  3. 关于路径:如果访问网站的时候,只填写了IP地址或者是域名信息,此时请求的路径为【/】
  4. 关于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('服务已经启动....')
})
1
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('服务已经启动....')
})
1
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('服务已经启动....')
})
1
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('服务已经启动....')
})
1
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')
1
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('服务已经启动....')
})
1
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('服务已经启动....')
})
1
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('服务已经启动....')
})
1
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('服务已经启动....')
})
1
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('服务已经启动....')
})
1
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('服务已经启动....')
})
1
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请求则没有大小限制