高端网站建设费用,东莞高端建站公司,站长广告联盟平台,专业建设 教学成果奖网站文章目录前言一、什么是Node.js?二、fs文件系统模块三、Http模块四、模块化五、开发属于自己的包模块加载机制六、Express1.初识ExpressGET/POSTnodemon2.路由模块化3.中间件中间件分类自定义中间件4. 跨域问题七、Mysql模块安装与配置基本使用Web开发模式Session认证JWT八、m…
文章目录前言一、什么是Node.js?二、fs文件系统模块三、Http模块四、模块化五、开发属于自己的包模块加载机制六、Express1.初识ExpressGET/POSTnodemon2.路由模块化3.中间件中间件分类自定义中间件4. 跨域问题七、Mysql模块安装与配置基本使用Web开发模式Session认证JWT八、multer模块九、大事件项目十、总结前言
从某程序员速学NodeJS的一些笔记 一、什么是Node.js?
基于Chrome V8引擎的Javascript的运行环境V8相对于火狐的OdinMonkeySafri浏览器的JSCore都要快。Node.js是Javascript的后端运行环境无法调用DOM和BOM等浏览器内置API仅仅提供了基础的功能和API这些基础使很多强大的框架出现可以基于Express框架构建Web应用基于restify快速构建API项目可以读写和操作数据库等。 二、fs文件系统模块
fs模块是Node.js官方提供的用来操作文件的模块提供一系列的方法和属性用来满足用户对文件的操作有如下的一些常用命令。
命令说明open(pathmodecallback)异步模式下打开文件stat(pathcallback)异步模式下获取文件信息writeFile(filedatacallback)异步模式下写入文件read(fdbufferoffsetlengthpositioncallback)通过文件描述符读取文件close(fdcallback)通过文件描述符读取文件ftruncate(fdlencallback)通过文件描述符读取文件fs.unlink(path, callback)删除文件fs.mkdir(path[, options], callback)创建目录fs.readdir(path, callback)读取目录fs.rmdir(path, callback)删除目录fs.exists(path, callback)文件是否存在
例子
const fsrequire(fs)
const buf new Buffer.alloc(1024);
fs.mkdir(../file/,function (err) {if(err){console.log(err)}console.log(目录创建成功)
})
fs.writeFile(../file/input.txt,通过writeFile写入文件内容,function (err) {if(err){return console.log(err)}console.log(文件写入成功)
})
fs.readdir(../file/,function (err,files) {if(err){return console.error(err)}files.forEach(function (file) {console.log(目录读取开始)console.log(file)})})
fs.open(../file/input.txt,r,function (err,fd) {if(err){return console.error(err)}console.log(开始文件读取)fs.read(fd,buf,0,buf.length,0,function (err,bytes) {if(err){console.log(err)}if(bytes0){console.log(buf.subarray(0,bytes).toString())}console.log(bytes字节被读取)})fs.close(fd,function (err) {if(err){console.log(err)}console.log(文件读取关闭)})
})
fs.unlink(../file/input.txt,function (err) {if(err){return console.error(err)}console.log(文件删除成功)})
setTimeout(function() {fs.rmdir(../file/, function (err) {if (err) {return console.log(err)}console.log(目录删除成功)})
},3000)path路径模块 Node在读取或者写入文件使用相对路径的时候会以Node使用的目录为起点直接拼接相对目录的字符串可以使用绝对路径或者path模块解决。
命令说明sep平台路径分割符join()拼接路径basename()最后的文件名extname()文件扩展名isAbsolute()判断是否为绝对路径relative转为相对路径normalize规范化路径
const pathrequire(path)
console.log(path.sep) //路径分割符
const pathStrpath.join(/tmp,/Node,fs,../,path)
console.log(pathStr)const pathStr1path.join(__dirname,../files/input.txt)
console.log(pathStr1)console.log(path.basename(../files/input.txt)) //获取最后的文件名
console.log(path.extname(../files/input.txt)) //文件扩展名
console.log(path.isAbsolute(/tmp/node/path)) //判断是否是绝对路径
console.log(path.relative(/tmp/node/path,/tmp/node/http/)) //转换成相对路径
console.log(path.normalize(/test/test1//2slashes/1slash/tab/../)); //规范化路径烟花分割案例 在index.html页面中CSS和Javascript都写入了里面没有分成index.cssindex.js文件使用fs和path对index.html分割成这几个文件。 原index.html:
!doctype html
html
head
meta charsetutf-8
title烟花动画特效/titlestyle
html,body{margin:0px;width:100%;height:100%;overflow:hidden;background:#000;
}#canvas{width:100%;height:100%;
}
/style/head
bodycanvas idcanvas/canvasscript
function initVars(){piMath.PI;ctxcanvas.getContext(2d);canvas.widthcanvas.clientWidth;canvas.heightcanvas.clientHeight;cxcanvas.width/2;cycanvas.height/2;playerZ-25;playerXplayerYplayerVXplayerVYplayerVZpitchyawpitchVyawV0;scale600;seedTimer0;seedInterval5,seedLife100;gravity.02;seedsnew Array();sparkPicsnew Array();shttps://cantelope.org/NYE/;for(i1;i10;i){sparkPicnew Image();sparkPic.srcssparki.png;sparkPics.push(sparkPic);}sparksnew Array();pow1new Audio(spow1.ogg);pow2new Audio(spow2.ogg);pow3new Audio(spow3.ogg);pow4new Audio(spow4.ogg);frames 0;
}function rasterizePoint(x,y,z){var p,d;x-playerX;y-playerY;z-playerZ;pMath.atan2(x,z);dMath.sqrt(x*xz*z);xMath.sin(p-yaw)*d;zMath.cos(p-yaw)*d;pMath.atan2(y,z);dMath.sqrt(y*yz*z);yMath.sin(p-pitch)*d;zMath.cos(p-pitch)*d;var rx1-1000,ry11,rx21000,ry21,rx30,ry30,rx4x,ry4z,uc(ry4-ry3)*(rx2-rx1)-(rx4-rx3)*(ry2-ry1);if(!uc) return {x:0,y:0,d:-1};var ua((rx4-rx3)*(ry1-ry3)-(ry4-ry3)*(rx1-rx3))/uc;var ub((rx2-rx1)*(ry1-ry3)-(ry2-ry1)*(rx1-rx3))/uc;if(!z)z.000000001;if(ua0ua1ub0ub1){return {x:cx(rx1ua*(rx2-rx1))*scale,y:cyy/z*scale,d:Math.sqrt(x*xy*yz*z)};}else{return {x:cx(rx1ua*(rx2-rx1))*scale,y:cyy/z*scale,d:-1};}
}function spawnSeed(){seednew Object();seed.x-50Math.random()*100;seed.y25;seed.z-50Math.random()*100;seed.vx.1-Math.random()*.2;seed.vy-1.5;//*(1Math.random()/2);seed.vz.1-Math.random()*.2;seed.bornframes;seeds.push(seed);
}function splode(x,y,z){t5parseInt(Math.random()*150);sparkV1Math.random()*2.5;typeparseInt(Math.random()*3);switch(type){case 0:pic1parseInt(Math.random()*10);break;case 1:pic1parseInt(Math.random()*10);do{ pic2parseInt(Math.random()*10); }while(pic2pic1);break;case 2:pic1parseInt(Math.random()*10);do{ pic2parseInt(Math.random()*10); }while(pic2pic1);do{ pic3parseInt(Math.random()*10); }while(pic3pic1 || pic3pic2);break;}for(m1;mt;m){sparknew Object();spark.xx; spark.yy; spark.zz;p1pi*2*Math.random();p2pi*Math.random();vsparkV*(1Math.random()/6)spark.vxMath.sin(p1)*Math.sin(p2)*v;spark.vzMath.cos(p1)*Math.sin(p2)*v;spark.vyMath.cos(p2)*v;switch(type){case 0: spark.imgsparkPics[pic1]; break;case 1:spark.imgsparkPics[parseInt(Math.random()*2)?pic1:pic2];break;case 2:switch(parseInt(Math.random()*3)){case 0: spark.imgsparkPics[pic1]; break;case 1: spark.imgsparkPics[pic2]; break;case 2: spark.imgsparkPics[pic3]; break;}break;}spark.radius25Math.random()*50;spark.alpha1;spark.trailnew Array();sparks.push(spark);}switch(parseInt(Math.random()*4)){case 0: pownew Audio(spow1.ogg); break;case 1: pownew Audio(spow2.ogg); break;case 2: pownew Audio(spow3.ogg); break;case 3: pownew Audio(spow4.ogg); break;}dMath.sqrt((x-playerX)*(x-playerX)(y-playerY)*(y-playerY)(z-playerZ)*(z-playerZ));pow.volume1.5/(1d/10);pow.play();
}function doLogic(){if(seedTimerframes){seedTimerframesseedInterval*Math.random()*10;spawnSeed();}for(i0;iseeds.length;i){seeds[i].vygravity;seeds[i].xseeds[i].vx;seeds[i].yseeds[i].vy;seeds[i].zseeds[i].vz;if(frames-seeds[i].bornseedLife){splode(seeds[i].x,seeds[i].y,seeds[i].z);seeds.splice(i,1);}}for(i0;isparks.length;i){if(sparks[i].alpha0 sparks[i].radius5){sparks[i].alpha-.01;sparks[i].radius/1.02;sparks[i].vygravity;pointnew Object();point.xsparks[i].x;point.ysparks[i].y;point.zsparks[i].z;if(sparks[i].trail.length){xsparks[i].trail[sparks[i].trail.length-1].x;ysparks[i].trail[sparks[i].trail.length-1].y;zsparks[i].trail[sparks[i].trail.length-1].z;d((point.x-x)*(point.x-x)(point.y-y)*(point.y-y)(point.z-z)*(point.z-z));if(d9){sparks[i].trail.push(point);}}else{sparks[i].trail.push(point);}if(sparks[i].trail.length5)sparks[i].trail.splice(0,1); sparks[i].xsparks[i].vx;sparks[i].ysparks[i].vy;sparks[i].zsparks[i].vz;sparks[i].vx/1.075;sparks[i].vy/1.075;sparks[i].vz/1.075;}else{sparks.splice(i,1);}}pMath.atan2(playerX,playerZ);dMath.sqrt(playerX*playerXplayerZ*playerZ);dMath.sin(frames/80)/1.25;tMath.sin(frames/200)/40;playerXMath.sin(pt)*d;playerZMath.cos(pt)*d;yawpipt;
}function rgb(col){var r parseInt((.5Math.sin(col)*.5)*16);var g parseInt((.5Math.cos(col)*.5)*16);var b parseInt((.5-Math.sin(col)*.5)*16);return #r.toString(16)g.toString(16)b.toString(16);
}function draw(){ctx.clearRect(0,0,cx*2,cy*2);ctx.fillStyle#ff8;for(i-100;i100;i3){for(j-100;j100;j4){xi;zj;y25;pointrasterizePoint(x,y,z);if(point.d!-1){size250/(1point.d);d Math.sqrt(x * x z * z);a 0.75 - Math.pow(d / 100, 6) * 0.75;if(a0){ctx.globalAlpha a;ctx.fillRect(point.x-size/2,point.y-size/2,size,size); }}}}ctx.globalAlpha1;for(i0;iseeds.length;i){pointrasterizePoint(seeds[i].x,seeds[i].y,seeds[i].z);if(point.d!-1){size200/(1point.d);ctx.fillRect(point.x-size/2,point.y-size/2,size,size);}}point1new Object();for(i0;isparks.length;i){pointrasterizePoint(sparks[i].x,sparks[i].y,sparks[i].z);if(point.d!-1){sizesparks[i].radius*200/(1point.d);if(sparks[i].alpha0)sparks[i].alpha0;if(sparks[i].trail.length){point1.xpoint.x;point1.ypoint.y;switch(sparks[i].img){case sparkPics[0]:ctx.strokeStyle#f84;break;case sparkPics[1]:ctx.strokeStyle#84f;break;case sparkPics[2]:ctx.strokeStyle#8ff;break;case sparkPics[3]:ctx.strokeStyle#fff;break;case sparkPics[4]:ctx.strokeStyle#4f8;break;case sparkPics[5]:ctx.strokeStyle#f44;break;case sparkPics[6]:ctx.strokeStyle#f84;break;case sparkPics[7]:ctx.strokeStyle#84f;break;case sparkPics[8]:ctx.strokeStyle#fff;break;case sparkPics[9]:ctx.strokeStyle#44f;break;}for(jsparks[i].trail.length-1;j0;--j){point2rasterizePoint(sparks[i].trail[j].x,sparks[i].trail[j].y,sparks[i].trail[j].z);if(point2.d!-1){ctx.globalAlphaj/sparks[i].trail.length*sparks[i].alpha/2;ctx.beginPath();ctx.moveTo(point1.x,point1.y);ctx.lineWidth1sparks[i].radius*10/(sparks[i].trail.length-j)/(1point2.d);ctx.lineTo(point2.x,point2.y);ctx.stroke();point1.xpoint2.x;point1.ypoint2.y;}}}ctx.globalAlphasparks[i].alpha;ctx.drawImage(sparks[i].img,point.x-size/2,point.y-size/2,size,size);}}
}function frame(){if(frames100000){seedTimer0;frames0;}frames;draw();doLogic();requestAnimationFrame(frame);
}window.addEventListener(resize,(){canvas.widthcanvas.clientWidth;canvas.heightcanvas.clientHeight;cxcanvas.width/2;cycanvas.height/2;
});initVars();
frame();
/script/body
/html
const fsrequire(fs)
const pathrequire(path)const regStyle/style[\s\S]*\/style/
const regJs/script[\s\S]*\/script/fs.readFile(path.join(__dirname,static/index.html),function (err,data) {if(err){return console.log(文件读取失败)}resolveCSS(data)resoleJs(data)resoleHtml(data)
})
function resolveCSS(htmlStr) {const rregStyle.exec(htmlStr)const newCssr[0].replace(style,).replace(/style,)fs.writeFile(path.join(__dirname,static/style.css),newCss,function (err) {if(err) {return console.log(CSS文件写入失败err.message)}})
}
function resoleJs(htmlStr){const rregJs.exec(htmlStr)const newJsr[0].replace(script,).replace(/script,)fs.writeFile(path.join(__dirname,static/index.js),newJs,function (err) {if(err) {return console.log(JS文件写入失败err.message)}})
}function resoleHtml(htmlStr) {const rregStyle.exec(htmlStr)const r1regJs.exec(htmlStr)const pathStyle./style.cssconst pathStyle1./index.jsconst newHtmlhtmlStr.toString().replace(r[0],link relstylesheet hrefpathStyle/).replace(r1[0],script srcpathStyle1/script)fs.writeFile(path.join(__dirname,static/index.html),newHtml,function (err) {if(err){return console.log(文件写入失败err.message)}})
}三、Http模块 http模块是Node.js官方提供的用来创建web服务器的模块通过http模块提供的createServer()方法将电脑变成Web服务器对外提供Web资源服务。 烟花案例
const httprequire(http)
const fsrequire(fs)
const url require(url)
const pathrequire(path)serverhttp.createServer()server.on(request ,function (req,res) {const url req.urllet pathnameif(url/ || url/index.html){pathnamepath.join(__dirname,./static/index.html)}else{pathnamepath.join(__dirname,./static,url)}fs.readFile(pathname,utf-8,function (err,data) {if(err){res.write(出现未知错误err.message)}res.end(data)})
})server.listen(8080,function () {console.log(服务器运行在http://127.0.0.1:8080)
})
Get请求
const http require(http);
const url require(url);http.createServer(function(req, res){res.writeHead(200, {Content-Type: text/html;charsetutf-8});const params url.parse(req.url, true).query;if(params.usernameadminparams.passwordadmin123){res.write(h1登录成功/h1)}res.end();}).listen(8080);post请求
const httprequire(http)
const querystringrequire(querystring)let postHTMLhtmlheadmeta charsetutf-8titleNode.js 例子/title/head body form methodpost 用户名 input nameusernamebr 密码 input namepasswordbr input typesubmit /form /body/html;serverhttp.createServer(function (req,res) {let body;req.on(data,function (chunk) {bodychunk})req.on(end,function () {bodyquerystring.parse(body)res.writeHead(200, {Content-Type: text/html; charsetutf8});if(body.usernameadminbody.passwordadmin123){res.write(h1登录成功/h1)}else{res.write(postHTML)}res.end()})
}).listen(8080)四、模块化
模块化在编程中指的是遵守固定规则把一个大文件拆成相互依赖的多个小模块从而提高代码的复用性可维护性按需加载等。模块化规范对代码进行模块化拆分和组合时要遵守的规则从而方便了各大模块之间的相互调用。Node.js的模块分类内置模块(官方提供自定义模块(用户自己创建)第三方模块(第三方开发使用前下载模块作用域为了防止全局变量污染自定义模块中定义的变量和方法等成员只能在当前模块被访问。向外共享模块作用域成员每个.js自定义模块中都有module对象存储了和当前模块的有关信息使得模块内成员可以通过export被模块外访问到。 向外共享成员例子
//向module.exports对象上挂载username属性或方法
module.exports.usernameAiwin
module.exports.sayfunction () {console.log(您好,exports向外共享)
}function NoInvoke() {console.log(这里不能暴露出去)
}const mrequire(./test)
console.log(m)使用require()方法导入模块时导入的结果永远以module.exports指向的新对象为准即exports对象可以被重覆盖。 //向module.exports对象上挂载username属性或方法
module.exports.usernameAiwin
module.exports.sayfunction () {console.log(您好,exports向外共享)
}//require指向永远以新的对象为准
module.exports{name:Aiwin,say(){console.log(Hello)}
}
function NoInvoke() {console.log(这里不能暴露出去)
}为了简化向外共享成员的代码Node提供了exports对象默认情况下module.exports和exports指向同一对象最终共享的结果以module.exports指向对象为准。 npm和包NodeJs中的包就是由第三方个人或团队开发出来的模块免费供所有人使用包基于内置模块封装出来提供了更高级更方便的API极大的提高了开发效率。 全球最大的包共享平台 npm初体验
传统初始化时间
function dataFormat(timeStr){const datanew Date(timeStr)const ydata.getFullYear()const mpadZero(data.getMonth())const dpadZero(data.getDate())const hhpadZero(data.getHours())const mmpadZero(data.getMinutes())const sspadZero(data.getSeconds())return ${y}-${m}-${d} ${hh}:${mm}:${ss}
}function padZero(n) {return n9?n:0n
}
module.exports{dataFormat
}
const timerequire(./dataFormat)const tnew Date()
console.log(t)
console.log(time.dataFormat(t))npm的一些使用
命令说明npm install 包完整名称版本号安装npm包npm i 包名称版本号安装npm包npm init -y创建package.json文件npm config get registry查看镜像源npm config set registryhttps://registry.npm.taobao.org/安装淘宝镜像源npm i nrm -g下载nrmnpm uninstall 包名 -g卸载全局安装的包nrm ls查看可下载镜像源nrm use 名称更换镜像源npm uninstall卸载包npm i 包名 -D将包记录到devDependencies节点npm i / npm install自动从dependencies中识别包下载devDependencies中的包是指项目只在开发阶段用到的包项目上线后遇不到dependencies中有项目安装的npm包。 使用npm
const momentrequire(moment)const timemoment().format(YYYY-MM-DD HH:mm:ss)
console.log(time)规范的包结构应包含以下3点要求 1包必须以单独的目录存在。 2包的顶级目录必须包含package.json管理配置文件。 3package.json必须包含name、version、main这三个属性代表包名、版本、入口。
五、开发属于自己的包
功能要求1.转义HTML中的特殊字符 2.还原转义字符
步骤如下1. 创建新目录作为包的根目录 2.在根目录下创建入口jspackage.jsonREADME.MD 3.编写package.json和代码 注意包名必须唯一创建包名前须查看是否存在相同包名 创建package.json package.json的一些基本信息包名版本号入口关键词协议 编写index.js代码
function htmlEscape(html) {return html.replace(/|||/g,match{switch (match) {case :return lt;case :return gt;case :return quot;case :return amp;}})
}
function htmlUnEscape(html) {return html.replace(/lt;|gt;|quot;|amp;/g,match{switch (match) {case lt;:return case gt;:return case quot;:return case amp;:return }})
}
module.exports{htmlEscape,htmlUnEscape
}测试
const {htmlEscape, htmlUnEscape} require(../Aiwin-tools);const htmlStrh1 titletest这是一次测试spanTestnbsp;/span/h1const strhtmlEscape(htmlStr)
console.log(str)
console.log(htmlUnEscape(str))编写README.MD文档 将包用法说清楚即可没有特别的规范 将包发布到npm 前往npm官网注册一个账号npm login登录npm账号,注意登录服务器必须为官网的服务器npm publish 发布包npm unpublish 包名 --force 删除发布72小时内发布的包 模块加载机制
模块加载机制优先从缓存中加载即多次调用require()不会导致模块代码被执行多次。内置模块加载机制内置模块的加载优先级最高如require(‘fs’)始终返回内置的fs模块即可node_modules目录下有相同的包叫fs。自定义模块加载机制使用require()加载自定义模块必须指定./或…/开头的路径标识符如果省略文件名则会按照顺序为确切文件名补.js.json.node扩展名加载。第三方模块加载机制如果传递给require()的模块标识符不是内置模块也没有./或者…/开头则会从当前目录向上寻找node_modules文件夹加载第三方模块。目录作为模块标识符会在被加载目录下寻找package.json文件寻找main属性作为加载入口若没有则加载目录下index.js文件若两者都没有则报错。
六、Express
1.初识Express
Express基于Node.js平台快读、开放、极简的Web开发框架通俗的说就是http模块的加强版是基于http模块进一步封装出来极大提高开发效率专门用于创建Web服务器。安装express安装到项目中直接npm install下载即可。创建基本Web服务器
const express require(express);
const app express();app.get(/, function (req, res) {res.send(Hello express);
})const server app.listen(8081, function () {const host server.address().address;const port server.address().port;console.log(express server running at https://%s:%s, host, port)});GET/POST
const express require(express);
const bodyParser require(body-parser);
const app express();
app.use(bodyParser.urlencoded({ extends: false }))
app.use(./public,express.static(public))
app.get(/, function (req, res) {res.sendFile( __dirname /public/ index.html );
})app.post(/post,function (req,res) {let response{username:req.body.username,password:req.body.password}res.end(JSON.stringify(response))
})//动态参数
app.get(/user/:username/:password,function (req,res) {res.send(req.params)
})
const server app.listen(8081, function () {const {address,port} server.address();console.log(express server running at https://%s:%s, address, port)});!doctype html
html
headmeta charsetutf-8titleExpress/title
/head
body
form actionhttp://127.0.0.1:8081/post methodPOSTUsername: input typetext nameusername brPassword: input typetext namepasswordinput typesubmit valueSubmit
/form
/body
/html express.static()创建静态资源服务器如果有多个静态资源目录会按添加顺序查找所需文件注意post请求中的body要解析才能提取参数。 nodemon 用于在编写调试Node.js项目时修改了项目代码工具会自动监听项目文件变动当代码被修改后自动重启项目方便开发和调试类似于springboot的devtools。 安装npm install -g nodemon 使用nodeman将启动项目的node命令替换为nodeman命令即可。
2.路由模块化
为了方便路由进行模块化管理Express不建议将路由直接挂载到app推荐将路由抽离为单独模块步骤如下
创建路由模块对应的js文件调用express.Router()函数创建路由对象向路由对象上挂载具体路由使用module.exports向外共享路由对象使用app.use()函数注册路由模块
router.js:
const expressrequire(express)const routerexpress.Router()router.get(/get,function (req,res) {res.send(这是Get请求)
})router.post(/post,function (req,res) {res.send(这是post请求)
})
module.exportsrouter
express.js:
const routerrequire(./router)
const expressrequire(express)
const appexpress()app.use(router)const server app.listen(8081, function () {const {address,port} server.address();console.log(express server running at https://%s:%s, address, port)});3.中间件
当一个请求到达Express的服务器之后可以连续调用多个中间件从而对这次请求进行预处理。Express中间件本质上就是一个function处理函数格式如下function(req,res,next)必须包含next参数next表示把流转关系转交给下一个中间件或路由。全局中间件到达服务器后都会触发的中间件。
例子登录拦截器
const expressrequire(express)
const appexpress()
const bodyParser require(body-parser);
const cookieParser require(cookie-parser);app.use(bodyParser.urlencoded({ extends: false }))
app.use(express.static(public))
app.use(cookieParser())
const middlewarefunction (req,res,next) {let urlreq.urlconst cookiereq.cookiesif(url!/logincookie.name!Aiwin){res.location(/)res.setHeader(Location,/)res.send(301)}next()
}
app.use(middleware)app.get(/,function (req,res) {res.sendFile(__dirnameindex.html)
})
app.post(/login,function(req,res){const usernamereq.body.usernameconst passwordreq.body.passwordif(usernameadminpasswordadmin){res.cookie(name,Aiwin,{maxAge:3600})res.setHeader(Location,/admin)res.location(/admin)res.send(301)}})
app.get(/admin,function (req,res) {res.sendFile(__dirname/public/admin.html)
})
const server app.listen(8082, function () {const {address,port} server.address();console.log(express server running at https://%s:%s, address, port)});admin.html:
!doctype html
html
headmeta charsetutf-8titleExpress/title
/head
body
form actionhttp://127.0.0.1:8082/login methodPOSTUsername: input typetext nameusername brPassword: input typetext namepasswordinput typesubmit valueSubmit
/form
/body
/html
admin.html:
!DOCTYPE html
html langen
headmeta charsetUTF-8titleAdmin/title
/head
body
h1这是登录后的页面/h1
/body
/html所有的请求都会经过中间件在没有登录成功获取cookie的时候无法访问/admin页面 局部中间件不使用app.use()定义中间件使用app.METHOD(PATH中间件function)定义局部中间件只对该路由生效。
const expressrequire(express)const appexpress()
const mwfunction (req,res,next) {req.a局部中间件生效next()
}
app.get(/,mw,function (req,res) {res.send(req.a)
})
app.get(/other,function (req,res) {res.send(局部中间件不生效)
})const server app.listen(8081, function () {const {address,port} server.address();console.log(express server running at https://%s:%s, address, port)});6. 中间件注意事项 在路由之前注册中间件(错误级别除外)。中间件一定要执行next()函数。连续调用多个中间件多个中间件共享reqres对象。 中间件分类
应用级别中间件通过app实例上的中间件。路由级别中间件绑定到Router()上的中间件。错误级别中间件捕获整个项目发生的异常作物防止项目崩溃。
const express require(express)
const app express()// 注意除了错误级别的中间件其他的中间件必须在路由之前进行配置
// 通过 express.json() 这个中间件解析表单中的 JSON 格式的数据
app.use(express.json())
app.use(express.urlencoded({extended:false}))
app.post(/, (req, res) {// 在服务器可以使用 req.body 这个属性来接收客户端发送过来的请求体数据// 默认情况下如果不配置解析表单数据中间件则 req.body 默认等于 undefinedconsole.log(req.body)res.send(OK)
})
app.post(/url,function (req,res) {console.log(req.body)res.send(OK)
})
app.listen(8081)错误级别中间件注册到路由之后res.send()没有执行因为错误中间件中断掉了后面的执行。 内置中间件
中间件作用express.static托管静态资源中间件express.json解析JSON格式请求体数据express.urlencoded解析URL-encoded格式请求体数据
const express require(express)
const app express()// 注意除了错误级别的中间件其他的中间件必须在路由之前进行配置
// 通过 express.json() 这个中间件解析表单中的 JSON 格式的数据
app.use(express.json())app.post(/, (req, res) {// 在服务器可以使用 req.body 这个属性来接收客户端发送过来的请求体数据// 默认情况下如果不配置解析表单数据中间件则 req.body 默认等于 undefinedconsole.log(req.body)res.send(OK)
})app.listen(8081)第三方中间件非官方内置由别人开发可以进行下载使用的跟第三方模块差不多。
自定义中间件
手动模拟express.urlencoded中间件解析POST提交到服务器的表单数据。
编写代码封装为模块
自定义中间件.js
const querystring require(querystring);
const mwfunction (req,res,next) {let body//监听data事件,把数据切割,避免数据太大,无法接受req.on(data,(chunk){bodychunk})req.on(end,(){req.bodyquerystring.parse(body)next()})
}module.exportsmwexpress.js
const expressrequire(express)
const mwrequire(./自定义中间件)const appexpress()
app.use(express.static(public))
app.get(/,(req,res){console.log(1)res.sendFile(__dirnameindex.html)
})
app.post(/login,mw,(req,res){let response{name:req.body.name,gender:req.body.gender}res.send(response)
})app.listen(3000)4. 跨域问题
CORS相关响应头
名称作用Access-Control-Allow-Origin只允许访问该资源的外域URLAccess-Control-Allow-Headers仅支持服务端发送9个请求头设置此参数额外增加Access-Control-Allow-Methods允许客户端请求服务器的方法
CORS跨域请求资源分类
简单请求只允许getposthead且请求头不超过合规的9个字段无自定义头部。一次预检请求请求方式为getposthead外的请求请求头包含自定义头部向服务器发送application/json格式数据。浏览器会发送OPTION请求进行预检以获知服务器是否允许该实际请求服务器响应预检请求后才会发送真正的请求。两次
解决方案1. CORS中间件JSONP(有缺陷只支持POST请求) npm install cors直接使用 router.js:
const expressrequire(express)
const routerexpress.Router()router.get(/get,function (req,res) {const queryreq.queryres.send({status:0,message:GET 请求成功!,data:query})
})
router.post(/post,function (req,res) {const queryreq.bodyres.send({status:0,message:POST 请求成功!,data:query})
})
router.delete(/delete,function (req,res) {res.send({status:0,message:Delete 请求成功})
})
module.exportsrouterexpress.js:
const expressrequire(express)
const appexpress()
const routerrequire(./router)
const corsrequire(cors)
app.use(cors())
app.use(express.urlencoded({extended:false}))
app.use(/api,router)
app.listen(8080)index.html:
!DOCTYPE html
html langen
headmeta charsetUTF-8title跨域问题/titlescript srchttps://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js/script
/head
body
button idbtnGetGET/button
button idbtnPostPOST/button
button idbtnDeleteDelete/button
script
$(#btnGet).on(click,function () {$.ajax({type:GET,url:http://127.0.0.1:8080/api/get?nameAiwin,data:{name:Aiwin},success:function (res) {console.log(res)}})
})
$(#btnPost).on(click,function () {$.ajax({type:POST,url:http://127.0.0.1:8080/api/post,data:{name:Aiwin},success:function (res) {console.log(res)}})
})
$(#btnDelete).on(click,function () {$.ajax({type:DELETE,url:http://127.0.0.1:8080/api/delete,success:function (res) {console.log(res)}})
})
/script
/body
/html七、Mysql模块
安装与配置
安装操作Mysql数据库的第三方模块通过mysql模块连接到Mysql数据库通过mysql模块执行SQL语句
const mysqlrequire(mysql)const databasemysql.createPool({host:127.0.0.1, //数据库的IP地址user:root, //登录用户名password:root, //登录密码database:node //要操作的数据库
})//测试mysql是否连接成功
database.query(SELECT 1,(err,results){if(err){return console.log(err.message)}console.log(results)
})基本使用
mysql模块进行mysql的操作其实就是通过数据库语句对数据库进行查询只不过mysql模块负责与数据库交互。
const connectrequire(mysql)const databaseconnect.createPool({host:127.0.0.1, //数据库的IP地址user:root, //登录用户名password:root, //登录密码database:node //要操作的数据库
})//SELECT查询
database.query(select *from users,(err,results){if(err){return console.log(err.message)}console.log(results)
})//INSERT插入数据
const user{username:Tom,password:IamTom}
const sqlStrINSERT INTO users (username,password) VALUES (?,?)
//const sqlStrINSERT INTO users SET? 插入数据字段对应的时候
//database.query(sqlStr,user,function)
database.query(sqlStr,[user.username,user.password],(err,results){if(err){return console.log(err.message)}if(results.affectedRows1){ //results是一个对象,内置了fieldCount、affectedRows、InsertId、serverStatus、 warningCount、 message、 protocol41、changedRows属性console.log(数据插入成功)}
})//更新数据库对象
const UpdateUser{id:2,username:Jerry,password:IamJerry}
const updateStrUPDATE users SET username?,password? where id?
// const updateStrUPDATE users SET ? where id?
database.query(updateStr,[UpdateUser.username,UpdateUser.password,UpdateUser.id],(err,results){if(err){return console.log(err.message)}if(results.affectedRows1){console.log(更新成功)}
})//Delete删除
const DeleteStrDelete FROM users where id?
database.query(DeleteStr,2,(err,results){if(err){return console.log(err.message)}if(results.affectedRows1){console.log(删除成功)}
})删除推荐使用标记删除即使用status等字段标记删除避免误操作delete直接真删除了 Web开发模式
服务器渲染的Web开发服务器发送给客户端HTML页面是在服务器通过字符串拼接动态生成的不需要使用ajax额外请求数据 优点 前端耗时少服务器负责动态生成HTML内容浏览器直接渲染页面即可。利于SEO服务器响应的是完整的HTML页面内容爬虫更容易爬取。 缺点 占用服务器资源服务器端完成HTML页面拼接如果请求较多服务器压力过大。不利于前后端分离开发效率低使用服务器渲染无法完成分工合作对于前端复杂度高的项目不利于开发。 前后端分离的Web开发依赖于Ajax技术的应用后端只负责提供API接口前端使用ajax调用接口的技术。 优点 开发体验好利于分工前端负责UI开发后端专注于API开发前端选择性更多用户体验好实现页面局部刷新。减轻服务器端渲染压力。 缺点 不利于SEO完整的HTML页面需要在客户端动态拼接完成爬虫无法爬取页面有效信息。 Session认证
通过session-express中间件使用session。
const expressrequire(express)
const appexpress()
const sessionrequire(express-session)
app.use(express.urlencoded({extended:false}))
app.use(session({secret:Aiwin,resave:false, //是否允许并行发送多个请求saveUninitialized:true //初始化session时是否保存到存储。默认为true
}))
app.post(/api/login,(req,res){if(req.body.username!admin||req.body.password!admin){return res.send({status:1,message:登录失败})}req.session.userreq.bodyreq.session.islogintrueres.send({status:0,message:登录成功})})
app.get(/api/username,(req,res){if(!req.session.islogin){return res.send({status:1,message:fail})}return res.send({status:0,username:req.session.user})
})
app.get(/api/logout,(req,res){req.session.destroy()res.send({status:0,message:退出Session成功})
})
app.listen(8080)未登录时 登录后
JWT Session认证机制需要配合Cookie才能实现由于Cookie不支持跨域访问当涉及前端跨域请求后端接口时需要使用很多额外的配置才能实现Session认证此时推荐使用JWT。 工作原理用户的信息通过Token字符串的形式保存在客户端浏览器服务器通过还原token字符串的形式来认证用户身份。 JWT组成部分 JWTHeader(头部).Payload(有效载荷).Signature(签名)
部分说明头部HeaderJWT类型加密算法JWT主要加密算法又HS256 RS256载荷Payload携带存放的数据和注册声明包括签发者接收者签发时间过期时间等签名Signature使用头部定义的加密算法和密钥生成对数据进行加密生成
使用中间件 jsonwebtoken 生成JWT字符串 express-jwt 用于将JWT字符串解析还原成JSON对象 const expressrequire(express)
const jwtrequire(jsonwebtoken)
const {expressjwt: expressJWT} require(express-jwt);
const bodyParserrequire(body-parser)
const appexpress()
app.use(bodyParser.urlencoded({ extends: false }))
const secretKeyAiwin
//.unless()指定那些接口不需要访问权限
app.use(expressJWT({secret: secretKey,algorithms: [HS256],}).unless({path:[/^\/api\//]})
);
app.post(/api/login,(req,res){const userreq.bodyif(user.username!admin||user.password!admin){return res.send({status:1,message:登录失败})}const tokenjwt.sign({username:user.username},secretKey,{expiresIn:300s})res.send({status:200,message:登录成功,token:token})})
app.get(/admin/getinfo,(req,res) {res.send({status:200,message:获取用户信息成功,data:req.auth})
})
//捕捉解析JWT解析失败
app.use((err,req,res,next){if(err.nameUnauthorizedError){return res.send({status:401,message:无效token})}res.send({status:500,message:未知错误})
})
app.listen(8080)八、multer模块
用于处理multipart/form-data类型的表单数据,它主要用于上传文件
例子 app.js:
const expressrequire(express)
const appexpress()
app.use(/multer,require(./multer));app.listen(3000)multer.js:
const router require(express).Router();
const multer require(multer);
const pathrequire(path)
path.extname(jpg)
const fsrequire(fs)
const upload multer({dest:./upload//上传文件存放路径
});const singleMidle upload.single(singleFile);//一次处理一张
//可同时处理多个上传控件的上传
//实际项目中根据自己的情况使用以上三种用法之一即可
router.get(/,(req,res){res.sendFile(path.join(__dirname,/public/index.html))
})
router.post(/singup, singleMidle, function (req, res) {let oldnamereq.file.pathlet newnamereq.file.path path.extname(req.file.originalname)fs.renameSync(oldname,newname)res.send(req.file);
});module.exports router;index.html:
!DOCTYPE html
html
headmeta charsetutf-8titlemulter的使用方案/title
/head
body
form class action/multer/singup methodpost enctypemultipart/form-data!--enctypemultipart/form-data表示不会对数据本身编码multer只处理enctypemultipart/form-data的表单数据--input typefile namesingleFile valueinput typesubmit name value上传1
/form
/body
/html九、大事件项目
参考文档某大事件项目
完成的API接口 链接https://pan.baidu.com/s/1CXZvE1uq7AFo5iggCDjKFg?pwds483 提取码s483 十、总结
学的东西都比较基础上手也是非常快最后的项目也是相当于把前面复习的都用了一遍感觉NodeJS开发效率也是很高有很多第三方中间件能直接引用完成很多东西不需要再自己写算是站在别人的肩膀上自己学的比较粗糙因为只要求自己能看懂基本的代码即可真正要学好还得细学。