Node服务器与文件上传

一.写在前面


在这个系列前面的的文章中我们学习了一系列的Node相关的API,但是我相信大部分人学习Node的主要原因是想用它来为自己的前端页面提供数据的,那么这篇文章我们就会讲解到如何基于Node的原生http模块来构建一个简单的服务器,并且对常用的参数进行解析,为后续我们学习Express和Koa框架开发应用程序打下良好的基础,那么废话不多说,就让我们开始吧,如果这篇文章对你有帮助,请不要吝啬你的👍如果你对Node相关的文章感兴趣可以关注我的专栏Node服务端,我们一起学习,一起进步。

二.认识Web服务器


什么是Web服务器哪?当应用程序(客户端)需要一个资源的时候,可以向一台服务器,通过Http请求获取这个资源,提供这个资源的服务器就是一个Web服务器。

目前有很多开源的Web服务器:Nginx,Apache(静态),Apache Tomcat(静态,动态),Node.js。

三.Node的http模块


在Node中,提供web服务器的资源返回给浏览器,主要是通过http模块,我们先进行简单的一个使用

js 复制代码
const http = require('http')
const PORT = 3000

const server = http.createServer((req, res) => {
  res.end("Hello Nodejs")
})

server.listen(PORT, () => {
  console.log("🚀服务器启动成功,端口号为:" + PORT + "")
})

四.创建Web服务器


我们使用http其实已经创建了一个简单的Web服务器,接下来我们就一起来看下http模块创建Web服务器的过程,并且底层是做了什么事情,然后用另外一种方式创建一个http服务器,创建服务器我们使用的是createServer 来完成的,当我们使用http.createServer 会返回服务器对象,底层其实是new Server对象。

js 复制代码
function createServer(opts,requestListener){
  return new Server(opts,requestListener)
}

当然我们也可以自己来创建这些对象。

js 复制代码
const http = require('http')
const server = new http.createServer((req,res)=>{
  res.end("Hello Nodejs");
})
server.listen(3000,()=>{
  console.log("🚀服务器启动成功,端口号为:3000")
})

上面我们已经看到,创建Server时会传入一个回调函数,这个回调函数在被调用时会传入两个参数,分别是req:request 请求对象,包含请求相关的信息,res:response响应对象,包含我们要发送给客户端的信息。

Server通过listen方法来开启服务器,并且在某一个主机和端口号上监听网络请求,也就是当我们通过ip:port的方式发送我们监听的Web服务器上时,我们就可以对其进行相关的处理。

listen函数有三个参数:

  1. 端口port:可以不传,系统会默认分配端,后续我们会写入环境变量中。

  2. 主机host:通常可以传入localhost,ip地址127.0.0.1,或者ip地址0.0.0.0,默认是0.0.0.0;

  3. 回调函数:服务器启动成功时的回调函数。

五.request对象


在向服务器发送请求时,我们会携带很多信息,比如本次请求的URL,服务器需要根据不同的URL进行不同的处理,本次请求的请求方式,比如GET,POST请求传入的参数和处理方式是不同的,本次请求的header中也会携带一些信息,比如客户端信息,接受数据的格式,支持的编码等等,这些信息会帮助我们封装到一个request对象中,我们可以直接来处理这个request对象,我们可以通过如下这种方式取得。

js 复制代码
const http = require('http')
const server = new http.createServer((req, res) => {
  console.log(req.url)    // 接收URL
  console.log(req.method) // 接收请求方式
  console.log(req.headers) // 接收请求头
})
server.listen(3000, () => {
  console.log("服务器启动成功,端口号为:3000")
})

六.URL的处理和解析


客户端在发送请求的时候会请求不同的数据,那么会传入不同的请求地址,服务端需要根据传入的不同地址做不同的响应,比如我们对如下的url做解析。

js 复制代码
 // http://localhost:8000/login?name='zz'&age=18
// http://localhost:8000/products?pageSize=10&pageIndex=2
const http = require("http")
const url = require("url")
const qs = require("querystring")

const port = 3000
const server = http.createServer((req, res) => {
  const { pathname, query } = url.parse(req.url)
  const queryObj = qs.parse(query)
  res.end(`pathname: ${pathname} \n query: ${query} \n queryObj: ${JSON.stringify(queryObj)}`)
})

server.listen(port, () => {
  console.log(`server is running on port ${port}`)
})

