likes
comments
collection
share

Nodejs 第二十五章 http

作者站长头像
站长
· 阅读数 31

Nodejs 第二十五章 http

在 Node.js 中,http 模块是一个内置的模块,用于构建HTTP服务器和客户端应用程序。它提供了一系列的功能,使得开发者可以轻松地处理HTTP请求和响应,而无需额外的库。

核心功能

  1. 创建 HTTP 服务器
    • http 模块可以快速创建一个 HTTP 服务器,监听来自客户端的 HTTP 请求,并返回响应。
  2. 发送 HTTP 请求
    • 使用 http 模块,可以编写 Node.js 客户端应用程序,用于向 HTTP 服务器发送请求并接收响应。
  3. 处理请求和响应
    • 服务器可以解析请求的 URL、头部(headers)和正文(body),以及发送包含头部和正文的响应。
  4. 流和性能
    • http 模块支持流,这意味我们可以在数据到达时逐块处理请求和响应的正文,优化内存使用和增强性能。

数据请求

  • 我们从前面知道,服务器可以监听来自客户端的HTTP请求。
    • 那HTTP请求,在前端中,常用的就ajax、axios、fetch等等这些方式
    • 或者通过路由(Router)去处理不同的业务逻辑,在切换页面或者切换组件时返回不同的数据。像图下的话,在不同的路由界面,用到数据的不同,那接口也会不同,比如/api/xxx
    • HTTP在处理GET和POST的传参方式也不同

Nodejs 第二十五章 http

Nodejs 第二十五章 http

创建http服务器

  • createServer(): 这个方法接受一个请求监听器作为参数。请求监听器是一个函数,它有两个参数 requestresponserequest 对象表示客户端的 HTTP 请求,response 对象用于向客户端发送响应
  • .listen(3000): 这是服务器对象的一个方法,用于启动服务器并使其监听指定的端口。在这个例子中,服务器将监听 3000 端口。.listen 方法还可以接受第二个参数,一个回调函数,当服务器开始监听时,这个函数会被调用
const http = require("node:http")

http.createServer((req,res) => {
  //内容主体
}).listen(3000, () => {
  console.log("3000端口已经进入监听");
})

插件辅助

  • REST Client 插件通常用于集成开发环境(IDE)中,它允许开发者在开发过程中方便地发送和测试 RESTful API 请求

    1. 快速测试API:开发者可以通过插件快速构建和发送HTTP请求,测试后端服务的响应。
    2. 编写和发送请求:插件提供了一个用户友好的界面,允许用户输入请求的URL、HTTP方法(如GET、POST、PUT、DELETE等)、请求头和请求体。
    3. 查看响应:插件可以显示服务器的响应状态码、响应头和响应体,帮助开发者理解API的行为。
    4. 环境变量管理:允许用户定义环境变量,如基础URL、认证令牌等,以便于在不同的环境(如开发、测试、生产)中快速切换。
    5. 历史记录:插件通常能够保存请求历史,方便开发者回顾之前发送过的请求。
    6. 脚本和自动化:一些REST Client插件支持编写脚本来自动化测试流程,提高测试效率。
    7. 数据模拟:在某些情况下,插件还可以用于模拟服务器响应,帮助前端开发者在后端API尚未完成时进行开发。
    8. 代码生成:一些插件能够根据API的响应自动生成客户端代码,简化客户端开发过程。
  • 好处很多,但对我们目前来说,就相当于不需要打开postman或者apifox了,这会让我们需要打开或者下载(如果你没有的话)的软件少一点

Nodejs 第二十五章 http

如何分清 GET 和 POST

  • 在网络请求中,往往有很多种请求的方式,比如说:
    1. PUT: 用于上传指定资源,如果资源不存在则创建,如果资源已存在则更新。
    2. DELETE: 用于删除指定的资源。
    3. HEAD: 类似于 GET 请求,但是不返回响应体,只返回响应头。
    4. OPTIONS: 请求服务器告知客户端服务器支持哪些HTTP请求方法。
    5. PATCH: 用于对资源进行部分修改,不同于 PUT 请求,PATCH 请求只修改部分数据而不是整个资源。
    6. CONNECT: HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
    7. TRACE: 请求服务器回送收到的请求信息,主要用于测试或诊断。
    8. PURGE: 用于请求网络服务器删除指定的缓存内容。
    9. LINK: 用于建立与资源的关联。
    10. UNLINK: 用于取消与资源的关联。
  • 但最常用的还是GETPOSTPUTDELETE这四个,分别对应了CRUD(创建、读取、更新、删除)操作
    • 而在这里,我们就先讲前两者
const http = require("node:http")

