nodejs+koa2 实现模仿springMVC

  • A+
所属分类:Web前端
摘要

koa2-MVC架构                                                     ———后端技术做前端

koa2-MVC架构

                                                    ---------后端技术做前端

环境:nodejs

开发工具:Visual Studio Code(下文简称:VSC)

环境安装,工具安装及中文自行百度 ,环境调整好后开始进入正题。

 

1、在硬盘上新增一个文件夹,打开VSC,点击‘添加工作区文件夹’,如果没有欢迎‘使用页面’,点击--文件--新建窗口,效果如下图:

 

nodejs+koa2 实现模仿springMVC

 nodejs+koa2 实现模仿springMVC

 2、添加vsc调试。Shift+ctrl+p,输入框内输入:launch.json

 

 

 nodejs+koa2 实现模仿springMVC

 

选择刚刚的文件夹

 

 nodejs+koa2 实现模仿springMVC

 

 

 nodejs+koa2 实现模仿springMVC

 

 

3、目录结构

 

 

nodejs+koa2 实现模仿springMVC

 

 

从低到高one by one

3-1、package.json

 1 {  2     "name": "koa2mcv",  3     "version": "1.0.0",  4     "description": "Hello Koa 2 example with MVC",  5     "main": "app.js",  6     "scripts": {  7         "start": "node app.js"  8     },  9     "author": "baba", 10     "dependencies": { 11         "koa": "2.11.0", 12         "koa-router": "8.0.8", 13         "koa-bodyparser": "4.3.0", 14         "koa-static-plus": "0.1.1", 15         "koa-view": "2.1.3", 16         "koa-jwt": "4.0.0", 17         "koa-log4": "2.3.2", 18         "jsonwebtoken": "8.5.1", 19         "nunjucks": "3.2.1", 20         "mime": "2.4.5", 21         "mz": "2.7.0" 22     } 23 }

 

 

 参数介绍:name项目名称、version版本号、description项目描述、main项目启动文件、scripts启动快捷设置,author作者,dependencies第3方中间件名称及版本。

 3-2、app.js

1 //启动服务 2 require('./config/init').startServer();

 

启动相关配置,封装到config/init.js中,启动文件直接引用即可。

3-3、views存放html页面

3-4、static存放静态文件,css,js,font等

3-5、src存放业务控制,类似于springMVC中的controller、service。

3-6、config存放核心配置文件。

3-6-1、init.js项目核心。

异常友好处理
 1 function handler(){  2     return async (ctx, next) => {  3           4         const start = new Date().getTime();   5           6         var urlReq=ctx.request.url;  7         if(urlReq !== '/favicon.ico'){  8               9             console.log(`请求地址:${ctx.request.method} ${urlReq}`);  10             try { 11                  12                 let params =Object.assign({}, ctx.request.query, ctx.request.body); 13                  14                 if(config["token"].excludeUrl.indexOf(urlReq) == -1 && !tokenFunction.varifyToken(params.token)){ 15                     ctx.status =401; 16                 }else{ 17                     await next(); 18                 } 19             } catch (error) { 20                 ctx.status=401; 21                 console.log(`错误!无法获取token参数`); 22             }finally{ 23                      24                     let err={}; 25                     if(!ctx.status){ 26                         err.status = 500; 27                     }else if(ctx.status==200){ 28                         return; 29                     }else{ 30                         err.status = ctx.status; 31                     } 32                      33                     switch(err.status){ 34                         case 404:  35                             err.url = config["server-name"]+'/static/public/404.html'; 36                             err.message="资源不存在!"; 37                             break; 38                         case 401:  39                             err.url = config["server-name"]+'/static/public/10000.html'; 40                             err.message="登陆失效!请重新登陆!"; 41                             break; 42                         case 500:  43                             err.url = config["server-name"]+'/static/public/500.html'; 44                             err.message="系统内部错误!"; 45                             break; 46                     } 47                      48                     switch(ctx.request.type){ 49                         case 'application/json':  50                             ctx.type = 'application/json'; 51                             ctx.body = {errorCode:err.errorCode,message: err.message} 52                             break; 53                         default:  54                              55                             ctx.type = 'text/html'; 56                             ctx.redirect(err.url); 57                             break; 58                     } 59             } 60         } 61         const ms = new Date().getTime() - start; 62         console.log(`请求消耗时间: ${ms}ms`); 63     } 64 }

 

 

路由配置

 1 function controller(){  2       3     const router = new koaRouter({  4         prefix: config["server-name"]  5     });  6       7     function findJsonFile(rootpathStr){  8           9         fs.readdirSync(rootpathStr).forEach(function (item, index) { 10              11             let fPath = path.join(rootpathStr,item); 12              13             let stat = fs.statSync(fPath); 14              15             if(stat.isDirectory() === true) { 16                 findJsonFile(fPath); 17             } 18              19             if (stat.isFile() === true&&fPath.endsWith('.js')) {  20                  21                 var mapping =  require(fPath); 22                 for (var url in mapping) { 23                     if (url.startsWith('GET ')) { 24                         router.get(url.substring(4), mapping
);
25 } else if (url.startsWith('POST ')) { 26 router.post(url.substring(5), mapping
);
27 } else if (url.startsWith('PUT ')) { 28 router.put(url.substring(4), mapping
);
29 } else if (url.startsWith('DELETE ')) { 30 router.del(url.substring(7), mapping
);
31 } 32 console.log(`注册 URL: ${url}`); 33 } 34 } 35 }); 36 } 37 38 findJsonFile(rootpath + 'src'); 39 return router.routes(); 40 }

 

