Node.js—http模块

news2024/11/15 11:45:32

目录

  • 1、HTTP 协议
    • 1.1 概念
    • 1.2 请求报文的组成
    • 1.3 HTTP 的请求行
    • 1.4 HTTP 请求头
    • 1.5 HTTP 的请求体
    • 1.6 响应报文的组成
  • 2、创建 HTTP 服务
    • 2.1 操作步骤
    • 2.2 测试
    • 2.3 注意事项
  • 3、获取 HTTP 请求报文
    • 3.1 请求方法 request.method
    • 3.2 请求版本 request.httpVersion
    • 3.3 请求路径 request.url
    • 3.4 URL 路径 require('url').parse(request.url).pathname
    • 3.5 URL 查询字符串 require('url').parse(request.url, true).query
    • 3.6 实例化 URL 的对象
    • 3.7 请求版本 request.headers
    • 3.8 请求体
    • 3.9 小案例
  • 4、设置 HTTP 响应报文
    • 4.1 设置响应状态码 response.statusCode
    • 4.2 设置响应状态描述 response.statusMessage ( 用的非常少 )
    • 4.3 设置响应头信息 response.setHeader('头名', '头值')
    • 4.4 设置响应体response.write('xx')、response.end('xxx')
    • 4.5 小案例
  • 5、网页资源的基本加载过程
  • 6、静态资源服务
    • 6.1 什么是静态资源
    • 6.2 网站根目录或静态资源目录
    • 6.3 网页中的 URL
      • 6.3.1 绝对路径
      • 6.3.2 相对路径
      • 6.3.3网页中使用 URL 的场景小结
    • 6.4 设置资源类型(mime类型)
  • 7、GET 和 POST 请求
    • 7.1 场景小结
    • 7.2 GET和POST请求的区别
  • 参考

在介绍http模块之前先来了解一下什么是HTTP

1、HTTP 协议

1.1 概念

HTTP(hypertext transport protocol)协议;中文叫超文本传输协议
是一种基于TCP/IP的应用层通信协议
这个协议详细规定了 浏览器 和万维网 服务器之间互相通信的规则。
协议中主要规定了两个方面的内容

  • 客户端:用来向服务器发送数据,可以被称之为请求报文
  • 服务端:向客户端返回数据,可以被称之为响应报文

报文:可以简单理解为就是一堆字符串

1.2 请求报文的组成

  • 请求行
  • 请求头
  • 空行
  • 请求体

1.3 HTTP 的请求行

  • 请求方法(getpostputdelete等)
  • 请求 URL(统一资源定位器
    例如:http://www.baidu.com:80/index.html?a=100&b=200#logo
    • http: 协议(https、ftp、ssh等)
    • www.baidu.com 域名
    • 80 端口号
    • /index.html 路径
    • a=100&b=200 查询字符串
    • #logo 哈希(锚点链接)
    • HTTP协议版本号

1.4 HTTP 请求头

  • 格式:『头名:头值』
  • 常见的请求头有:
    在这里插入图片描述

1.5 HTTP 的请求体

请求体内容的格式是非常灵活的,

  • (可以是空)==> GET请求,
  • (也可以是字符串,还可以是JSON)===> POST请求

例如:

  • 字符串:keywords=手机&price=2000
  • JSON:{“keywords”:“手机”,“price”:2000}

1.6 响应报文的组成

  • 响应行
HTTP/1.1 200 OK
  1. HTTP/1.1:HTTP协议版本号
  2. 200:响应状态码 404 Not Found 500 Internal Server Error
  3. 还有一些状态码,参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
    OK:响应状态描述

响应状态码和响应字符串关系是一一对应的。

  • 响应头

Cache-Control: 缓存控制 private 私有的,只允许客户端缓存数据
Connection:链接设置
Content-Type:text/html;charset=utf-8 设置响应体的数据类型以及字符集,响应体为html,字符集utf-8
Content-Length:响应体的长度,单位为字节

  • 空行
  • 响应体
    • 响应体内容的类型是非常灵活的,常见的类型有 HTML、CSS、JS、图片、JSON

2、创建 HTTP 服务

2.1 操作步骤

//1. 导入 http 模块
const http = require('http')
//2. 创建服务对象 create 创建 server 服务
// request 意为请求. 是对请求报文的封装对象, 通过 request 对象可以获得请求报文的数据
// response 意为响应. 是对响应报文的封装对象, 通过 response 对象可以设置响应报文
const server = http.createServer((request, response) => {
    response.end('hello world')
})
//3. 监听端口, 启动服务
server.listen(9000,()=>{
    console.log('服务已经启动, 端口 9000 监听中...');
})

http.createServer 里的回调函数的执行时机: 当接收到HTTP 请求的时候,就会执行。

2.2 测试

  • 浏览器请求对应端口
    • http://127.0.0.1:9000

127.0.0.1是 回送地址 ,指本地机,一般用来测试使用。

浏览器返回结果
在这里插入图片描述

2.3 注意事项

  1. 命令行 ctrl + c 停止服务
  2. 当服务启动后,更新代码 必须重启服务才能生效
  3. 响应内容中文乱码的解决办法
	//设置响应头
    response.setHeader('content-type', 'text/html;charset=utf-8')
  1. 端口号被占用
Error: listen EADDRINUSE: address already in use :::9000
  1. 关闭当前正在运行监听端口的服务 ( 使用较多 )
  2. 修改其他端口号
  3. 在资源管理器中的侦听端口中找到9000端口,然后记下它的PID值;打开任务管理器,在详细信息中找到对应的PID值,右键结束任务

5.HTTP 协议默认端口是 80 。HTTPS 协议的默认端口是 443, HTTP 服务开发常用端口有 3000,8080,8090,9000 等

3、获取 HTTP 请求报文

想要获取请求的数据,需要通过 request 对象

在这里插入图片描述

3.1 请求方法 request.method

//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
  //获取请求的方法
  console.log(request.method);
  response.end('http'); //设置响应体
});

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

