问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
你好,欢迎来到懂视!登录注册
当前位置: 首页 - 正文

Node.js——express核心用法和源码解读

发布网友 发布时间:2024-09-15 02:10

我来回答

1个回答

热心网友 时间:2024-10-10 11:36

客户端发送请求的方式

客户端传递到服务器参数常见的5种方法

通过get请求中的URL的params

通过get请求中的URL的query

通过post请求中的body的json格式

通过post请求中的body的x-www-form-urlencoded格式

通过post请求中的form-data格式

传递参数params和queryparams

如果请求路径是http://localhost:3000/user/123132,那么我们就可以通过req.params的方式获取到用户传递过来的参数信息

constexpress=require('express')constapp=express()app.get('/user/:id',(req,res,next)=>{console.log(req.params);//{id:'123132'}res.end('Hello')})app.listen(3000,()=>{console.log('express框架初体验');})

用户在传递的时候可能也会传递多个参数,但这种情况比较少;请求路径为http://localhost:3000/user/haha/18

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})query

如果请求路径是http://localhost:3000/user?name=haha&age=18,那么我们就可以通过req.params的方式获取到用户传递过来的参数信息

constexpress=require('express')constapp=express()app.get('/user',(req,res,next)=>{console.log(req.query);//{name:'haha',age:'18'}res.end('Hello')})app.listen(3000,()=>{console.log('express框架初体验');})响应数据end方法

类似于http中的response.end方法,用法是一致的;但是其只能返回字符串或者是buffer数据

constexpress=require('express')constapp=express()app.post('/login',(req,res,next)=>{//下面这行代码会报错res.end({name:'chris'})//The"chunk"argumentmustbeoftypestringoraninstanceofBufferorUint8Array.ReceivedaninstanceofObject})app.listen(3000,()=>{console.log('express框架初体验');})

如果我们想返回一个json数据,则需要先用res.type方法设置响应头中的content-type为application/json,然后再利用JSON.stringify将我们要返回的对象转换为json格式的数据之后再使用res.end方法返回

constexpress=require('express')constapp=express()app.post('/login',(req,res,next)=>{//指定响应数据的数据类型,这样客户端才知道以什么形式的数据进行解析res.type('application/json')//将返回的对象先转为json格式的字符串res.end(JSON.stringify({name:'chris'}))})app.listen(3000,()=>{console.log('express框架初体验');})json方法

json方法中可以传入很多的类型:object、array、string、boolean、number、null等,它们最终都会被转换成json格式返回;json方法相当于帮我们做了两件事情,先设置好响应头中的content-type属性,再将数据转化为json格式响应回去

constexpress=require('express')constapp=express()app.post('/login',(req,res,next)=>{res.json({name:'chris'})})app.listen(3000,()=>{console.log('express框架初体验');})status方法

用于设置状态码

constexpress=require('express')constapp=express()app.post('/login',(req,res,next)=>{res.status(204)res.json([1,2])})app.listen(3000,()=>{console.log('express框架初体验');})Express的路由

如果我们将所有的代码逻辑都写在app中,那么app会变得越来越复杂

一方面完整的Web服务器包含非常多的处理逻辑

另一方面有些处理逻辑其实是一个整体,我们应该将它们放在一起:比如对用户user相关的处理

获取用户列表

获取某一个用户信息

创建一个新的用户

删除一个用户

更新一个用户

我们可以使用express.Router来创建一个路由处理程序

一个Router实例拥有完整的中间件和路由系统

因此,它也被称为迷你应用程序(mini-app)

//app.jsconstexpress=require('express')constuserRouter=require('./routers/user')constapp=express()//注册一个普通中间件来解析请求体中的json数据app.use(express.json())//将有关用户的请求全部引入到注册好的用户路由中去执行app.use('/user',userRouter)app.listen(3000,()=>{console.log('express框架初体验');})

一个路由实例就相当于是一个小的mini-app,其拥有完整的中间件,所以其也可以像app一样注册其它的中间件

//routers/user.jsconstexpress=require('express')//使用express.Router函数注册一个路由constrouter=express.Router()//一个路由实例拥有完整的中间件功能,所以其也可以像app一样注册其它的中间件router.get('/:id',(req,res,next)=>{res.json({nickname:'chris',age:18})})router.post('/',(req,res,next)=>{console.log(req.body);res.end('注册了一个新用户')})router.delete('/:id',(req,res,next)=>{res.end(`删除了用户${req.params.id}`)})//将注册号的路由导出module.exports=router静态资源服务器

在node里面,如果想把某一个文件夹作为静态的服务器让客户端可以进行访问的话是非常简单的

express内置了一个static方法,其返回值是一个函数,可以作为我们的中间件

如果我们通过http来做的话,需要自己读取里面的文件然后再返回回去

但是在express里面,我们只需要注册一个普通中间件就可以了,只指定文件夹作为我们的静态资源文件夹,这样当我们去请求资源的时候就会去该文件夹里面寻找

dist文件夹是之前自己做的后台管理系统项目的打包文件,我们可以通过express来部署项目到本机;express内置了一个static方法,其返回值是一个函数,可以作为我们的中间件,需要传递一个路径作为静态资源文件夹

constexpress=require('express')constapp=express()//express内置了一个static方法,其返回值是一个函数,可以作为我们的中间件app.use(express.static('./dist'))app.listen(3000,()=>{console.log('express框架初体验');})express的错误处理

next函数中如果传入了参数,执行的并不是下一个匹配上的中间件,而是专门用来匹配错误的中间件;这样我们所有的错误处理就可以集中在一个中间件中进行管理,代码更加整洁且利于维护

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})0

下面是自己用postman进行的测试

源码解读调用express()到底创建的是什么?

express()创建的是一个名为app的函数,在这个函数上面集成了很多的方法,最经典的就是可以用app来创建中间件

我们从express的index.js文件中发现其是从lib文件夹下面的express.js文件中导入的

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})1

来到express.js中发现其导出的其实是一个函数createApplication,也就是说constexpress=require('express')其实就是将createApplication函数的引用赋值给了变量express而已

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})2