http.createServer((req,res) => {
  //内容主体
  if(req.method == 'POST'){
    res.end('这是POST请求')
  }else if(req.method == 'GET'){
    res.end('这是一个GET请求')
  }
}).listen(3000, () => {
  console.log("3000端口已经进入监听");
})
  • 首先我们可以通过req参数,也就是requst请求中的method方法,获取当用户传递过来的请求方式。目前我们只是设置了最基础的POST和GET判断,还有更多的边界情况没有去处理

  • 接着就需要用到我们的插件辅助了,模拟用户发送数据请求来测试我们刚写好的http服务器

    • 我们需要先创建一个文件,文件名无所谓,但后缀要是http,在该文件中,进行发送模拟请求测试
    • 结果也是正常返回了
//index.http文件
POST http://localhost:3000/post HTTP/1.1

Content-Type: application/json

{
  "name":"小余"
}

Nodejs 第二十五章 http

  • 接下来是进行GET请求的发送测试,代码也是类似的
    • GET和POST最大的不同就在于他的信息数据是直接拼接在URL后面的,所有人都能直接看到这个信息,所以最好别在这里放敏感数据
GET http://localhost:3000?a=1&b=2 HTTP/1.1

Nodejs 第二十五章 http

url模块

  • url 模块提供了用于解析和格式化 URL 的工具。url.parse() 方法是这个模块中非常有用的一个函数,它能够将一个 URL 字符串分解为不同的组件
    • 这里讲到的内容,我们等下在区分路由的时候就会用到了,所以需要提前了解一下

返回值

url.parse() 方法返回一个对象,其中包含了 URL 的各个组成部分。这个对象的属性如下:

  • href: 完整的 URL。
  • protocol: URL 的协议部分,例如 https:
  • slashes: 布尔值,表示在协议和主机名之间是否有斜杠。
  • auth: URL 的认证信息部分,例如 user:pass
  • hostname: URL 的主机名部分,不包括端口号。
  • port: URL 的端口号部分。
  • host: hostnameport 的组合,如果端口号存在的话。
  • pathname: URL 的路径部分,从 / 开始,比如http://localhost:3000/login 就是从**/login**开始。
  • search: 查询字符串部分,即 ? 后面的部分。
  • query: 查询字符串被解析成的对象。如果没有查询字符串,它就是一个空对象,GET请求会用到
  • hash: URL 的片段标识符部分,即 # 后面的部分。

查询字符串解析

url.parse() 方法还可以接受一个可选的 true 参数作为第二个参数,这将使得查询字符串被解析为一个对象,而不是一个简单的字符串。

const parsedUrlWithQuery = url.parse(myUrl, true);
console.log(parsedUrlWithQuery.query); // 输出: { name: 'ferret' }

如果没有提供 true 参数,query 属性将是一个空对象。

格式化 URL

除了 parse 方法,url 模块还提供了 format 方法,它将一个解析后的 URL 对象转换回 URL 字符串。

const formattedUrl = url.format(parsedUrl);
console.log(formattedUrl); // 输出: 'https://user:pass@example.com:8888/path/to/page?name=ferret#hash'

区分路由

POST区分

  • 在要是路由不同,我们要怎么样做到在我们需要的路由界面正确的返回数据呢?
    • 我们在判断POST和GPT请求的时候,通过用户传递过来的请求,获取其中的method方法进行判断
    • 而如果是路由的话,也一定会从用户那里传递过来,我们最重要的就是从用户传递过来的数据中筛选除路由,然后进行判断处理。而这就需要用到我们node的url内置模块
    • 通过url内置模块,我们把req.url,也就是用户传递过来的地址传递进去。然后我们从其中解构出pathname变量,这个也就是我们的路由。最后在POST的判断里继续嵌套一层对路由的判断就OK了
    • 如果你疑惑为什么是在POST中加,而不是GET中加。那是因为我们测试的是login登录接口,账号密码是隐私的数据,当然不能让所有人都看见了
const http = require("node:http")
const url = require("node:url")

http.createServer((req, res) => {
 const { pathname } = url.parse(req.url); // 解析请求的 URL,获取路径
  //内容主体
  if (req.method == 'POST') {
    //请注意看这里,我们这次的区分路由逻辑就在这里
    if(pathname == '/login'){
      res.end('这是一个login路由,登录成功了')
    }else{
      //在处理完所有情况后,我们还会进行边界处理,万一出现我们意料之外的结果就返回404网络状态码,告诉用户,这个内容找不到
      res.statusCode = 404
      res.end('404')
    }
  } else if (req.method == 'GET') {
    res.end('这是一个GET请求')
  }
}).listen(3000, () => {
  console.log("3000端口已经进入监听");
})
  • 做好了基础工作之后,我们其实还能接收一下POST发送过来的参数。比如上面我们在写一个login登录功能,那首先我们肯定要先拿到用户传递过来的数据,才能和我们自己数据库内的数据进行一个判断
    • 那怎么拿到数据呢?
    • 通过req.on进行获取,就跟事件监听有点像,我们监听data参数,然后通过回调函数返回的chunk片段参数进行拼接成最后的完整数据