返回的结果:

在这里插入图片描述

3.2 请求版本 request.httpVersion

//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
  //获取 HTTP 协议的版本号
  console.log(request.httpVersion);
  response.end('http'); //设置响应体
});

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

返回结果:
在这里插入图片描述

3.3 请求路径 request.url

//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
  //获取请求的 url
  console.log(request.url);// 只包含 url 中的路径与查询字符串
  response.end('http'); //设置响应体
});

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

返回的结果:
在这里插入图片描述
测试地址:
http://127.0.0.1:9000/search?name=hh&password=123

注意:

  • request.url 只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容
  • 关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为 /
    - 关于 favicon.ico:这个请求是属于浏览器自动发送的请求

3.4 URL 路径 require(‘url’).parse(request.url).pathname

//导入 http 模块
const http = require('http');
//1. 导入 url 模块
const url = require('url');

//创建服务对象
const server = http.createServer((request, response) => {
  let res = url.parse(request.url);
  console.log(res.pathname);
  response.end('url');
});

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

在这里插入图片描述
测试地址:
http://127.0.0.1:9000/search?name=hh&password=123

3.5 URL 查询字符串 require(‘url’).parse(request.url, true).query

我们先来看看url.parse(request.url)不加第二个参数true的结果

//导入 http 模块
const http = require('http');
//1. 导入 url 模块
const url = require('url');

//创建服务对象
const server = http.createServer((request, response) => {
  let res = url.parse(request.url);
  console.log(res);
  response.end('url');
});
//监听端口, 启动服务
server.listen(9000, () => {
  console.log('服务已经启动....')
});

返回的结果:
在这里插入图片描述

  • query的值是一个字符串,这样对我们是不友好的。
  • 我们再来看看加上true的结果:
  let res = url.parse(request.url,true);
  console.log(res);

在这里插入图片描述

query的值变成了一个对象,这样我们就可以通过对象.的形式拿到里面对应的值了

  • 比如我们想拿到name的值,可以这样:
  console.log(res.query.name);

返回结果:
在这里插入图片描述

3.6 实例化 URL 的对象

  • 这种方法相对于之前的 parse(request.url)来说是优化的。
  • 我们先来看一下它返回的url是怎样的对象。
//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
  //实例化 URL 的对象
  let url = new URL(request.url, 'http://127.0.0.1');
  console.log(url);
});

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

返回的结果:

URL {
  href: 'http://127.0.0.1/search?name=hh&password=123',
  origin: 'http://127.0.0.1',
  protocol: 'http:',
  username: '',
  password: '',
  host: '127.0.0.1',
  hostname: '127.0.0.1',
  port: '',
  pathname: '/search',
  search: '?name=hh&password=123',
  searchParams: URLSearchParams { 'name' => 'hh', 'password' => '123' },
  hash: ''
}
  • 相比 之前的方法多了一条 searchParams: URLSearchParams
  • 我们就可以通过这个对象来拿到我们的查询字符串路径的获取和之前是一样。
//导入 http 模块
const http = require('http');

//创建服务对象
const server = http.createServer((request, response) => {
  //实例化 URL 的对象
  let url = new URL(request.url, 'http://127.0.0.1');
  console.log(url);
//输出路径
  console.log(url.pathname);
  //输出 keyword 查询字符串
  console.log(url.searchParams.get('name')); 
  response.end('url new');
});

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

返回结果:
在这里插入图片描述

3.7 请求版本 request.headers

//1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
  //获取 HTTP 的请求头
  console.log(request.headers);
  response.end('http'); //设置响应体
});

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

返回的是一个对象

{
  host: '127.0.0.1:9000',
  connection: 'keep-alive',
  'cache-control': 'max-age=0',
  'sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"',
  'sec-ch-ua-mobile': '?0',
  'sec-ch-ua-platform': '"Windows"',
  'upgrade-insecure-requests': '1',
  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36', 
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
  'sec-fetch-site': 'none',
  'sec-fetch-mode': 'navigate',
  'sec-fetch-user': '?1',
  'sec-fetch-dest': 'document',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'zh-CN,zh;q=0.9'
}
{
  host: '127.0.0.1:9000',
  connection: 'keep-alive',
  'sec-ch-ua': '"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"',
  'sec-ch-ua-mobile': '?0',
  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36', 
  'sec-ch-ua-platform': '"Windows"',
  accept: 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
  'sec-fetch-site': 'same-origin',
  'sec-fetch-mode': 'no-cors',
  'sec-fetch-dest': 'image',
  referer: 'http://127.0.0.1:9000/search?name=hh&password=123',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'zh-CN,zh;q=0.9'
}
  • 注意事项:
    request.headers 将请求信息转化成一个对象,并将属性名都转化成了小写

    既然返回的是一个对象,我们就可以通过对象.的形式获取其中的值。

  console.log(request.headers.host);

返回的结果:
在这里插入图片描述

3.8 请求体

  • request.on('data', function(chunk){})
  • request.on('end', function(){});
//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');
  });
});

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

返回结果:
在这里插入图片描述

由于我们默认是GET请求,所以请求体默认是空的,我们通过form表单设置为POST在请求一次。

在这里插入图片描述
在这里插入图片描述

3.9 小案例

  • 按照以下要求搭建 HTTP 服务

在这里插入图片描述

 //1. 导入 http 模块
const http = require('http');
//2. 创建服务对象
const server = http.createServer((request, response) => {
  //获取请求的方法
  let {method} = request;
  //获取请求的 url 路径
  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'){ // register 注册
    response.end('注册页面');
  }else{
    response.end('Not Found');
  }
});
//3. 监听端口 启动服务
server.listen(9000, () => {
  console.log('服务已经启动.. 端口 9000 监听中....');
}) 

案例效果图:
在这里插入图片描述

4、设置 HTTP 响应报文

在这里插入图片描述

4.1 设置响应状态码 response.statusCode

//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
  //1. 设置响应状态码
  response.statusCode = 203
  response.end('hh')
});
//监听端口, 启动服务
server.listen(9000, () => {
  console.log('服务已经启动....')
}); 

返回结果:

在这里插入图片描述

4.2 设置响应状态描述 response.statusMessage ( 用的非常少 )

//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
  //1. 设置响应状态码
 response.statusCode = 404;
   //2. 响应状态的描述 
 response.statusMessage = 'I love you'
  response.end('hh')
});
//监听端口, 启动服务
server.listen(9000, () => {
  console.log('服务已经启动....')
}); 

返回的结果:

在这里插入图片描述

4.3 设置响应头信息 response.setHeader(‘头名’, ‘头值’)

//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
  //1. 设置响应状态码
  // response.statusCode = 404;
  // //2. 响应状态的描述 
  // response.statusMessage = 'I love you'
  //3. 响应头
  response.setHeader('content-type', 'text/html;charset=utf-8')
  response.setHeader('Server','Node,js')
  response.end('哈哈')
});
//监听端口, 启动服务
server.listen(9000, () => {
  console.log('服务已经启动....')
}); 

