一篇文章搞懂node中的http模块

407 阅读7分钟

「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战

http模块可以创建http服务,提供客户端和服务器之间通信。

创建http服务

  1. http.createServer( )
  2. new http.Server( )

第一种方式:http.createServer( )

 const serve = http.createServer((req, res) => {
   res.end("hello server");
 });

第二种方式:htpp.Server( )

 const serve = new http.Server((req, res) => {
   res.end("nihao1");
 });

req是请求对象,res是响应对象

listen监听服务器

Server通过listen来创建和监听相应的服务。

参数:

  1. 监听的端口号,可以不传,系统会默认为我们分配

  2. 监听的主机号,可以不传,默认是0.0.0.0。通常可以传入127.0.0.1或localhost,localhost实际上一个本地域名,会被解析为127.0.0.1。127.0.0.1是回环地址。

    127.0.0.1:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收;

    正常的数据库包经常 应用层 - 传输层 - 网络层 - 数据链路层 - 物理层 ;

    而回环地址,是在网络层直接就被获取到了,是不会经过数据链路层和物理层的;

    比如我们监听 127.0.0.1时,在同一个网段下的主机中,通过ip地址是不能访问的;

    0.0.0.0:

    监听IPV4上所有的地址,再根据端口找到不同的应用程序;

    比如我们监听 0.0.0.0时,在同一个网段下的主机中,通过ip地址是可以访问的;

  3. 回调函数,当服务启动成功后执行的回调函数。

 const http = require("http");
 ​
 const serve = http.createServer((req, res) => {
   res.end("hello server");
 });
 ​
 serve.listen(8000, "localhost", () => {
   console.log("启动成功");
 });

request

创建服务后回调函数中会有传入两个参数,一个是request请求对象,另一个是响应对象response。

request中会包含很多的信息:

  1. 请求的URL
  2. 请求的方式,例如get和post请求
  3. 请求头,请求头中包含客户端信息,接受数据的格式,编码格式等
  4. 等好多信息
处理url

例如:请求地址为:localhost:8000/urser?user=wang&age=16

当请求为get请求时,我们需要对url进行解析,获取请求数据。我们可以使用node的内置模块url来解析url。

 //导入url
 const url = require("url");
 //解析url
 const parseUrl = url.parse(req.url);

通过url.parse解析url后会为我们生成一个对象:其中包括一下数据:

 Url {
   protocol: null,
   slashes: null,
   auth: null,
   host: null,
   port: null,
   hostname: null,
   hash: null,
   search: '?user=wang&age=16',
   query: 'user=wang&age=16',
   pathname: '/urser',
   path: '/urser?user=wang&age=16',
   href: '/urser?user=wang&age=16'
 }

解析完url之后,我们向获取到query中的键值对数据,我们可以通过 querystring来解析我们的query.

 //导入querystring
 const qs = require("querystring");
 //解析query
 const str = qs.parse(urlQuery);

通过qs.parse解析,我们可以得到一个对象。()

 { user: 'wang', age: '16' }
处理method

在Restful规范中,进行不同的操作我们应该使用不同的请求方式:

  1. GET:查询数据;
  2. POST:新建数据;
  3. PATCH:更新数据;
  4. DELETE:删除数据;

当我们使用post请求发送数据时,我们要在服务器中做相应的接受。

 const http = require("http");
 const url = require("url");
 const qs = require("querystring");
 const server = http.createServer((req, res) => {
   //解析url
   const {pathname} = url.parse(req.url);
   console.log(pathname);
   if (pathname === "/user") {
     if (req.method === "POST") {
       //设置编码格式为utf-8
       //req.setEncoding("utf-8");
       req.on("data", (data) => {
         //获取到的通过json来解析为真正的js对象
         const parseStr = JSON.parse(data);
         console.log(parseStr);
       });
     }
   }
   //解析query
 ​
   res.end("请求结束");
 });
 ​
 server.listen("8000", "0.0.0.0", () => {
   console.log("请求成功");
 });
 ​
heahers
 {
   'content-type': 'application/json',
   'cache-control': 'no-cache',
   'postman-token': '3b1b58d3-9514-4402-825b-9ee214d270f1',
   'user-agent': 'PostmanRuntime/7.6.0',
   accept: '*/*',
   host: 'localhost:8000',
   'accept-encoding': 'gzip, deflate',
   'content-length': '33',
   connection: 'keep-alive'
 }

headers会返回客户端的相关信息:

  • content-type: 表示这次请求携带的数据类型,text为单纯的为文本类型,application表示传输的是文件