if (pathname == 'login') {
  let data = '';
  req.on('data', (chunk) => {
    data += chunk
  })
}
  • 但其实这样还没有结束,还记得我们一开始的时候,也是有通过res.end("xxx")把内容进行返回的,比如说登录的话,你至少要返回说是不是登录成功了,不成功的话会是什么问题?成功了也要有所提示
    • 在这里我们不直接使用res.end方法了,而是通过on进行监听end方法,这样就能处理更多的事情,比如返回的状态码和向应头。
    • 由于是测试,所以我们直接返回数据看看,正常情况下返回提示或者错误即可
req.on('end', () => {
  //设置响应头
  res.setHeader('Content-Type','application/json')
  //设置响应状态码,2xx表示请求成功
  res.statusCode = 200
  //返回给用户的数据
  res.end(data)
})
  • 写好了判断逻辑,当然要来测试一下啦
    • 测试的数据还是使用我们一开始测试POST的数据
    • 也是成果将响应状态码和数据返回给了用户

Nodejs 第二十五章 http

GET区分

  • 那在前面,我们已经了解到了GET最大的区别在于他的信息是放在URL里的,所以我们想要拿到信息,就需要拿到URL中?后面的内容
    • 那我们在了解内置模块url的时候,也知道了这里面其实是有拿到?后面内容的方式的,也就是query,但距离想要使用,还需要进一步的处理
//那首先,进行判断路由的方式,我们也是先直接照搬了过来,这是一样的
... else if (req.method == 'GET') {
  if(pathname === '/get'){
    console.log(query);
  }else{
    res.statusCode = 404
    res.end('404')
  }
}
  • 接下来我们就先从url模块中取出query打印一下,同时在url的parse方法中继续加上true,这能使query参数进行序列化
const { pathname, query } = url.parse(req.url, true); // 解析请求的 URL,获取路径和查询参数

...else if (req.method == 'GET') {
    if (pathname === '/get') {
      console.log(query);
    } else {
      res.statusCode = 404
      res.end('404')
    }
  }

Nodejs 第二十五章 http

  • 而没经过序列号的数据是这样的:a=1&b=2。很显然,还是对象形式的键值对更方便拿取数值。直接query.a的去取值

完整代码

  • 完整的代码,我们能看到if嵌套的层级是有点多的,阅读起来比较不方便。因为这是原生的写法,在正式的开发中,我们往往会上框架,就会简略很多,阅读观感也会更好
const http = require("node:http")
const url = require("node:url")

http.createServer((req, res) => {
  const { pathname, query } = url.parse(req.url); // 解析请求的 URL,获取路径和查询参数
  //内容主体
  if (req.method === 'POST') {
    if (pathname === '/login') {
      let data = '';
      req.on('data', (chunk) => {
        data += chunk
      })
      req.on('end', () => {
        res.setHeader('Content-Type', 'application/json')
        res.statusCode = 200
        res.end(data)
      })
    } else {
      res.statusCode = 404
      res.end('404')
    }
  } else if (req.method == 'GET') {
    if (pathname === '/get') {
      console.log(query);
    } else {
      res.statusCode = 404
      res.end('404')
    }
  }
}).listen(3000, () => {
  console.log("3000端口已经进入监听");
})
  • 那对原生的代码呢,我们也进行一个映射优化,让其更加精简适合阅读
const http = require("node:http");
const url = require("node:url");

// 创建路由处理映射
const routes = {
  'GET': {
    '/get': (req, res) => {
      const { query } = url.parse(req.url, true); // 解析查询字符串为对象
      console.log(query);
      res.statusCode = 200;
      res.end('Data received');
    }
  },
  'POST': {
    '/login': (req, res) => {
      let data = '';
      req.on('data', (chunk) => {
        data += chunk; // 读取请求体数据
      });
      req.on('end', () => {
        res.setHeader('Content-Type', 'application/json');
        res.statusCode = 200;
        res.end(data); // 返回接收到的数据作为响应体
      });
    }
  }
};

// 通用的404处理
function handleNotFound(req, res) {
  res.statusCode = 404;
  res.end("404 Not Found");
}

// 创建服务器
http.createServer((req, res) => {
  const { pathname } = url.parse(req.url);
  const methodRoutes = routes[req.method];
  
  // 检查请求方法和路径是否有对应的处理函数
  if (methodRoutes && methodRoutes[pathname]) {
    methodRoutes[pathname](req, res);
  } else {
    handleNotFound(req, res); // 无匹配路由,返回404
  }
}).listen(3000, () => {
  console.log("Server is running on port 3000");
});
转载自:https://juejin.cn/post/7360928627633094682
评论
请登录