然后我们通过nodemon进行启动,通过apifox来请求测试一下接口

然后我们可以发现客户端成功的接收到了返回的数据。

但是其实http请求不仅仅只有这种类型,常见的请求方式包含五种,我们会在后续Express框架中讲解,我们首先再看下POST请求方式的解析,你会发现上述的请求方式仅仅是GET请求,如果是POST请求我们就不能这样简单的直接从req中获取了,因为POST请求的参数是包含在请求体中的,但是请求体body根本不是request对象的属性,如果想要对POST请求进行解析我们需要使用Sream流方式来处理,处理的方式涉及到Stream的知识,可以看我的专栏Stream流相关的文章。在Node中其实request其实是一个可读流,我们就可以通过流的方式来获取body请求体中的数据,代码实现如下,感兴趣的小伙伴可以复制下来跑一跑。

js 复制代码
const http = require('http')
const url = require("url")
const qs = require("querystring")

const PORT = 3000

const server = http.createServer((req, res) => {
  // 设置编码方式
  req.setEncoding('utf-8')

  // request本质上是一个可读流
  let isLogin = false
  req.on('data', (data) => {
    const dataString = data
    const loginInfo = JSON.parse(dataString)
    if (loginInfo.username === 'zz' && loginInfo.password === '123456') {
      isLogin = true
    } else {
      isLogin = false
    }
  })

  req.on('end', () => {
    if (isLogin) {
      res.end('登录成功,欢迎回来~')
    } else {
      res.end('账号和密码错误,请检查登录信息~')

    }
  })
})

server.listen(PORT, () => {
  console.log("🚀服务器启动成功,端口号为:" + PORT + "")
})

我们使用apifox来请求测试下如上的代码

然后我们可以看到成功接收到了服务端返回的数据

七.request header详解


我们在上述内容中了解了request对象传递过来的内容,包括url,method,header这三个可以直接通过request对象获取到,请求体body我们需要通过流的方式来获取,url,method我们已经简单了解过了,并且我们也实现了请求体的获取,接下来我们我们就来详细讲解下header。

在request对象的header中包含很多有用的信息,客户会默认传递过来一些信息。

content-type是这次请求请求携带的数据的类型:

  1. application/x-www-form-urlencoded :表示数据被编码成以&分隔的键值对,同时以=分隔键和值。
  2. application/ json:表示一个json类型。
  3. text/plain:表示是文本类型。
  4. application/xml:表示xml类型。
  5. mutipart/from-data:表示是上传文件。

content-length:文件的大小长度

keep-alive:

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

accept:告知服务器,客户端可接受文件的格式类型;

user-agent:客户端相关的信息;

我们在开发登录的接口时候往往是要获取token来进行校验的,token其实也是放在header中的,我们可以通过如下的代码来实现下上述的内容

js 复制代码
const http = require('http')
const url = require("url")
const qs = require("querystring")
const PORT = 3000

const server = http.createServer((req, res) => {
  console.log(req.headers)
  console.log(req.headers['content-type'])
  console.log(req.headers['authorization']) // 获取token

  res.end("Hello Nodejs")
})

server.listen(PORT, () => {
  console.log("🚀服务器启动成功,端口号为:" + PORT + "")
})

八.response对象


我们在上述的内容学习了request对象,可以获取客户端传递过来的东西,它本质上是一个可读流的数据,而response对象是一个可写流对象,用来向客户端响应数据。

js 复制代码
const http = require('http')
const PORT = 3000

const server = http.createServer((req, res) => {
  res.write("hello Nodejs")
  res.write("hello Node全栈")
  res.end()
})

server.listen(PORT, () => {
  console.log("🚀服务器启动成功,端口号为:" + PORT + "")
})

💡注意:虽然response是一个可写流,但是它的内部做了一些限制,我们并不能直接使用close方法,我们仅仅只能使用end方法来结束响应。


如果我们希望给客户端响应的结果数据,可以通过两种方式

  1. Write方法:这种方式是直接写出数据,但是并没有关闭流。

  2. end方法:这种方式是写最后的数据,并且写后会关闭流。

九.响应状态码


http状态码是用来表示http响应状态的数字代码,http的状态码非常多,可以根据不同的情况,给客户端返回不同的状态码。

我们可以如下的方式来响应给客户端状态码,我们可以通过两种方式