返回的结果:

在这里插入图片描述

  • 注:有时候我们的头名有多个不同的头值,那我们该如何设置呢?

示例:

  response.setHeader('coke','one,tow,three')

在这里插入图片描述

这样子设置是不行的,我们必须通过数组的形式来完成。

  response.setHeader('coke',['one','tow','three'])

在这里插入图片描述

4.4 设置响应体response.write(‘xx’)、response.end(‘xxx’)

//导入 http 模块
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
  //1. 设置响应状态码
  // response.statusCode = 404;
  // //2. 响应状态的描述 
  // response.statusMessage = 'I love you'
  //3. 响应头
  // response.setHeader('content-type', 'text/html;charset=utf-8')
  // response.setHeader('Server','Node,js')
  // response.setHeader('coke',['one','tow','three'])
  //4. 响应体的设置
  response.write('I')
  response.write('love')
  response.write('you')
  response.end('hh')
});

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

返回结果:
在这里插入图片描述

  • 注意:
write 和 end 的两种使用情况:
//1. write 和 end 的结合使用 响应体相对分散
response.write('xx');
response.write('xx');
response.write('xx');
response.end(); //每一个请求,在处理的时候必须要执行 end 方法的
//2. 单独使用 end 方法 响应体相对集中
response.end('xxx');

4.5 小案例

  • 搭建 HTTP 服务,响应一个 4 行 3 列的表格,并且要求表格有 隔行换色效果 ,且 点击 单元格能 高亮显示。
const http = require('http');
//创建服务对象
const server = http.createServer((request, response) => {
  response.end(`
  <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        table {
            border-collapse: collapse;

        }

        table td {
            padding: 20px 60px;
        }

        table tr:nth-child(odd) {
          background-color: rgb(73, 31, 177);
        }

        table tr:nth-child(even) {
            background-color: pink;
        }

        .active {
            background-color: black;
        }
    </style>
</head>

<body>
    <table border="1">
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </table>
    <script>
        document.querySelectorAll('td').forEach(item => {
            item.addEventListener('click', function () {
                let od = document.querySelector('table .active')
                if (od) {
                    od.classList.remove('active')
                }
                this.classList.add('active')
            })
        })
    </script>
</body>

</html>`)//设置响应体
});

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

显示效果:
在这里插入图片描述

  • 大家应该也发现了,响应体的内容太多,而且这样写没有代码高亮和提示,对我们来说是不友好的。

解决方法:
将响应体内容放在一个HTML文件中,通过fs模块来对此文件进行读取。

const http = require('http');
const fs = require('fs');
//创建服务对象
const server = http.createServer((request, response) => {
  // 读取文件内容
  let html = fs.readFileSync(__dirname + '/table1.html')
  response.end(html)//设置响应体
});

//监听端口, 启动服务
server.listen(9000, () => {
  console.log('服务已经启动....')
});
  • 如果我们将css和js各种单独放在一个文件当中,我们这样写是有问题的。

显示效果:
在这里插入图片描述

没有css样式js行为,为什么会这样呢?
我们先来看看浏览器响应体返回的结果是什么

在这里插入图片描述
在这里插入图片描述

  • css和js文件返回的依旧是HTML文件
  • 在服务器返回响应给我们的时候会执行下面这个回调函数,而回调函数里面只返回了一个html,所以会导致这样的结果发生。
const server = http.createServer((request, response) => {
  // 读取文件内容
  let html = fs.readFileSync(__dirname + '/table1.html')
  response.end(html)//设置响应体
});
  • 我们该如何解决?
    • 我们只需要给每一个路径进行判断,如果是这个文件,就返回该文件的内容。
// 导入模块
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')
    if (pathname === '/') {
        let html = fs.readFileSync(__dirname + '/index.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('404,NOT FOUND')
    }
})

server.listen(9000, () => {
    console.log('服务已启动');
})

显示效果:
在这里插入图片描述

  • 说到这其实又引申出另外一个问题,如果我们有很多个文件,那我们岂不是要对每个文件进行判断,这样是很麻烦的。
  • 所以我们又想到了另外一种解决办法。
  • 通过搭建静态服务资源来解决这个问题
  • 目录结构:
    在这里插入图片描述在这里插入图片描述