我们来到createApplication函数后发现,在其里面定义了一个名为app的函数并导出。我们现在应该明白constapp=express()的时候函数的返回值是什么了吧?其实就是在createApplication函数里面声明的一个名为app的函数而已

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})3app.listen()是如何启动服务器的?

app.listen()其实本质上用的还是http模块搭建服务器的方式,相当于在外面封装了一层而已

仔细查看createApplication函数之后,没有发现有给app函数添加过listen属性,那我们为什么可以直接调用app.listen方法呢?

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})4

其实基础比较好的同学肯定已经想到了,一个对象上面没有对应的属性就会到其原型中寻找,我们能调用listen方法就说明该方法肯定是在app函数的原型上的,而mixin(app,proto,false)这行代码做的就是将proto对象上的属性混合到app上

我们去proto对象中寻找之后发现,其身上的确具有listen方法,所以我们调用app.listen时实际上调用的是下面的代码;不难看出app.listen实际上是使用了原生http模块的createServer方法来进行搭建服务器的,监听端口号的方法用的也是server.listen而已,相当于是做了一层封装。所以本质上express就是http模块的封装这句话一点也不假

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})5

传入http.createServer函数的this就是app;这个函数会在客户端发送请求的时候执行,所以在express框架中,用户发送请求的时候执行的函数其实是app

app.use(中间件)内部到底发生了什么?

其实app.use的本质是调用了router.use方法,在内部将我们传递进去的中间件函数都放入到了一个stack数组中,以这种方法帮我们注册了中间件

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})6

从Layer构造函数中可以知道,其在创建实例的时候将对应的中间件函数的引用赋值给了layer.handle属性

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})7

下面是lazyrouter函数,主要做的事情就是注册了一个路由并赋值给了app._router

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})8

下面是router.use函数,前面的操作跟app.use的操作很类似,所以现在大家应该可以理解之前在创建路由当中为什么可以用router.use来创建中间件了吧,并不是router.use模仿了app.use,而是app.use基于router.use

