当前位置: 首页 > 后端技术 > Node.js

使用Node.js原生API编写Web服务器_0

时间:2023-04-03 20:23:06 Node.js

Node.js是一门在JavaScript基础上发展起来的语言,所以前端开发者应该是天生的。一般我们会把它作为CLI工具或者Web服务器使用,Web服务器有很多成熟的框架,比如Express、Koa。但是Express和Koa都封装了Node.js的原生API,所以实际上我们可以不借助任何框架只用原生API来写一个web服务器。这篇文章要讲的是如何在不使用框架的情况下,用原生API编写一个Web服务器。因为在我的计划中,后面会写Express和Koa的源码分析,都是使用原生API实现的。所以这篇文章其实就是这两个源码分析的前置知识,可以帮助我们更好的理解Express、Koa等框架的含义和源码。本文只是为了说明原生API的使用,代码比较丑陋,实际工作中请勿模仿!本文可运行代码示例已上传到GitHub,大家可以拿下来玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/Node.js/HttpServerHelloWorld搭建一个简单的Web服务器,使用原生的http模块就够了,简单的HelloWorld程序几行代码就够了:consthttp=require('http')constport=3000constserver=http.createServer((req,res)=>{res.statusCode=200res.setHeader('Content-Type','text/plain')res.end('HelloWorld')})server.listen(port,()=>{console.log(`Serverisrunningonhttp://127.0.0.1:${port}/`)})这个例子很简单,直接使用http.createServer创建一个服务器,这个服务器没有逻辑,只有在访问ReturntoHelloWorld的时候。服务器创建完成后,使用server.listen在3000端口运行。这个例子真的很简单,但是好像除了输出一个HelloWorld,什么也做不了。它与我们一般使用的web服务器相差甚远,主要有这些差异:不支持HTTP动词,如GET,POST不支持路由,没有静态资源托管,不能持久化数据。前三点是Web服务器必不可少的基本功能。现在还有各种各样的微服务和数据库打交道来做持久化,但是我们也应该知道如何做持久化。那么我们来写一个真正可用的web服务器,也就是把缺的地方补上。我们在路由和HTTP动词前面的HelloWorld并不是完全不能用,因为代码位置还是要在http.createServer里面,我们会在里面添加路由功能。为了区别于下面的静态资源,我们的API请求都是以/api开头的。做路由匹配并不难。最简单的方法就是直接用if条件判断。为了得到请求地址,我们需要使用url模块来解析传递过来的地址。Http动词可以直接通过req.method获取。所以http.createServer修改如下:consturl=require('url');constserver=http.createServer((req,res)=>{//获取url的所有部分//url.parse可以将req.url解析为一个包含路径名和查询字符串的对象//consturlObject=url.parse(req.url);const{pathname}=urlObject;//api以一个API请求开始if(pathname.startsWith('/api')){//然后判断路由if(pathname==='/api/users'){//获取HTTP动词constmethod=req.method;if(method==='GET'){//写一个假数据constresData=[{id:1,name:'Xiaoming',age:18},{id:2,name:'小红',age:19}];res.setHeader('Content-Type','application/json')res.end(JSON.stringify(resData));return;}}}});现在我们访问/api/users就可以得到用户列表了:支持静态文件上面写着API请求是基于/api的,也就是说可以认为是一个不以这个开头的静态文件.不同的文件有不同的内容类型。在我们的例子中,暂时只支持一种.jpg。实际上,只需在我们的if(pathname.startsWith('/api'))中添加一个else。返回静态文件需要:使用fs模块读取文件。返回文件时,根据不同的文件类型设置不同的Content-Type。所以我们的else看起来是这样的://...省略前后的代码...else{//使用路径模块获取文件扩展名constextName=path.extname(pathname);if(extName==='.jpg'){//使用fs模块读取文件fs.readFile(pathname,(err,data)=>{res.setHeader('Content-Type','image/jpeg');res.write(data);res.end();})}}那我们在同级目录放一张图试试看:数据持久化数据持久化有几种方式,一般是存储在数据库中,在少数情况下也存储在文件中。保存数据库很麻烦,需要创建并连接到数据库。这里没有很好的demo,我们来演示一个保存文件的例子。通常,POST请求用于存储新数据。我们在上一个的基础上增加一个POST/api/users来添加一条数据。我们只需要在前面的if(method==='GET')后面加上一个POST判断就可以了://...省略其他代码...elseif(method==='POST'){//注意数据传输中可能有多个chunk//我们需要将这些chunk拼接起来letpostData='';req.on('data',chunk=>{postData=postData+chunk;})req.on('end',()=>{//数据传输后向db.txt插入内容fs.appendFile(path.join(__dirname,'db.txt'),postData,()=>{res.end(postData);//数据写入后,再次返回数据});})}然后我们测试这个API:看文件里有没有写:综上所述,我们就完成了一个基本功能的web服务器。代码并不复杂,但是对我们理解NodeWeb服务器的原理很有帮助。但是上面的代码还有一个很大的问题:代码丑!所有代码都写成一堆,HTTP动词和路由匹配都是用if条件判断。几百个API,十多个动词,代码简直就是一场灾难!因此,我们应该将路由处理、HTTP动词、静态文件、数据持久化等所有功能抽离出来,让整个应用更加优雅和可扩展。这就是Express、Koa等框架存在的意义。下一篇我们会去Express的源码看看他是怎么解决这个问题的。注意不要迷路~本文可运行代码示例已上传到GitHub,大家可以拿下来玩转:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/Node.js/HttpServer文末,感谢您抽出宝贵时间阅读本文,如果本文对您有一点帮助或启发,请不要吝啬您的点赞和GitHubstar,您的支持是作者继续创作的动力。作者博文GitHub项目地址:https://github.com/dennis-jiang/Front-End-Knowledges