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

一个你可能会用到的路由适配器

时间:2023-04-04 00:40:24 Node.js

前言这种状态有点像在学校写论文,文章开头总是“扯”出来,憋着难受。将原地址和源码地址从后台分离出来后,前端的童鞋们会需要处理一些节点层的工作,比如模板渲染,接口转发,以及一些业务逻辑。比较常用的框架有koa、koa-router等。现在我们要实现这样一个需求:当用户访问/fe时,页面显示hello。当fe用户访问/backend时,页面显示hellobackend。你认为我不需要koa、koa-router和nativenode来满足这个要求吗?模块可以解决问题。consthttp=require('http')consturl=require('url')constPORT=3000http.createServer((req,res)=>{let{pathname}=url.parse(req.url)letstr='你好'if(pathname==='/fe'){str+='fe'}elseif(pathname==='/backend'){str+='backend'}res.end(str)})。listen(PORT,()=>{console.log(`appstartat:${PORT}`)})确实,对于非常简单的需求,使用框架似乎有点浪费,但是对于上面的实现,有也有缺点存在,比如需要我们自己解析路径。路径分析和逻辑编写耦合在一起。如果以后要实现的需求越来越多,那么gg。所以让我们尝试用koa和koa-router实现app.js=3000router.get('/fe',(ctx)=>{ctx.body='hellofe'})router.get('/backend',(ctx)=>{ctx.body='hellobackend'})app.use(router.routes())app.use(router.allowedMethods())app.listen(PORT,()=>{console.log(`appstartat:${PORT}`)})通过以上处理,路径解析由koa-router处理,但是整体的写法还是有一些问题。匿名函数写法中的路由配置和逻辑处理没有办法不分离的复用在一个文件中。项目做大了也很麻烦。接下来我们再优化一下,先看看整体的目录结构├──app.js//应用入口├──controller//逻辑处理,子模块│├──hello.js│├──aaaaa.js├──middleware//中间件统一注册│├──index.js├──routes//路由配置,可由模块配置│├──index.js├──views//模板配置,按页面处理或module,本例未使用│├──index.html预览各个文件app.js的逻辑应用交集constKoa=require('koa')constmiddleware=require('./middleware')constapp=newkoa()constPORT=3000middleware(app)app.listen(PORT,()=>{console.log(`appstartat:${PORT}`)})routes/index.js路由配置中心constKoaRouter=require('koa-router')constrouter=newKoaRouter()constkoaCompose=require('koa-compose')consthello=require('../controller/hello')module.exports=()=>{路由器。get('/fe',hello.fe)router.get('/backend',hello.backend)returnkoaCompose([router.routes(),router.allowedMethods()])}controller/hello.jshello模块逻辑module.exports={fe(ctx){ctx.body='hellofe'},backend(ctx){ctx.body='hellobackend'}}middleware/index.js中间件统一注册constroutes=require('../routes')module.exports=(app)=>{app.use(routes())}写到这里你可能会有疑问?一个简单的需求,做成这样,就显得太复杂了。有必要吗?答案是:必须的,这样的目录结构可能不是最合理的,但是路由、控制器、视图层等各司其职,各司其职,对以后的扩展有很大的帮助。不知道大家有没有注意到路由的配置。routes/index.js路由配置中心constKoaRouter=require('koa-router')constrouter=newKoaRouter()constkoaCompose=require('koa-compose')consthello=require('../controller/hello')module.exports=()=>{router.get('/fe',hello.fe)router.get('/backend',hello.backend)returnkoaCompose([router.routes(),router.allowedMethods()])}每条路由对应一个controller去处理,很分离,很通用!!!这好像是我们前端写vue-router或者react-router的常用配置方式。但是当模块多了,这个文件夹就会变成constKoaRouter=require('koa-router')constrouter=newKoaRouter()constkoaCompose=require('koa-compose')//接下来需要require各个模块的文件进来consthello=require('../controller/hello')consta=require('../controller/a')constc=require('../controller/c')module.exports=()=>{router.get('/fe',hello.fe)router.get('/backend',hello.backend)//配置各个模块和控制器的路由router.get('/a/a',a.a)router.post('/a/b',a.b)router.get('/a/c',a.c)router.get('/a/d',a.d)router.get('/c/a',c.c)router.post('/c/b',c.b)router.get('/c/c',c.c)router.get('/c/d',c.d)//...等等returnkoaCompose([router.routes(),router.allowedMethods()])}有什么方法可以不用我们手动一个一个引入controller,然后手动调用koa-router的getpost和其他方法注册怎么样?比如我们只需要进行如下配置就可以完成上面手动配置的功能。routes/a.jsmodule.exports=[{path:'/a/a',controller:'a.a'},{path:'/a/b',methods:'post',controller:'a.b'},{路径:'/a/c',控制器:'a.c'},{路径:'/a/d',控制器:'a.d'}]routes/c.jsmodule.exports=[{路径:'/c/a',controller:'c.a'},{path:'/c/b',methods:'post',controller:'c.b'},{path:'/c/c',controller:'c.c'},{path:'/c/d',controller:'c.d'}]然后使用pure-koa-router模块进行简单配置就可以了constpureKoaRouter=require('pure-koa-router')constroutes=path.join(__dirname,'../routes')//指定路由constcontrollerDir=path.join(__dirname,'../controller')//指定控制器的根目录app.use(pureKoaRouter({routes,controllerDir}))这样一来,我们整个过程的重心就在路由配置上了,再也不用手动require一堆文件了。简单介绍一下上面的配置{path:'/c/b',methods:'post',controller:'c.b'}path:路径配置,可以是字符串/c/b或者数组['/c/b'],当然也可以是正则表达式/\c\b/methods:指定请求类型,可以是字符串get或者数组['get','post'],默认是get方法,controller:路由匹配的逻辑处理方法,c.b表示从controllerDir目录下的c文件导出的b方法,a.b.c表示从/a/b路径下的b文件导出的c方法源码controllerDir目录接下来我们一步步分析实现逻辑可以点击查看源码整体结构module.exports=({routes=[],controllerDir='',routerOptions={}})=>{//xxxreturnkoaCompose([router.routes(),router.allowedMethods()])})pure-koa-router接收路由,可以指定路由的文件目录,所以pure-koa-router会读取所有的目录下的文件(constroutes=path.join(__dirname,'../routes'))可以指定具体的文件,这样pure-koa-router读取指定的文件内容作为路由配置constroutes=path.join(__dirname,'../routes/tasks.js')可以直接指定文件export的内容(constroutes=require('../routes/index'))controllerDir,控制器routerOptions参数传递的根目录在newKoaRouter的时候,可以看到koa-router包会返回koaCompose执行后打包的中间文件,供koa实例添加。参数适配assert(Array.isArray(routes)||typeofroutes==='string','routesmustbeanArrayoraString')assert(fs.existsSync(controllerDir),'controllerDirmustbeafiledirectory')if(typeofroutes==='string'){routes=routes.replace('.js','')if(fs.existsSync(`${routes}.js`)||fs.existsSync(routes)){//处理传入的文件if(fs.existsSync(`${routes}.js`)){routes=require(routes)//处理传入的目录}elseif(fs.existsSync(routes)){//读取目录中的每个文件并合并routes=fs.readdirSync(routes).reduce((result,fileName)=>{returnresult.concat(require(nodePath.join(routes,fileName)))},[])}}else{//如果路由是字符串,则必须是文件或目录的路径thrownewError('routesisnotafileordirectory')}}路由注册不管传入的路由是否是文件都是还是一个目录,或者直接导出配置内容的最终结构是这样的。路由内容预览[//最基本的配置{path:'/test/a',methods:'post',controller:'test.index.a'},//多个路由到一个控制器{path:['/test/b','/test/c'],控制器:'test.iindex.a'},//多路由到多控制器{path:['/test/d','/test/e'],controller:['test.index.a','test.index.b']},//到控制器的单一路由{path:'/test/f',controller:['test.index.a','test.index.b']},//常规{path:/\/test\/\d/,controller:'test.index.c'}]主动注册letrouter=newKoaRouter(routerOptions)letmiddlewareroutes.forEach((routeConfig={})=>{let{path,methods=['get'],controller}=routeConfig//路由方法类型参数适配methods=(Array.isArray(methods)&&methods)||[methods]//控制器参数适配controller=(Array.isArray(controller)&&controller)||[controller]middleware=controller.map((controller)=>{//'test.index.c'=>['test','index','c']letcontrollerPath=controller.split('.')//方法名cletcontrollerMethod=controllerPath.pop()try{//c方法读取/test/index文件controllerMethod=require(nodePath.join(controllerDir,controllerPath.join('/')))[controllerMethod]}catch(error){throwerror}//判断读取的controllerMethod的参数必须是方法assert(typeofcontrollerMethod==='function','koamiddlewaremustbeafunction')returncontrollerMethod})//最后使用router.register注册router.register(path,methods,middleware)源码的实现过程基本就到这里了。最后pure-koa-router将路由配置和controller分离,让我们可以把精力放在路由配置和controller的实现上。希望能帮到你一点点。原文地址源代码地址