app.get('/user/:name/:age',(req,res,next)=>{console.log(req.params);//{name:'haha',age:'18'}res.end('Hello')})9用户发送了网络请求,中间件是如何被回调的?

刚刚我们探索了app.use帮助我们注册中间件的本质其实就是把中间件函数和layer绑定到了一起放到了stack数组中去。其实中间件是如何被执行的也很简单,express会在用户发送请求的时候遍历stack数组,找到第一个匹配的layer之后,就去执行绑定在它身上的中间件函数layer.handle,这样就实现了用户发送请求回调中间件的功能

首先我们要知道,当用户发送了网络请求之后,传入http.createServer的参数会被执行,我们从app.listen的源码中可以看到,我们其实是把app传入给了http.createServer函数,所以当用户发送请求的时候,http模块会帮助我们执行app函数

constexpress=require('express')constapp=express()app.get('/user',(req,res,next)=>{console.log(req.query);//{name:'haha',age:'18'}res.end('Hello')})app.listen(3000,()=>{console.log('express框架初体验');})0

那app函数里面到底有什么内容呢?不难发现,其本质上执行的是app.handle函数

constexpress=require('express')constapp=express()app.get('/user',(req,res,next)=>{console.log(req.query);//{name:'haha',age:'18'}res.end('Hello')})app.listen(3000,()=>{console.log('express框架初体验');})1

于是我们又找到了app.handle函数,发现其调用的又是router实例上的handle方法

constexpress=require('express')constapp=express()app.get('/user',(req,res,next)=>{console.log(req.query);//{name:'haha',age:'18'}res.end('Hello')})app.listen(3000,()=>{console.log('express框架初体验');})2

沿着代码的轨迹,我们来到了router文件夹的index.js文件中寻找router.handle方法;为了让代码看起来更加简洁,我删除掉我们这次不讨论的源码

constexpress=require('express')constapp=express()app.get('/user',(req,res,next)=>{console.log(req.query);//{name:'haha',age:'18'}res.end('Hello')})app.listen(3000,()=>{console.log('express框架初体验');})3

为了知道最终执行的layer.handle_request函数到底执行了哪些操作,我们来到了router文件夹下面的layer.js文件;仔细阅读后发现,最终调用的就是我们绑定在layer上面的中间件

constexpress=require('express')constapp=express()app.get('/user',(req,res,next)=>{console.log(req.query);//{name:'haha',age:'18'}res.end('Hello')})app.listen(3000,()=>{console.log('express框架初体验');})4

至此,用户发送网络请求是怎么执行中间件的这个过程我们就已经探索完了

调用next为什么会执行下一个中间件?

从上面的代码中,可以看到在执行fn函数的时候将req、res以及next作为参数传递了进去,传递进去的next其实就是router.handle中的next函数,所以我们在中间件函数中调用next,实际上调用的是router.handle中的next函数,这里涉及到了js的闭包——可以访问到其他函数作用域中的变量的函数

因为闭包特性,所以next在执行的时候是可以访问到作用域链中的idx变量的,这个变量记录了上一次执行的中间件函数在数组中的索引,所以当我们在中间件函数里面执行next函数的时候,会从idx开始寻找下一个匹配的中间件并执行

constexpress=require('express')constapp=express()app.get('/user',(req,res,next)=>{console.log(req.query);//{name:'haha',age:'18'}res.end('Hello')})app.listen(3000,()=>{console.log('express框架初体验');})5原文:https://juejin.cn/post/7103806298029817892
声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。
E-MAIL:11247931@qq.com
交通事故致人死亡,该如何处理? 爱普生LQ1600KIIIH 断色带 爱普生针式打印机LQ1600KⅢH近两月来总频繁中断打印,同时电脑显示是通讯... ACQUA DI PARMA 帕尔玛之水 蓝色地中海系列 桃金娘加州桂中性淡香水 E... 女士香水推荐-ACQUA DI PARMA 帕尔玛之水 优雅木兰女士浓香水 EDP 50... ACQUA DI PARMA帕尔玛之水克罗尼亚系列中性香水套装-适用对象 帕尔玛 | 必须拥有的小众沙龙香 ACQUA DI PARMA/帕尔玛之水克罗尼亚系列黑调男士古龙水-适用对象 ACQUA DI PARMA 帕尔玛之水 克罗尼亚系列 风度中性古龙水 EDC 20ml... ACQUA DI PARMA/帕尔玛之水-绅士男士古龙水EDC JS 实现网络测速 浏览器内置js对象 一直的英文单词是什么 坚持的英文单词怎么说? 多喝水肚子会不会慢慢变大 为什么喝水肚子就变大 汉代有宇这个字吗?家里有一个古董,据说汉代的,但是桶身上有一个宇字 我的名字叫鑫宇,想给自己取个字号,就像古人的字,哪位朋友能帮帮忙_百度... 迟宇古代字怎么写? 宇字在古代的文字 凯美瑞2019款20g和20e有何区别 为什么身边男生都不支持女生买凯美瑞? ...基本上都不能玩 能够验证游戏数据 但是加载就黑屏 一般网络游戏应该都有私服,那,私服是不是就是盗版呢? 如何禁止手机安装某些应用? 盗版游戏光盘故意装入了无用的垃圾数据吗? 容声冰箱哪款最值得买[容声冰箱型号价格推荐] 2024年买冰箱,容声到底哪个好?以及冰箱选购指南 内痔疮便血怎么治疗 寻求内痔疮(这几天出血)的药方,最好西药和药膏 地铁安检员招聘有什么条件? 福冈旅游攻略 日本福冈旅游攻略 哈尔滨直飞的国内城市有哪些,杭州到通化旅游攻略自驾 最新杭州(浙江省)人才居住证(绿卡) 奔驰C200CGI和一汽大众CC比哪个性价比高啊 镇江集体户口怎么办理老年人公交卡(65岁以上) 在镇江哪里可以办公交卡,有谁在哪里办过吗 为什么女生喜欢会拍照的男生 喜欢一个人出去玩 喜欢摄影的男生会有女生喜欢吗? 微信美颜功能怎么开? 怎么利用微信的视频美颜功能? 华为手机微信视频如何开美颜功能? 芋头苗怎么腌制才好吃 三峡旅游景点中包含了哪个瀑布? 长江三峡在哪里究竟如何 七上数学思维导图整理-全书知识总结框架图 有哪些上妆不卡粉的技巧值得分享? 怎样打造不卡粉的底妆? 怎样避免底妆卡粉?有哪些方法? 怎么化底妆不卡粉
  • 焦点

最新推荐

猜你喜欢

热门推荐