js 复制代码
res.statusCode = 200
res.writeHead(200)

十.响应头部数据


如果我们在向客户端响应数据如果不响应头部信息可能会出现很多问题,比如我们没有在头部写入编码有些浏览器中可能会出现乱码问题,所以一般我们都会在响应头中设置编码来避免这个问题,返回头部信息主要通过两种方式

js 复制代码
  res.setHeader('Content-Type', 'application/json;charset=utf-8')
  res.writeHead(200,{
    "Content-Type":"application/json;charset=utf-8"
  })

Header设置 Content-Type有什么作用呢?默认客户端接收到的是字符串客户端会按照自己默认的方式进行处理;

比如我们想要给客户端响应json格式的数据,这个时候我们就要设置返回json的格式,如果不设置的话返回的就是按照默认类型,返回的字符串类型。

js 复制代码
const http = require('http')
const PORT = 3000

const server = http.createServer((req, res) => {
  res.statusCode = 200
  res.writeHead(200, {
    "Content-Type": "application/json;charset=utf-8"
  })
  const list = [
    {
      name: "张三",
      age: 18,
      sex: "男"
    },
    {
      name: "李四",
      age: 18,
      sex: "女"
    }
  ]

  res.end(JSON.stringify(list))

})

server.listen(PORT, () => {
  console.log("🚀服务器启动成功,端口号为:" + PORT + "")
})

十一.Node发送http请求


在浏览器中我们经常使用发送请求的库是axios它既可以在浏览器中使用,也可以在Node中使用,在浏览器中,axios使用的是封装的xhr,在node中,使用的是http内置模块,我们一起来使用下。

js 复制代码
npm init -y

首先我们初始化node的基本项目,然后我们安装下axios

js 复制代码
npm i axios

然后我们就可以发送请求了,首先我们使用nodemon将如下的服务启动。然后学习一下发送GET请求

js 复制代码
const http = require('http')
const PORT = 3000

const server = http.createServer((req, res) => {
  res.statusCode = 200
  res.writeHead(200, {
    "Content-Type": "application/json;charset=utf-8"
  })
  const list = [
    {
      name: "张三",
      age: 18,
      sex: "男"
    },
    {
      name: "李四",
      age: 18,
      sex: "女"
    }
  ]

  res.end(JSON.stringify(list))

})

server.listen(PORT, () => {
  console.log("🚀服务器启动成功,端口号为:" + PORT + "")
})

然后我们首先发送GET请求。

js 复制代码
const http = require("http")
// 使用http发送get请求
http.get("http://localhost:3000", (req, res) => {
  // 从可读流中获取数据
  req.on("data", (data) => {
    const dataString = data.toString()
    const dataInfo = JSON.parse(dataString)
    console.log(dataInfo)
  })
})

然后我们就可以获取服务端返回的数据。

然后我们学习下发送POST请求。

js 复制代码
const req = http.request({
  method: 'POST',
  hostname: 'localhost',
  port: 3000
}, (res) => {
  res.on('data', (data) => {
    const dataString = data.toString()
    const dataInfo = JSON.parse(dataString)
    console.log(dataInfo)
  })
})

req.end()

十二.文件上传错误做法


文件上传我们在实际的开发中经常是使用插件的,无论是在Java中还是在Node中,所以对于手动编写文件上传我们仅仅理解即可,但是基于原生手写上传能够让我们更好的理解服务端数据的上传,首先我们来学习下看似正确实则是错误的文件上传的方式。

十三.文件上传正确做法


十四.总结


这篇文章到这里就结束了🎉,这篇文章我们学习了使用Node原生的http的各种功能的使用,可能我们在日后的Node开发中并不会编写这些代码,可能框架已经帮我们编写好了,但是无论我们使用的的Express,Koa或者基于Nest编写企业级应用,其实底层都是做了这些操作,这些内容是理解后续内容的前提,并且整个Node服务端文章的内容也是环环相扣的,Node服务器相关的内容还是要好好掌握哦🤖~

相关推荐
Nan_Shu_6141 分钟前
学习: Threejs (2)
前端·javascript·学习
G_G#9 分钟前
纯前端js插件实现同一浏览器控制只允许打开一个标签,处理session变更问题
前端·javascript·浏览器标签页通信·只允许一个标签页
@大迁世界24 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路33 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug37 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213839 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中1 小时前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子2 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端