const http = require('http')
const fs = require('fs')
const path = require('path')
// 创建服务对象
const server = http.createServer((request, response) => {
    //获取请求url的路径
    let { pathname } = new URL(request.url, 'http://127.0.0.1')
    //声明一个变量
    let root = __dirname + '/page'
    //拼接文件路径
    let filePath = path.resolve(root + pathname)
    //读取文件 fs 异步 API
    fs.readFile(filePath, (err, data) => {
        if (err) {
            response.statusCode = 500
            response.end('文件读取失败···')
            return
        }
        response.end(data)
    })
})
server.listen(9000, () => {
    console.log('服务已启动');
})

5、网页资源的基本加载过程

在这里插入图片描述

  • 网页资源的加载都是循序渐进的,首先获取 HTML 的内容, 然后解析 HTML 在发送其他资源的请求,如 CSS,Javascript,图片等。

6、静态资源服务

6.1 什么是静态资源

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

6.2 网站根目录或静态资源目录

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

思考:vscode 中使用 live-server 访问 HTML 时, 它启动的服务中网站根目录是谁?

  • 答案:以打开的文件夹作为网站根目录

6.3 网页中的 URL

  • 网页中的 URL 主要分为两大类:相对路径绝对路径

6.3.1 绝对路径

  • 绝对路径可靠性强,而且相对容易理解,在项目中运用较多
    在这里插入图片描述
  • 我们最常用的是第三种
  • 如果将来我们要改变域名ip,用前两种方法会比较麻烦,一但改变,里面的链接路径全部要更改。
  • 第三种方法则随便你域名ip更改我都不受影响

6.3.2 相对路径

  • 相对路径在发送请求时,需要与当前页面 URL 路径进行 计算 ,得到完整 URL 后,再发送请求,学习阶 段用的较多。
  • 例如当前网页 url 为 http://www.atguigu.com/course/h5.html
    在这里插入图片描述

在实际工作当中我们都是使用绝对路径,因为相对路径不可靠,体现在于页面url的路径相关,它是参照页面url的,当页面url不正常的时候,我们使用相对路径去获取资源就会异常

6.3.3网页中使用 URL 的场景小结

  • 包括但不限于如下场景:
    • a 标签 href
    • link 标签 href
    • script 标签 src
    • img 标签 src
    • video audio 标签 src
    • form 中的 action
    • AJAX 请求中的 URL

