教程基于npm install --save hapi@13.5.3 1,搭建http var Hapi = require('hapi'); var server=new Hapi.Server(); server.connection({port:4000}); server.route([{ method:'GET', path:'/', handler:function (request,reply) { reply({name:'234'}); } }]); server.start((err)=>{ console.log('正常'); }); 访问:http://127.0.0.1:4000/ 2,在shell窗口命令里面,实时监听http访问记录日志 var Hapi = require('hapi'); var server=new Hapi.Server(); server.connection({port:4000}); server.route([{ method:'GET', path:'/', handler:function (request,reply) { reply({name:'234'}); } }]); //需要安装 npm install --save good@6 good-console@5 server.register([{ register:require('good'), options:{ reporters:[{ reporter:require('good-console'), events:{response:'*'} }] } }],(err)=>{ server.start((err)=>{ console.log('正常'); }); }); 3,路由分离,(把一些路由独立出来) //route.js module.exports=[{ method:'GET', path:'/haha', handler:function (request,reply) { reply('你好哈哈'); //this.db 这样可以找到数据库 } }]; //index.js var Hapi = require('hapi'); var Sqlite3=require('sqlite3'); var db1=new Sqlite3.Database('./db') var server=new Hapi.Server(); server.connection({port:4000}); server.route([{ method:'GET', path:'/', handler:function (request,reply) { reply({name:'234'}); } }]); server.bind({db:db1}); //能保证引入的外来路由可以在handler里面的this.db找到数据库 //调用: server.route(require('./route')); 4,传参。将父页面的参数,传入给引入的子模块中 server.bind({db:'数据'}); server.route(require('./route')); //在route.js中的handler里面可以使用this.db来得到数据 //route.js module.exports=[{ method:'GET', path:'/', handler:function (request,reply) { this.db //打印出:数据 } }]; 5,ejs模板 npm install ejs --save server.register([{ register:require('good'), options:{ reporters:[{ reporter:require('good-console'), events:{response:'*'} }] } }, require('inert'), require('vision'), //加载模板,可以使用server.views()方法 ],(err)=>{ server.views({ //配置模板 engines:{ ejs:require('ejs') //npm install ejs --save }, relativeTo:__dirname, path:'./views', isCached:false //建议正式上线改为true }); server.route(require('./route')); server.start((err)=>{ console.log('正常'); }); }); 在views目录中存放index.ejs 内容: <%= title %> 使用: handler:function (request,reply) { reply.view('index',{ title:request.params.id }); } 6,后台接收get、post数据 接收get method:'GET', path:'/test/{id}', handler:function (request,reply) { reply.view('index',{ title:request.params.id }); } 接收post
method:'POST', path:'/post_test', config:{ payload:{ output:'data' } }, handler:function (request,reply) { console.log('post数据'+request.payload.user); } 7, cookies npm install --save hapi-auth-cookie@6 server.register([{ register:require('good'), options:{ reporters:[{ reporter:require('good-console'), events:{response:'*'} }] } }, require('inert'), require('vision'), require('hapi-auth-cookie'), //加载cookies ],(err)=>{ if(err){ throw err; } //设置cookies server.auth.strategy('session','cookie','try',{ password:'password-that-is-at-least-32-chars', //加密方式 ttl: 24 * 3600 * 1000, //1天后过期,如果不设置一旦关闭页面cookies值就消失 isSecure:false, //true时,只允许https使用 }); }); 设置: handler:function (request,reply) { request.cookieAuth.set({ name:encodeURIComponent(request.params.id) }); reply.view('index',{ title: request.params.id }); } 取值: request.auth.credentials.name 清除: request.cookieAuth.clear(); 判断是否存在cookies: request.auth.isAuthenticated //返回布尔值 8,页面跳转 reply.redirect('/'); 9,路由路径 path:'/images/{id}' http://127.0.0.1/images/sdf //可选参数 path:'/images/{id?}' http://127.0.0.1/images/sdf 或者 http://127.0.0.1/images/ //通配符 path:'/{id*}' 这个路径是全能型匹配,静态资源就是靠通配符配置的 10,服务器方法 类似 把多个路由里面的公共方法抽出来独立成一个公共的方法。 创建: server.method({ name:'mean123', method:function (values,next) { // var sum=values.reduce((a,b)=>{a+b}); return next(null,values); }, options:{} }); 调用:(要在路由里面的handler里面调用) handler:function (request,reply) { server.methods.mean123(JSON.parse(request.payload.user),(err,mean)=>{ console.log(JSON.stringify(mean)); return reply.view('index',{ title:{mean:JSON.stringify(mean)}['mean'] }); }); } 11,同步(先决条件) server.route({ method:'post', path:'/post_test', config:{ payload:{ output:'data' //靠request.payload.属性名 来获取。如果那么request.payload.user }, pre:[{ assign:'xx', //在handler里面靠request.pre.xx获取 method:function (request,reply) { //这里比handler要先执行 setTimeout(function () { reply('数据啊'); //要使用reply才能将数据返回给handler中的request.pre.xx },5000); // reply('跳过handler函数,直接将数据显示到浏览器').takeover(); } }] }, handler:function (request,reply) { //一定要等pre的method执行完毕后,才轮到handler执行 console.log('user:'+request.payload.user); //user是 console.log('xx:'+request.pre.xx); reply.view('index',{ title:request.pre.xx }); } }); 12,带有先决条件的服务器方法 (类似:多个方法同步排队执行) 声明多个服务器方法: server.method([{ name:'mean123', method:function (request,reply) { setTimeout(function () { reply('数据123'); },5000); }, options:{} }, { name:'mean456', method:function (request,reply) { console.log(request.pre.mean123); //可以获取到先执行完毕的服务器方法的数据 reply('数据456'); }, options:{} } ]); 调用: server.route({ method:'post', path:'/post_test', config:{ payload:{ output:'data' }, pre:['mean123','mean456'] //同步,要等mean123的服务器方法执行完毕后,才轮到mean456执行 }, handler:function (request,reply) { reply.view('index',{ title:request.pre.mean123+'--'+request.pre.mean456 }); } }); 异步: 例如: pre:[ [ //这两个服务器方法,属于异步执行,必须两个都完成,才能继续执行 'abc', 'efg' ], 'mean123', 'mean456' ] //这样的设计解决了,地狱回调的痛苦,而且数据可以共享 13,上传 var path=require('path'); var fs=require('fs'); html: 路由: //上传 server.route({ method:'post', path:'/upload_page', config:{ payload:{ output:'stream', parse:true, maxBytes:1048576 *4,//允许上传 4mb timeout:10000 //上传超时默认10秒 }, }, handler:function (request,reply) { var upload=request.payload.upload; var uploadname=path.basename(request.payload.upload.hapi.filename); var des=path.join(__dirname,'uploads',uploadname); upload.pipe(fs.createWriteStream(des)); reply.view('index',{ title:'上传完成' }); } }); 文件会上传存放在根目录的uploads目录下 14,设置黑明单,禁止指定ip不能访问 npm install --save boom@3 netmask@1.0.5 var Boom = require('boom'); var Netmask = require('netmask').Netmask; //黑名单 var blacklist=[ '127.0.0.0/8' ]; //-------------------- var blockips=function (request,reply) { var ip=request.info.remoteAddress; //获取客户端ip for(let i=0;i<%= statuscode %>
<%= error_name %>
server.route([ { method:'get', path:'/asf/{name}', handler:function (request,reply) { return reply('ok'); }, config:{ validate:{ params:{ name:Joi.string().min(4) // 路由至少要大于等于4位 } } } } ]); server.ext('onPreResponse',(request,reply)=>{ if(request.response.isBoom){ var error_name= request.response.output.payload.error; var statuscode= request.response.output.payload.statusCode; return reply.view('error',{ statuscode:statuscode, error_name:error_name, title:'这里是错误页面' }).code(statuscode); } reply.continue(); }); 19,验证 hapi的内部验证推荐使用的joi模块,一旦过不了validate验证就不会执行到handler函数,直接报错Boom错误。 npm install joi@8.4.2 --save var Joi=require('joi'); //推荐去了解更多joi用法 (1)get数据提交验证 (验证路径参数): method:'get', path:'/asf/{id}', handler:function (request,reply) { reply.view('index',{ title:'sfsfsdfds' }); }, config:{ validate:{ params:{ id:Joi.string().min(4) //必须是字符串而且要大于等于4位 } } } (2)post数据提交验证 method:'POST', path:'/post_test', config:{ payload:{ output:'data' }, validate:{ payload:{ user:Joi.number() // user 是 值必须是数字 } } }, (3)服务器传给客服端时,验证数据(比较适合做对外接口的数据验证,例如采集) method:'get', path:'/asf', handler:function (request,reply) { reply({title:123}); //输出到客户端的对象中,其中title必须时数字 }, config:{ response:{ schema:{ title:Joi.number() } } } (4)当以get或者post方式传给后台的数据验证失败时,可以触发指定错误回调 method:'get', path:'/asf/{id}', handler:function (request,reply) { reply.view('index',{ title:'sfsfsdfds' }); }, config:{ validate:{ params:{ id:Joi.string().min(4) //必须是字符串而且要大于等于4位 }, failAction:function (request,reply,source,error) { reply('sdf'); //request,reply就是hanlder里面的 //source 告诉我们是哪部分出错 //error 输出一些错误信息 } } } 20,判断客户端是pc端还是手机端,选择加载不同的模版,域名不变 npm install hapi-mobile-detect@1.3.5 --save const MobileDetect = require('hapi-mobile-detect'); handler:function (request,reply) { if(!!request.plugins.md.mobile()){ reply.view('./mobile/index_mobile',{ title:'手机' }); }else{ reply.view('index',{ title:'pc' }); } } ./views/index.ejs ./views/mobile/index_mobile.ejs 21,插件(中间件) 加载需要靠:server.register加载 server.register([ //服务器启动时,仅仅加载一次,而且还是同步执行 {register:require('./one_module'), options:{ xx:'参数值,传个插件' } } ]); //one_module.js exports.register=function (server,options,next) { console.log('工作了'+options.xx); //工作了参数值,传个插件 next(); }; exports.register.attributes={ pkg:require("../package.json") }; 如果插件依赖某个模块,可以使用: exports.register=function (server,options,next) { server.dependency(['vision','good'],function (server,next) { console.log('adsfafas'); next(); }); next(); }; exports.register.attributes={ pkg:require("../package.json") }; 22,服务器静态数据 var server=new Hapi.Server({ app:{ xx:'这里是自定义值' } }); 在路由中获取: handler:function (request,reply) { request.server.settings.app.xx }, 23,缓存 客户端缓存: server.route({ method:'GET', path:'/{param*}', handler:{ directory:{ path:'public' } }, config:{ cache:{ privacy:'private', expiresIn:86400*1000 //为静态资源缓存 } } }); 同样也可以在路由器中配置,缓存页面。