视图渲染

function templating() {     var         autoescape = config['templating-autoescape'] === null ? true : config['templating-autoescape'],         noCache = config['templating-noCache'] === null ? false : config['templating-noCache'],         watch = config['templating-watch'] === null ? false : config['templating-watch'],         throwOnUndefined = config['templating-throwOnUndefined'] === null ? false :config['templating-throwOnUndefined'],         env = new nunjucks.Environment(             new nunjucks.FileSystemLoader(rootpath+'views', {                 noCache: noCache,                 watch: watch,             }), {                 autoescape: autoescape,                 throwOnUndefined: throwOnUndefined             });     if (config['templating-filters'] != null) {         for (var f in config['templating-filters']) {             env.addFilter(f, config['templating-filters'][f]);         }     }     return async (ctx, next) => {         ctx.render = function (view, model) {             ctx.response.body = env.render(view, Object.assign({}, ctx.state || {}, model || {}));             ctx.response.type = 'text/html';         };         await next();     }; }

 

启动构建

function startServer(){      const app = new koa();      app.use(koaStaticPlus(rootpath+'static', {         pathPrefix: config["server-name"]+'/static'     })     );          app.use(koaBodyParser());         app.use(handler());         app.use(templating());          app.use(controller());        app.listen(config["server-port"]);  }

 

3-6-2、config.js项目参数配置。

module.exports ={     'server-name':'/koa',     'server-port':3000,     "templating-noCache":true,     "templating-watch":true,     "templating-autoescape":null,     "templating-throwOnUndefined":null,     "templating-filters":null,     "token":{       "excludeUrl":[         "/koa/login",         "/koa/dologin"       ],       "timeout":1000 * 60 * 60 * 24 * 7,       "secret":"jiaobaba"     }       }

 

3-6-3、token.js项目token相关方法封装。

const jwt = require("jsonwebtoken"); const config = require('./config'); /**  * 创建token的方法  */ let createToken = (data)=>{     let obj = {};     //存入token的数据     obj.data = data || {};     //token的创建时间     obj.ctime = (new Date()).getTime();     return jwt.sign(obj,config["token"].secret); } /**  * 验证token是否合法的方法  * @param {*} token   */ let varifyToken = (token)=>{     let result = null;     try{         let {data,ctime,expiresIn} = jwt.verify(token,config["token"].secret);         let nowTime = (new Date()).getTime();         if(nowTime-ctime<config["token"].timeout){             result = data;                 }     }catch(error){      }     return result; }  module.exports = {     createToken,     varifyToken };

 

3-6-4、logger.js项目日志配置文件。

4、项目结构构建结束,接着引入所有依赖包,在终端中执行‘npm install’ ,会下载package.json中dependencies所有包,以及这些包所依赖的包。

 nodejs+koa2 实现模仿springMVC

 

 

 执行后项目结构会增加两个文件

nodejs+koa2 实现模仿springMVC

 

 

5、编写测试用例,在src下新建hello.js。

//token const token = require('../config/token');  var fn_hello = async (ctx, next) => {     var name = ctx.params.name;     ctx.response.body = `<h1>Hello, ${name}!</h1>`; };   var fn_index = async (ctx, next) => {     ctx.response.body = `<h1>Index</h1>         <form action="/koa/signin" method="post">             <p>Name: <input name="name" value=""></p>             <p>Password: <input name="password" type="password"></p>             <p><input type="submit" value="Submit"></p>         </form>`; };  var fn_signin = async (ctx, next) => {     var         name = ctx.request.body.name || '',         password = ctx.request.body.password || '';     console.log(`登陆名: ${name}, 密码: ${password}`);         ctx.response.body = `<h1>Welcome, ${name}!</h1><br/>you token:<br/>${token.createToken({user: name,password: password})}`;      };    module.exports = {     'GET /hello/:name': fn_hello,     'GET /login': fn_index,     'POST /dologin': fn_signin };

 

 6、启动项目

nodejs+koa2 实现模仿springMVC

 

 启动成功

nodejs+koa2 实现模仿springMVC

 

 测试访问:http://127.0.0.1:3000/koa/login

nodejs+koa2 实现模仿springMVC

 

输入值获取token

 nodejs+koa2 实现模仿springMVC

 

 nodejs+koa2 实现模仿springMVC

 

 先不用带token进行访问

nodejs+koa2 实现模仿springMVC

 

拦截成功

带上token 进行访问

 nodejs+koa2 实现模仿springMVC

 

 测试成功!

end