6.4 设置资源类型(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',
mp4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json'

对于未知的资源类型,可以选择application/octet-stream 类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的 下载 效果

我们来对之前的代码进行改进一下:

//导入 http 模块
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;
  }
  //获取请求url的路径
  let {pathname} = new URL(request.url, 'http://127.0.0.1');
  //声明一个变量
  let root = __dirname + '/page';
  // let root = __dirname + '/../';
  //拼接文件路径
  let filePath = root + pathname;
  //读取文件 fs 异步 API
  fs.readFile(filePath, (err, data) => {
    if(err){
      console.log(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>');
        default:
          response.statusCode = 500;
          response.end('<h1>Internal Server Error</h1>');
      }
      
      return;
    }
    //获取文件的后缀名
    let ext = path.extname(filePath).slice(1);
    //获取对应的类型
    let type = mimes[ext];
    if(type){
      //匹配到了                          text/html;charset=utf-8
      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.响应头里面的字符集权重比HTML页面中meta标签字符集权重要高。
2.当css、js、img等资源进入到网页当中去,会根据网页的字符集进行处理,所以没必要给它们设置字符集。

7、GET 和 POST 请求

7.1 场景小结

  • GET 请求的情况:
    • 在地址栏直接输入 url 访问

    • 点击 a 链接

    • link 标签引入 css

    • script 标签引入 js

    • img 标签引入图片

    • form 标签中的 method 为 get (不区分大小写)

    • ajax 中的 get 请求

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

7.2 GET和POST请求的区别

  • GETPOST 是 HTTP 协议请求的两种方式。
    • GET 主要用来获取数据,POST 主要用来提交数据
    • GET 带参数请求是将参数缀到 URL 之后,在地址栏中输入 url 访问网站就是 GET 请求, POST带参数请求是将参数放到请求体中
    • POST 请求相对 GET 安全一些,因为在浏览器中参数会暴露`在地址栏
    • GET 请求大小有限制,一般为 2K,而 POST 请求则没有大小限制

参考

尚硅谷2023版Node.js零基础视频教程,nodejs新手到高手

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/440643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

shell 初级

判断当前磁盘剩余空间是否有20G&#xff0c;如果小于20G&#xff0c;则将报警邮件发送给管理员&#xff0c;每天检查一次磁盘剩余空间。 [rootlocalhost ~]# bash c.sh c.sh: line 7: echo剩余内存:1GB,小于20GB 判断web服务是否运行&#xff08;1、查看进程的方式判断该程序是…

ctfshow_愚人杯WEB之easy_flask

1、easy_flask 百度了下什么是flask&#xff0c;flask是一款非常流行的python web框架。 2、尝试步骤 &#xff08;1&#xff09;、搜索博文&#xff0c;查看该题是如何破解的 先注册账号&#xff0c;登录自己注册的账号&#xff0c;查看可访问内容&#xff08;可以查看到部…

深入探讨Linux驱动开发:字符设备驱动开发与测试

文章目录 一、字符设备驱动介绍1.设备驱动介绍 二、设备号1.设备号介绍2.分配与释放设备编号①dev_t类型②静态分配设备号③动态分配设备号④释放主次设备号⑤手动创建设备节点⑥自动创建设备节点⑦删除设备节点 三、字符设备注册1.cdev结构体2.注册cdev到内核 三、字符设备驱动…

黑马点评实战篇问题总结

缓存穿透 用户查询的数据在缓存和数据库中都不存在 这样的请求每次都会打到数据库上 解决方案&#xff1a; 1.缓存空字符串&#xff08;额外的内存消耗&#xff0c;可能造成短期的不一致&#xff09; 2.布隆过滤&#xff08;内存占用少&#xff0c;没有多余key&#xff0c;实现…

访问若依vue版后端api接口

访问若依vue版后端api接口 如何使用Talend API Tester进行访问若依vue-前后端分离版的后端api接口&#xff1f; 方法一&#xff1a; 写好一个后台api接口&#xff0c;启动项目 直接使用Talend API Tester进行访问后台api出现如下错误&#xff0c;原因是因为若依系统有jwt认证…

2023软件测试工程师涨薪攻略,3年如何达到30K?

1.软件测试如何实现涨薪 首先涨薪并不是从8000涨到9000这种涨薪&#xff0c;而是从8000涨到15K加到25K的涨薪。基本上三年之内就可以实现。 如果我们只是普通的有应届毕业生或者是普通本科那我们就只能从小公司开始慢慢往上走。 有些同学想去做测试&#xff0c;是希望能够日…

数据可视化的web工具 apache-superset

文章目录 简介安装window10Ubuntu1804CentOS8 配置连接数据库创建仪表板创建图表图表加入仪表板时间序列折线图 简介 Superset是一款由Airbnb开源的、目前由Apache孵化的&#xff0c;基于Flask-appbuilder搭建的“现代化的企业级BI&#xff08;商业智能&#xff09;Web应用程序…

很合适新手入门使用的Python游戏开发包pygame实例教程-02[如何控制飞行]

前面一篇博文&#xff0c;我们让飞机动起来了&#xff0c;但不是那么完美&#xff0c;我们继续来完善我们的游戏代码&#xff0c;本篇博文主要介绍获取按键的方式已经飞行的控制。 文章目录 一、获取按键的三种方式1、通过event.get配合pygame.key枚举2、通过event.get配合ord…

C++入门教程||C++ 文件和流||C++ 异常处理

C 文件和流 C 文件和流 到目前为止&#xff0c;我们已经使用了 iostream 标准库&#xff0c;它提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流。 本教程介绍如何从文件读取流和向文件写入流。这就需要用到 C 中另一个标准库 fstream&#xff0c;它定义…

《我的第一本算法书》读书笔记

《我的第一本算法书》读书笔记 作者&#xff1a;宫崎修一 石田保辉 ◆ 1-3 数组 在链表和数组中&#xff0c;数据都是线性地排成一列。在链表中访问数据较为复杂&#xff0c;添加和删除数据较为简单&#xff1b;而在数组中访问数据比较简单&#xff0c;添加和删除数据却比较复…

转行IT,怎么选专业?

转行IT&#xff0c;怎么选专业&#xff1f; 知己知彼&#xff0c;百战不殆 先清楚你自身的基础情况&#xff1a;学历、年龄、是否有基础、学习能力如何、自律性、时间管理能力、有没有生活压力、有没有家要养、车贷、房贷…… 思考的问题越现实&#xff0c;对你的帮助越大 选…

运营商大数据获客是什么,是如何实现精准获客的

长久以来&#xff0c;企业希望自己的产品获得更多的客户&#xff0c;那么就需要花钱做推广和营销。然而随着互联网和自媒体的发展&#xff0c;并不是钱花出去了&#xff0c;就能带来有效的流量和高质量的客户&#xff0c;费效比太高&#xff0c;精准度太差&#xff0c;没有好的…

神经网络初学者的激活函数指南

如果你刚刚开始学习神经网络&#xff0c;激活函数的原理一开始可能很难理解。但是如果你想开发强大的神经网络&#xff0c;理解它们是很重要的。 但在我们深入研究激活函数之前&#xff0c;先快速回顾一下神经网络架构的基本元素。如果你已经熟悉神经网络的工作原理&#xff0c…

STM32+ESP8266+QT客户端上位机显示DHT11温湿度与点灯

目录 1、简介 2、硬件连接 3、上位机源码 3.1 widget.h 3.2 widget.c 3.3 显示图 4、下位机源码 4.1 cubemax配置 4.2 keil源码 1、简介 本文使用STM32F103C8T6单片机使用单片机通过ESP8266WIFI模块与QT设计的上位机进行通讯&#xff0c;ESP8266设置AP模式。实现DHT11传…

跨越DDD从理论到工程落地的鸿沟

DDD作为一种优秀的设计思想&#xff0c;的确为复杂业务治理带来了曙光。然而因为DDD本身难以掌握&#xff0c;很容易造成DDD从理论到工程落地之间出现巨大的鸿沟。就像电影里面的桥段&#xff0c;只谈DDD理论姿势很优美&#xff0c;一旦工程落地就跪了…所以DDD的项目&#xff…

Android实战-RecyclerView+Glide刷新列表的若干bug

文章目录 前言一. RecyclerView中使用Glide出现加载图片闪烁1.1 提出问题1.2 查看源码1.3 ViewTarget和SimpleTarget 二. CustomTarget和CustomViewTarget2.1 onResourceCleared和onLoadCleared2.2 onLoadStarted和onResourceLoading 结束 前言 最近在项目中使用RecyclerViewG…

Java——合并两个排序的链表

题目链接 牛客在线oj题——合并两个排序的链表 题目描述 输入两个递增的链表&#xff0c;单个链表的长度为n&#xff0c;合并这两个链表并使新链表中的节点仍然是递增排序的。 数据范围&#xff1a; 0≤n≤1000&#xff0c;−1000≤节点值≤1000 要求&#xff1a;空间复杂…

物联网定位技术|实验报告|实验二 多边定位算法、DV-HOP算法

在WSN定位中常常采用三边定位算法&#xff0c;试画图推导三边定位的计算公式&#xff0c;并表示为矩阵形式。 目录 1. 实验目标 2. 实验要求 3. 算法介绍 3.1基本内容介绍 3.2迭代多边定位算法 3.3 DV-HOP算法 4. 算法实现 4.1迭代多边定位算法 第一步&#xff1a;将数据读入内…

STM32HAL库USART外设配置流程及库函数讲解

HAL库中USART外设配置流程及库函数讲解 一说到串口通信&#xff0c;及必须说一下aRS-232/485协议。232协议标准物理接口就是我们常用的DB9串口线 RS-232电平&#xff1a; 逻辑1&#xff1a;-15~-3 逻辑0&#xff1a; 3~15 COMS电平&#xff1a; 逻辑1&#xff1a;3.3 逻辑0&a…

文件操作【下篇】

文章目录 &#x1f5c3;️5.文件的随机读写&#x1f4c1;5.1. fseek&#x1f4c1;5.2. ftell&#x1f4c1;5.3. rewind &#x1f5c3;️6.文本文件和二进制文件&#x1f5c3;️7.文件读取结束的判定&#x1f4c1;7.1. 被错误使用的 feof &#x1f5c3;️8.文件缓冲区 &#x1f…