image.png

  • content-length:文件的大小和长度

  • keep-alive:

    http是基于TCP协议的,但是通常在进行一次请求和响应结束后会立刻中断;

     在http1.0中,如果想要继续保持连接:
     
              浏览器需要在请求头中添加 connection: keep-alive; 
    

    服务器需要在响应头中添加 connection:keey-alive;

    当客户端再次放请求时,就会使用同一个连接,直到一方中断连接;

       在http1.1中,所有连接默认是 connection: keep-alive的;
    

    不同的Web服务器会有不同的保持 keep-alive的时间;

                 Node中默认是5s中
    
  • accept-encoding:告知服务器,客户端支持的文件压缩格式,比如js文件可以使用gzip编码,对应 .gz文件;

  • accept:告知服务器,客户端可接受文件的格式类型;*号表示可以接受所有的文件格式

  • user-agent:客户端的相关信息,不同的浏览器有不同的相应的客户端信息。

    例如:火狐浏览器:'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'

返回响应信息

  • wirte()方法,响应信息。

  • end()方法,可以传入三个参数 data , endcoding , callback

    此方法向服务器发出信号,表明所有响应头和正文都已发送;该服务器应认为此消息已完成。 response.end() 方法必须在每个响应上调用。

    如果指定了 data,则其效果类似于调用 response.write(data, encoding) 后跟 response.end(callback)

    如果指定了 callback,则将在响应流完成时调用。

状态码

Http状态码(Http Status Code)是用来表示Http响应状态的数字代码

常见的状态码:

状态码状态描述说明
200ok请求成功
301Moved Permanently永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302Found临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
400Bad Request客户端请求的语法错误,服务器无法理解
401Unauthorized当前请求需要用户验证。该响应必须包含一个适用于被请求资源的 WWW-Authenticate 信息头用以询问用户信息
403Forbidden服务器已经理解请求,但是拒绝执行它
404Not Found请求失败,请求所希望得到的资源未被在服务器上发现. 404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。
500Internal Server Error服务器遇到了不知道如何处理的情况。
503Service Unavailable服务器没有准备好处理请求。 常见原因是服务器因维护或重载而停机。可能需要一段时间之后才可以访问。

设置响应码:

 res.statusCode = 403;

设置响应头文件格式

  1. 通过setHeader()来设置
  2. 通过writeHead(状态码,{ })来设置响应头和状态码
     //1.通过setHeader()来设置
     res.setHeader("Content-Type", "text/plain;charset=utf8");
     res.statusCode = 403;
     //2.通过writeHead(状态码,{ })来设置相应头和状态码
     res.writeHead(200, {
         "Content-Type": "text/html;charset=utf8",
     });
     const header = res.getHeader("content-type");

http发送网络请求

发送网络请求有两种方式:

  1. http.get()
  2. http.request()

一、htthp.get()发送get请求

 //get发送请求
 http.get("http:localhost:8000/uesr", (res) => {
   res.on("data", (data) => {
     console.log(data.toString());
   });
   res.on("end", () => {
     console.log("获取到了结果");
   });
 });

二、http.request() 可以发送任何类型的网络请求

 ​
 //post发送请求
 const req = http.request(
   {
     method: "GET",
     hostname: "localhost",
     port: 8000,
     path: "/user?a=3&b=3",
   },
   (res) => {
     res.on("data", (data) => {
       console.log(data.toString());
     });
     res.on("end", () => {
       console.log("请求到了所有的数据");
     });
   }
 );
 ​
 req.end();

手动实现文件上传

上传图片:

使用到的方法:

  1. subString(start,end) :截取字符串
  2. repelace(原字符,替换为的字符)
  3. querystring中的qs.parse(解析的内容,分隔符,键值对的分隔符)
 const http = require("http");
 const fs = require("fs");
 const qs = require("querystring");
 const url = require("url");
 ​
 const server = http.createServer((req, res) => {
   const {query, pathname} = url.parse(req.url);
   //判断请求方式和pathname
   if (req.method === "POST") {
     if (pathname === "/upload") {
       //设置编码为二进制
       req.setEncoding("binary");
       //获取到boundry
       var boundary = req.headers["content-type"]
         .split("; ")[1]
         .replace("boundary=", "");
       console.log(boundary);
       const fileLen = req.headers["content-length"];
       let body = "";
       let curSize = 0;
 ​
       req.on("data", (data) => {
         curSize += data.length;
         res.write(`文件上传了${(curSize / fileLen) * 100}%\r\n`);
         body += data;
       });
 ​
       req.on("end", () => {
         const payload = qs.parse(body, "\r\n", ": ");
         //截取content-type
         const conType = payload["Content-Type"];
 ​
         console.log(conType);
         //获取到content-type的index
         const cIndex = body.indexOf(conType);
         //开始截取的位置
         const headIndex = cIndex + conType.length;
         //截取到的包含开头\r\n的字符串
         const boundryBody = body.substring(headIndex);
         //去除字符串
         const binary = boundryBody.replace(/^\s\s*/, "");
         //去除最后的boundry
         const finalBody = binary.substring(
           0,
           binary.indexOf("--" + boundary + "--")
         );
         console.log(finalBody);
         fs.writeFile("./baz.png", finalBody, "binary", (err) => {
           if (err) console.log(err);
           res.end("图片上传成功");
         });
       });
     }
   }
 });
 ​
 server.listen("8000", () => {
   console.log("服务器启动成功");
 });
 ​