nodejs初學(xué)筆記完整版_第1頁
nodejs初學(xué)筆記完整版_第2頁
nodejs初學(xué)筆記完整版_第3頁
nodejs初學(xué)筆記完整版_第4頁
nodejs初學(xué)筆記完整版_第5頁
已閱讀5頁,還剩61頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

nodejs初學(xué)筆記目錄Node.js(最全)基礎(chǔ)+全棧項目一、Node.js基礎(chǔ)認(rèn)識Node.jsnodejs的特性使用Node.js需要了解多少JavaScript瀏覽器環(huán)境vsnode環(huán)境開發(fā)環(huán)境搭建模塊、包、commonJSCommonJS規(guī)范modules模塊化規(guī)范寫法Npm&Yarnnpm的使用全局安裝nrmyarn使用內(nèi)置模塊http模塊url模塊querystring模塊http模塊補(bǔ)充event模塊fs文件操作模塊stream流模塊zlibcrypto路由基礎(chǔ)獲取參數(shù)靜態(tài)資源處理二、Express1.特色2.安裝3.路由4.中間件應(yīng)用級中間件路由級中間件錯誤處理中間件內(nèi)置的中間件第三方中間件5.獲取請求參數(shù)6.利用Express托管靜態(tài)文件7.服務(wù)端渲染(模板引擎)三、MongoDB1.關(guān)系型與非關(guān)系型數(shù)據(jù)庫2.安裝數(shù)據(jù)庫3.啟動數(shù)據(jù)庫windowsmac4.在命令行中操作數(shù)據(jù)庫5.可視化工具進(jìn)行增刪改查6.nodejs連接操作數(shù)據(jù)庫四、接口規(guī)范與業(yè)務(wù)分層1.接口規(guī)范2.業(yè)務(wù)分層五、登錄鑒權(quán)Cookie&SessionJSONWebToken(JWT)介紹實(shí)現(xiàn)六、文件上傳管理APIDOC-API文檔生成工具Koa21.簡介2.快速開始安裝koa2helloworld代碼啟動demo3.koavsexpress3.1更輕量Context對象異步流程控制中間件模型4.路由4.1基本用發(fā)router.allowedMethods作用請求方式4.4拆分路由路由前綴路由重定向靜態(tài)資源獲取請求參數(shù)6.1get參數(shù)6.2post參數(shù)ejs模板安裝模塊使用模板引擎8.cookie&sessioncookiesession9.JWT10.上傳文件11.操作MongoDB九、MySQL1.介紹2.與非關(guān)系數(shù)據(jù)庫區(qū)別3.sql語句4.nodejs操作數(shù)據(jù)庫十、Socket編程1.websocket介紹2.ws模塊3.socket.io模塊十一、mocha1.編寫測試2.chai斷言庫3.異步測試4.http測試5.鉤子函數(shù)Node.js(最全)基礎(chǔ)+全棧項目 \h 作者:kerwin 版本:QF1.0 版權(quán):千鋒HTML5大前端教研院 公眾號:大前端私房菜一、Node.js基礎(chǔ) \h1.認(rèn)識Node.js \hNode.js是一個javascript運(yùn)行環(huán)境。它讓javascript可以開發(fā)后端程序,實(shí)現(xiàn)幾乎其他后端語言實(shí)現(xiàn)的所有功能,可以與PHP、Java、Python、.NET、Ruby等后端語言平起平坐。Nodejs是基于V8引擎,V8是Google發(fā)布的開源JavaScript引擎,本身就是用于Chrome瀏覽器的js解釋部分,但是RyanDahl這哥們,鬼才般的,把這個V8搬到了服務(wù)器上,用于做服務(wù)器的軟件。01nodejs的特性 \hNodejs語法完全是js語法,只要你懂js基礎(chǔ)就可以學(xué)會Nodejs后端開發(fā)NodeJs超強(qiáng)的高并發(fā)能力,實(shí)現(xiàn)高性能服務(wù)器開發(fā)周期短、開發(fā)成本低、學(xué)習(xí)成本低02使用Node.js需要了解多少JavaScript \h \hhttp://node\hj\h/learn/how-much-\hj\havascript-do-\hy\hou-need-to-know-to-use-node\hj\hs03瀏覽器環(huán)境vsnode環(huán)境 \hNode.js可以解析JS代碼(沒有瀏覽器安全級別的限制)提供很多系統(tǒng)級別的API,如:文件的讀寫文件的讀寫(FileSystem)constfs=require('fs')fs.readFile('./ajax.png','utf-8',(err,content)=>{console.log(content)})進(jìn)程的管理進(jìn)程的管理(Process)functionmain(argv){console.log(argv)}main(process.argv.slice(2))網(wǎng)絡(luò)通信網(wǎng)絡(luò)通信(HTTP/HTTPS).2開發(fā)環(huán)境搭建\hconsthttp=require("http")http.createServer((req,res)=>{res.writeHead(200,{"content-type":"text/plain"})res.write("hellonodejs")res.end()}).listen(3000)htthttp://node\h/download\h/3.模塊、包、commonJS\h 02CommonJS規(guī)范 \h03modules模塊化規(guī)范寫法 \h我們可以把公共的功能抽離成為一個單獨(dú)的js文件作為一個模塊,默認(rèn)情況下面這個模塊里面的方法或者屬性,外面是沒法訪問的。如果要讓外部可以訪問模塊里面的方法或者屬性,就必須在模塊里面通過exports或者module.exports暴露屬性或者方法。m1.jsm1.js:main.js:constname='gp19'constsayName=()=>{console.log(name)}console.log('module1')//接口暴露方法一:module.exports={say:sayName}//接口暴露方法二:exports.say=sayName//錯誤!exports={say:sayName}constconstm1=require('./m1')m1.say()4.Npm&Yarn \h01npm的使用 \hnpmnpminitnpminstall包名–g(uninstall,update)npminstall包名--save-dev(uninstall,update)npmlist-g(不加-g,列舉當(dāng)前目錄下的安裝包)npminfo包名(詳細(xì)信息)npminfo包名version(獲取最新版本)npminstallmd5@1(安裝指定版本)npmoutdated(檢查包是否已經(jīng)過時)"dependencies":{"md5":"^2.1.0"}^表示如果直接npminstall將會安md52.*.*最新版本"dependencies":{"md5":"~2.1.0"}~表示如果直接npminstall將會安裝md52.1.*最新版本"dependencies":{"md5":"*"}*表示如果直接npminstall將會安裝md5最新版本02全局安裝nrm \hNRM(npmregistrymanager)是npm的鏡像源管理工具,有時候國外資源太慢,使用這個就可以快速地在npm源間切換。手動切換方法:npmconfigsetregistry安裝nrm在命令行執(zhí)行命令,npminstall-gnrm,全局安裝nrm。使用nrm執(zhí)行命令nrmls查看可選的源。其中,帶*的是當(dāng)前使用的源,上面的輸出表明當(dāng)前源是官方源。切換nrm如果要切換到taobao源,執(zhí)行命令nrmusetaobao。測試速度你還可以通過你還可以通過nrmtest測試相應(yīng)源的響應(yīng)時間。擴(kuò)展:nrmtestnpminstall-gcnpm--registry=03yarn使用 \hnpminstall-gyarnnpminstall-gyarn對比npm:速度超快:Yarn緩存了每個下載過的包,所以再次使用時無需重復(fù)下載。同時利用并行下載以最大化資源利用率,因此安裝速度更快。超級安全:在執(zhí)行代碼之前,Yarn會通過算法校驗(yàn)每個安裝包的完整性。開始新項目yarninit添加依賴包yarnadd[package]yarnadd[package]@[version]yarnadd[package]--dev升級依賴包yarnupgrade[package]@[version]移除依賴包yarnremove[package]安裝項目的全部依賴yarninstall5.內(nèi)置模塊 \h0101http模塊\h要使用HTTP服務(wù)器和客戶端,則必須require('http')。02url模塊\hconsthttp=require('http';)//創(chuàng)建本地服務(wù)器來從其接收數(shù)據(jù)constserver=http.createServer((req,res)=>{res.writeHead(200,{'Content-Type':'application/json'});res.end(JSON.stringify({data:'HelloWorld!'}));});server.listen(8000);consthttp=require('http';)//創(chuàng)建本地服務(wù)器來從其接收數(shù)據(jù)constserver=http.createServer();//監(jiān)聽請求事件server.on('request',(request,res)=>{res.writeHead(200,{'Content-Type':'application/json'});res.end(JSON.stringify({data:'HelloWorld!'}));;})server.listen(8000);02.1parseconstconsturl=require('url')consturlString=':443/ad/index.html?id=8&name=mouse#tag=110'constparsedStr=url.parse(urlString)console.log(parsedStr)02.2formatconstconsturl=require('url')consturlObject={protocol:'https:',slashes:true,auth:null,host:':443',port:'443',hostname:'',hash:'#tag=110',search:'?id=8&name=mouse',query:{id:'8',name:'mouse'},pathname:'/ad/index.html',path:'/ad/index.html?id=8&name=mouse'}constparsedObj=url.format(urlObject)console.log(parsedObj)02.3resolveconsturl=require('url')vara=url.resolve('/one/two/three','four')(注意最后加/,不加/的區(qū)別)varb=url.resolve('/','/one')varc=url.resolve('/one','/two')console.log(a+","+b+","+c)03querystring模塊 \h03.1parseconstconstquerystring=require('querystring')varqs='x=3&y=4'varparsed=querystring.parse(qs)console.log(parsed)03.2stringifyconstconstquerystring=require('querystring')varqo={x:3,y:4}varparsed=querystring.stringify(qo)console.log(parsed)03.3escape/unescapeconstconstquerystring=require('querystring')varstr='id=3&city=北京&url='varescaped=querystring.escape(str)console.log(escaped)constquerystring=require('querystring')varstr='id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2F'varunescaped=querystring.unescape(str)console.log(unescaped)04http模塊補(bǔ)充 \h04.1接口:jsonpconstconsthttp=require('http')consturl=require('url')constapp=http.createServer((req,res)=>{leturlObj=url.parse(req.url,true)switch(urlObj.pathname){case'/api/user':res.end(`${urlObj.query.cb}({"name":"gp145"})`)breakdefault:res.end('404.')break}})app.listen(8080,()=>{console.log('localhost:8080')})04.2跨域:CORSconstconsthttp=require('http')consturl=require('url')constquerystring=require('querystring')constapp=http.createServer((req,res)=>{letdata=''leturlObj=url.parse(req.url,true)res.writeHead(200,{'content-type':'application/json;charset=utf-8','Access-Control-Allow-Origin':'*'})req.on('data',(chunk)=>{data+=chunk})req.on('end',()=>{responseResult(querystring.parse(data))})functionresponseResult(data){switch(urlObj.pathname){case'/api/login':res.end(JSON.stringify({message:data}))breakdefault:res.end('404.')break}}})app.listen(8080,()=>{console.log('localhost:8080')})04.3模擬getvarvarhttp=require('http')varhttps=require('https')//1//1、接口2、跨域constserver=http.createServer((request,response)=>{varurl=request.url.substr(1)vardata=''response.writeHeader(200,{'content-type':'application/json;charset=utf-8','Access-Control-Allow-Origin':'*'})https.get(`/listmore.json${url}`,(res)=>{res.on('data',(chunk)=>{data+=chunk})res.on('end',()=>{response.end(JSON.stringify({ret:true,data}))})})})server.listen(8080,()=>{console.log('localhost:8080')})04.4模擬post:服務(wù)器提交(攻擊)constconsthttps=require('https')constquerystring=require('querystring')constpostData=querystring.stringify({province:'上海',city:'上海',district:'寶山區(qū)',address:'同濟(jì)支路199號智慧七立方3號樓2-4層',latitude:43.0,longitude:160.0,message:'求購一條小魚',contact:'13666666',type:'sell',time:1571217561})constoptions={protocol:'https:',hostname:'ik9hkddr.qcloud.la',method:'POST',port:443,path:'/index.php/trade/add_item',headers:{'Content-Type':'application/x-www-form-urlencoded','Content-Length':Buffer.byteLength(postData)}}functiondoPost(){letdataletreq=https.request(options,(res)=>{res.on('data',chunk=>data+=chunk)res.on('end',()=>{console.log(data)})})req.write(postData)req.end()}//setInterval(()=>{//doPost()//},1000)04.5爬蟲constconsthttps=require('https')consthttp=require('http')constcheerio=require('cheerio')http.createServer((request,response)=>{response.writeHead(200,{'content-type':'application/json;charset=utf-8'})constoptions={//protocol:'https:',hostname:'',port:443,path:'/',method:'GET'}constreq=https.request(options,(res)=>{letdata=''res.on('data',(chunk)=>{data+=chunk})res.on('end',()=>{filterData(data)})})functionfilterData(data){//console.log(data)let$=cheerio.load(data)let$movieList=$('.column.content')console.log($movieList)letmovies=[]$movieList.each((index,value)=>{movies.push({title:$(value).find('.movie-title.title').text(),detail:$(value).find('.detail.actor').text(),})})response.end(JSON.stringify(movies))}req.end()}).listen(3000)05event模塊 \hconstconstEventEmitter=require('events')classMyEventEmitterextendsEventEmitter{}constevent=newMyEventEmitter()event.on('play',(movie)=>{console.log(movie)})event.emit('play','我和我的祖國')event.emit('play','中國機(jī)長')06fs文件操作模塊 \hconstconstfs=require('fs')//創(chuàng)建文件夾fs.mkdir('./logs',(err)=>{console.log('done.')})//文件夾改名fs.rename('./logs','./log',()=>{console.log('done')})//刪除文件夾fs.rmdir('./log',()=>{console.log('done.')})//寫內(nèi)容到文件里fs.writeFile('./logs/log1.txt','hello',//錯誤優(yōu)先的回調(diào)函數(shù)(err)=>{if(err){console.log(err.message)}else{console.log('文件創(chuàng)建成功')}})//給文件追加內(nèi)容fs.appendFile('./logs/log1.txt','\nworld',()=>{console.log('done.')})//讀取文件內(nèi)容fs.readFile('./logs/log1.txt','utf-8',(err,data)=>{console.log(data)})//刪除文件fs.unlink('./logs/log1.txt',(err)=>{console.log('done.')})//批量寫文件for(vari=0;i<10;i++){fs.writeFile(`./logs/log-${i}.txt`,`log-${i}`,(err)=>{console.log('done.')})}//讀取文件/目錄信息fs.readdir('./',(err,data)=>{data.forEach((value,index)=>{fs.stat(`./${value}`,(err,stats)=>{//console.log(value+':'+stats.size)console.log(value+'is'+(stats.isDirectory()?'directory':'file'))})})})//同步讀取文件try{constcontent=fs.readFileSync('./logs/log-1.txt','utf-8')console.log(content)console.log(0)}catch(e){console.log(e.message)}//異步讀取文件:方法一fs.readFile('./logs/log-0.txt','utf-8',(err,content)=>{console.log(content)console.log(0)})console.log(1)//異步讀取文件:方法二constfs=require("fs").promises在在fs模塊中,提供同步方法是為了方便使用。那我們到底是應(yīng)該用異步方法還是同步方法呢?fs.readFile('./logs/log-0.txt','utf-8').then(result=>{console.log(result)})由于Node環(huán)境執(zhí)行的JavaScript代碼是服務(wù)器端代碼,所以,絕大部分需要在服務(wù)器運(yùn)行期反復(fù)執(zhí)行業(yè)務(wù)邏輯的代碼,必須使用異步代碼,否則,同步代碼在執(zhí)行時期,服務(wù)器將停止響應(yīng),因?yàn)镴avaScript只有一個執(zhí)行線程。服務(wù)器啟動時如果需要讀取配置文件,或者結(jié)束時需要寫入到狀態(tài)文件時,可以使用同步代碼,因?yàn)檫@些代碼只在啟動和結(jié)束時執(zhí)行一次,不影響服務(wù)器正常運(yùn)行時的異步執(zhí)行。07stream流模塊 \hstream是Node.js提供的又一個僅在服務(wù)區(qū)端可用的模塊,目的是支持“流”這種數(shù)據(jù)結(jié)構(gòu)。什么是流?流是一種抽象的數(shù)據(jù)結(jié)構(gòu)。想象水流,當(dāng)在水管中流動時,就可以從某個地方(例如自來水廠)源源不斷地到達(dá)另一個地方(比如你家的洗手池)。我們也可以把數(shù)據(jù)看成是數(shù)據(jù)流,比如你敲鍵盤的時候,就可以把每個字符依次連起來,看成字符流。這個流是從鍵盤輸入到應(yīng)用程序,實(shí)際上它還對應(yīng)著一個名字:標(biāo)準(zhǔn)輸入流(stdin)。如果應(yīng)用程序把字符一個一個輸出到顯示器上,這也可以看成是一個流,這個流也有名字:標(biāo)準(zhǔn)輸出流(stdout)。流的特點(diǎn)是數(shù)據(jù)是有序的,而且必須依次讀取,或者依次寫入,不能像Array那樣隨機(jī)定位。有些流用來讀取數(shù)據(jù),比如從文件讀取數(shù)據(jù)時,可以打開一個文件流,然后從文件流中不斷地讀取數(shù)據(jù)。有些流用來寫入數(shù)據(jù),比如向文件寫入數(shù)據(jù)時,只需要把數(shù)據(jù)不斷地往文件流中寫進(jìn)去就可以了。在在Node.js中,流也是一個對象,我們只需要響應(yīng)流的事件就可以了:data事件表示流的數(shù)據(jù)已經(jīng)可以讀取了,end事件表示這個流已經(jīng)到末尾了,沒有數(shù)據(jù)可以讀取了,error事件表示出錯了。varfs=require('fs');//打開一個流:要注意,要注意,data事件可能會有多次,每次傳遞的chunk是流的一部分?jǐn)?shù)據(jù)。要以流的形式寫入文件,只需要不斷調(diào)用write()方法,最后以end()結(jié)束:varrs=fs.createReadStream('sample.txt','utf-8');rs.on('data',function(chunk){console.log('DATA:')console.log(chunk;)});rs.on('end',function(){console.log('END');});rs.on('error',function(err{)console.log('ERROR:'+err);;})pipepipe就像可以把兩個水管串成一個更長的水管一樣,兩個流也可以串起來。一個Readable流和一個Writable流串起來后,所有的數(shù)據(jù)自動從Readable流進(jìn)入Writable流,這種操作叫pipe。在Node.js中,Readable流有一個pipe()方法,就是用來干這件事的。讓我們用pipe()把一個文件流和另一個文件流串起來,這樣源文件的所有數(shù)據(jù)就自動寫入到目標(biāo)文件里了,所以,這實(shí)際上是一個復(fù)制文件的程序:08zlib\hvarfs=require('fs');varws1=fs.createWriteStream('output1.txt','utf-8';)ws1.write('使用Stream寫入文本數(shù)據(jù)...\n');ws1.write('END.';)ws1.end;()constfs=require('fs')constreadstream=fs.createReadStream('./1.txt')constwritestream=fs.createWriteStream('./2.txt')readstream.pipe(writestream)constconstfs=require('fs')constzlib=require('zlib')constgzip=zlib.createGzip()constreadstream=fs.createReadStream('./note.txt')constwritestream=fs.createWriteStream('./note2.txt')readstream.pipe(gzip).pipe(writestream)09crypto \hcrypto模塊的目的是為了提供通用的加密和哈希算法。用純JavaScript代碼實(shí)現(xiàn)這些功能不是不可能,但速度會非常慢。Nodejs用C/C++實(shí)現(xiàn)這些算法后,通過cypto這個模塊暴露為JavaScript接口,這樣用起來方便,運(yùn)行速度也快。MD5是一種常用的哈希算法,用于給任意數(shù)據(jù)一個“簽名”。這個簽名通常用一個十六進(jìn)制的字符串表示:update()update()方法默認(rèn)字符串編碼為UTF-8,也可以傳入Buffer。如果要計算SHA1,只需要把'md5'改成'sha1',就可以得到SHA1的結(jié)果40f32b9c9932c02227819a4151feed43e131aca1。constcrypto=require('crypto');consthash=crypto.createHash('md5';)//可任意多次調(diào)用update():hash.update('Hello,world!');hash.update('Hello,nodejs!';)console.log(hash.digest('hex'));Hmac算法也是一種哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac還需要一個密鑰:constcrypto=require('crypto');constcrypto=require('crypto');consthmac=crypto.createHmac('sha256','secret-key');hmac.update('Hello,world!');hmac.update('Hello,nodejs!');console.log(hmac.digest('hex'));//80f7e22570...只要密鑰發(fā)生了變化,那么同樣的輸入數(shù)據(jù)也會得到不同的簽名,因此,可以把Hmac理解為用隨機(jī)數(shù)“增強(qiáng)”的哈希算法。AES是一種常用的對稱加密算法,加解密都用同一個密鑰。crypto模塊提供了AES支持,但是需要自己封裝好函數(shù),便于使用:constconstcrypto=require("crypto");functionencrypt(key,iv,data){letdecipher=crypto.createCipheriv('aes-128-cbc',key,iv);//decipher.setAutoPadding(true);returndecipher.update(data,'binary','hex')+decipher.final('hex');}functiondecrypt(key,iv,crypted){crypted=Buffer.from(crypted,'hex').toString('binary');letdecipher=crypto.createDecipheriv('aes-128-cbc',key,iv);returndecipher.update(crypted,'binary','utf8')+decipher.final('utf8');}key,iv必須是16個字節(jié)可以看出,加密后的字符串通過解密又得到了原始內(nèi)容。6.路由 \h01基礎(chǔ) \h/*/**@作者:kerwin*@公眾號:大前端私房菜*/varfs=require("fs")varpath=require("path")functionrender(res,path){res.writeHead(200,{"Content-Type":"text/html;charset=utf8"})res.write(fs.readFileSync(path,"utf8"))res.end()}constroute={"/login":(req,res)=>{render(res,"./static/login.html")},},"/home":(req,res)=>{render(res,"./static/home.html")},"/404":(req,res)=>{res.writeHead(404,{"Content-Type":"text/html;charset=utf8"})res.write(fs.readFileSync("./static/404.html","utf8"))}}02獲取參數(shù) \hget請求"/api/login":(req,res)=>{constmyURL=newURL(req.url,':3000');console.log(myURL.searchParams.get("username"))render(res,`{ok:1}`)}post請求"/api/login""/api/login":(req,res)=>{varpost='';//通過req的data事件監(jiān)聽函數(shù),每當(dāng)接受到請求體的數(shù)據(jù),就累加到post變量中req.on('data',function(chunk){post+=chunk;});//在end事件觸發(fā)后,通過querystring.parse將post解析為真正的POST請求格式,然后向客戶端返回。req.on('end',function(){post=JSON.parse(post);render(res,`{ok:1}`)});}03靜態(tài)資源處理 \hfunctionfunctionreadStaticFile(req,res){constmyURL=newURL(req.url,':3000')varfilePathname=path.join(__dirname,"/static",myURL.pathname);if(fs.existsSync(filePathname)){//console.log(1111)res.writeHead(200,{"Content-Type":`${mime.getType(myURL.pathname.split(".")[1])};charset=utf8`})res.write(fs.readFileSync(filePathname,"utf8"))res.end()returntrue}else{returnfalse}}二、Express \h \hhttps://www.express\hj\h/基于Node.js平臺,快速、開放、極簡的web開發(fā)框架。1.特色 \h\h2.安裝$$npminstallexpress--save3.路由 \h路由是指如何定義應(yīng)用的端點(diǎn)(URIs)以及如何響應(yīng)客戶端的請求。路由是由一個URI、HTTP請求(GET、POST等)和若干個句柄組成,它的結(jié)構(gòu)如下:app.METHOD(path,[callback...],callback),app是express對象的一個實(shí)例,METHOD是一個HTTP請求方法,path是服務(wù)器上的路徑,callback是當(dāng)路由匹配時要執(zhí)行的函數(shù)。下面是一個基本的路由示例:varvarexpress=require('express');varapp=express();//respondwith"helloworld"whenaGETrequestismadetothehomepageapp.get('/',function(req,res){res.send('helloworld');});路由路徑和請求方法一起定義了請求的端點(diǎn),它可以是字符串、字符串模式或者正則表達(dá)式。////匹配根路徑的請求app.get('/',function(req,res){res.send('root');});//匹配/about路徑的請求app.get('/about',function(req,res){res.send('about');});//匹配/random.text路徑的請求app.get('/random.text',function(req,res){res.send('random.text');});使用字符串模式的路由路徑示例:////匹配acd和abcdapp.get('/ab?cd',function(req,res){res.send('ab?cd');});//匹配/ab/******app.get('/ab/:id',function(req,res){res.send('aaaaaaa');});//匹配abcd、abbcd、abbbcd等app.get('/ab+cd',function(req,res){res.send('ab+cd');});//匹配abcd、abxcd、abRABDOMcd、ab123cd等app.get('/ab*cd',function(req,res){res.send('ab*cd');});//匹配/abe和/abcdeapp.get('/ab(cd)?e',function(req,res){res.send('ab(cd)?e');});使用正則表達(dá)式的路由路徑示例:////匹配任何路徑中含有a的路徑:app.get(/a/,function(req,res){res.send('/a/');});//匹配butterfly、dragonfly,不匹配butterflyman、dragonflyman等app.get(/.*fly$/,function(req,res){res.send('/.*fly$/');});可以為請求處理提供多個回調(diào)函數(shù),其行為類似中間件。唯一的區(qū)別是這些回調(diào)函數(shù)有可能調(diào)用next('route')方法而略過其他路由回調(diào)函數(shù)??梢岳迷摍C(jī)制為路由定義前提條件,如果在現(xiàn)有路徑上繼續(xù)執(zhí)行沒有意義,則可將控制權(quán)交給剩下的路徑。appapp.get('/example/a',function(req,res){res.send('HellofromA!');});使用多個回調(diào)函數(shù)處理路由(記得指定next對象):appapp.get('/example/b',function(req,res,next){console.log('responsewillbesentbythenextfunction...');next();},function(req,res){res.send('HellofromB!');});使用回調(diào)函數(shù)數(shù)組處理路由:varvarcb0=function(req,res,next){console.log('CB0')next()}varcb1=function(req,res,next){console.log('CB1')next()}varcb2=function(req,res){res.send('HellofromC!')}app.get('/example/c',[cb0,cb1,cb2])混合使用函數(shù)和函數(shù)數(shù)組處理路由:varvarcb0=function(req,res,next){console.log('CB0')next()}varcb1=function(req,res,next){console.log('CB1')next()}app.get('/example/d',[cb0,cb1],function(req,res,next){console.log('responsewillbesentbythenextfunction...')next()},function(req,res){res.send('HellofromD!')})4.中間件 \hExpress是一個自身功能極簡,完全是由路由和中間件構(gòu)成一個的web開發(fā)框架:從本質(zhì)上來說,一個Express應(yīng)用就是在調(diào)用各種中間件。中間件(Middleware)是一個函數(shù),它可以訪問請求對象(requestobject(req)),響應(yīng)對象(responseobject(res)),和web應(yīng)用中處于請求-響應(yīng)循環(huán)流程中的中間件,一般被命名為next的變量。中間件的功能包括:執(zhí)行任何代碼。修改請求和響應(yīng)對象。終結(jié)請求-響應(yīng)循環(huán)。調(diào)用堆棧中的下一個中間件。如果當(dāng)前中間件沒有終結(jié)請求-響應(yīng)循環(huán),則必須調(diào)用next()方法將控制權(quán)交給下一個中間件,否則請求就會掛起。Express應(yīng)用可使用如下幾種中間件:應(yīng)用級中間件路由級中間件錯誤處理中間件內(nèi)置中間件第三方中間件使用可選則掛載路徑,可在應(yīng)用級別或路由級別裝載中間件。另外,你還可以同時裝在一系列中間件函數(shù),從而在一個掛載點(diǎn)上創(chuàng)建一個子中間件棧。(1)應(yīng)用級中間件 \h應(yīng)用級中間件綁定到app對象使用app.use()和app.METHOD(),其中,METHOD是需要處理的HTTP請求的方法,例如GET,PUT,POST等等,全部小寫。例如:varvarapp=express()//沒有掛載路徑的中間件,應(yīng)用的每個請求都會執(zhí)行該中間件app.use(function(req,res,next){console.log('Time:',Date.now())next()})路由級中間件 \h路由級中間件和應(yīng)用級中間件一樣,只是它綁定的對象為express.Router()。varvarrouter=express.Router()varapp=express()varrouter=express.Router()//沒有掛載路徑的中間件,通過該路由的每個請求都會執(zhí)行該中間件router.use(function(req,res,next){console.log('Time:',Date.now())next()})//一個中間件棧,顯示任何指向/user/:id的HTTP請求的信息router.use('/user/:id',function(req,res,next){console.log('RequestURL:',req.originalUrl)next()},function(req,res,next){console.log('RequestType:',req.method)next()})})//一個中間件棧,處理指向/user/:id的GET請求router.get('/user/:id',function(req,res,next){//如果userid為0,跳到下一個路由if(req.params.id==0)next('route')//負(fù)責(zé)將控制權(quán)交給棧中下一個中間件elsenext()//},function(req,res,next){//渲染常規(guī)頁面res.render('regular')})//處理/user/:id,渲染一個特殊頁面router.get('/user/:id',function(req,res,next){console.log(req.params.id)res.render('special')})//將路由掛載至應(yīng)用app.use('/',router)錯誤處理中間件 \h錯誤處理中間件和其他中間件定義類似,只是要使用4個參數(shù),而不是3個,其簽名如下:(err,req,res,next)。appapp.use(function(err,req,res,next){console.error(err.stack)res.status(500).send('Somethingbroke!')})內(nèi)置的中間件 \hexpress.static是Express唯一內(nèi)置的中間件。它基于serve-static,負(fù)責(zé)在Express應(yīng)用中提托管靜態(tài)資源。每個應(yīng)用可有多個靜態(tài)目錄。appapp.use(express.static('public'))app.use(express.static('uploads'))app.use(express.static('files'))第三方中間件 \h安裝所需功能的node模塊,并在應(yīng)用中加載,可以在應(yīng)用級加載,也可以在路由級加載。下面的例子安裝并加載了一個解析cookie的中間件:cookie-parser$npminstallcookie-parser$npminstallcookie-parservarexpress=require('express')varapp=express()varcookieParser=require('cookie-parser')//加載用于解析cookie的中間件app.use(cookieParser())5.獲取請求參數(shù) \hgetreqreq.querypostappapp.use(express.urlencoded({extended:false}))app.use(express.json())req.body6.利用Express托管靜態(tài)文件 \h通過Express內(nèi)置的express.static可以方便地托管靜態(tài)文件,例如圖片、CSS、JavaScript文件等。將靜態(tài)資源文件所在的目錄作為參數(shù)傳遞給express.static中間件就可以提供靜態(tài)資源文件的訪問了。例如,假設(shè)在public目錄放置了圖片、CSS和JavaScript文件,你就可以:appapp.use(express.static('public'))現(xiàn)在,public目錄下面的文件就可以訪問了。http://localhost:3000/images/kitten.jpghttphttp://localhost:3000/images/kitten.jpghttp://localhost:3000/css/style.csshttp://localhost:3000/js/app.jshttp://localhost:3000/images/bg.pnghttp://localhost:3000/hello.html如果你的靜態(tài)資源存放在多個目錄下面,你可以多次調(diào)用express.static中間件:appapp.use(express.static('public'))app.use(express.static('files'))訪問靜態(tài)資源文件時,express.static中間件會根據(jù)目錄添加的順序查找所需的文件。如果你希望所有通過express.static訪問的文件都存放在一個“虛擬(virtual)”目錄(即目錄根本不存在)下面,可以通過為靜態(tài)資源目錄指定一個掛載路徑的方式來實(shí)現(xiàn),如下所示:app.use('/static',express.static('public'))app.use('/static',express.static('public'))現(xiàn)在,你就可以通過帶有“/static”前綴的地址來訪問public目錄下面的文件了。httphttp://localhost:3000/static/images/kitten.jpghttp://localhost:3000/static/css/style.csshttp://localhost:3000/static/js/app.jshttp://localhost:3000/static/images/bg.pnghttp://localhost:3000/static/hello.html7.7.服務(wù)端渲染(模板引擎)\h需要在應(yīng)用中進(jìn)行如下設(shè)置才能讓Express渲染模板文件:views,放模板文件的目錄,比如:app.set('views','./views')viewengine,模板引擎,比如:app.set('viewengine','ejs')三、MongoDB\h1.關(guān)系型與非關(guān)系型數(shù)據(jù)庫\hnpmiejs2.安裝數(shù)據(jù)庫 \h\hhttps://docs.mon\hg\h/manual/administration/install-communit\hy\h/3.啟動數(shù)據(jù)庫 \h(1)windows \hmongod--dbpathd:/data/dbmongod--dbpathd:/data/dbmongo(2)mac \hmongod--config/usr/local/etc/mongod.confmongod--config/usr/local/etc/mongod.confmongo4.在命令行中操作數(shù)據(jù)庫 \h 5.可視化工具進(jìn)行增刪改查 \hRobomongoRobo3TadminMongo66.nodejs連接操作數(shù)據(jù)庫\h連接數(shù)據(jù)庫創(chuàng)建模型增加數(shù)據(jù)constmongoose=require("mongoose")mongoose.connect("mongodb://:27017/company-system")constmongoose=require("mongoose")constSchema=mongoose.SchemaconstUserType={username:String,password:String,gender:Number,introduction:String,avatar:String,role:Number}constUserModel=mongoose.model("user",newSchema(UserType))module.exports=UserModelUserModelUserModel.create({introduction,username,gender,avatar,password,role})查詢數(shù)據(jù)UserModelUserModel.find({username:"kerwin"},["username","role","introduction","password"]).sort({createTime:-1}).skip(10).limit(10)更新數(shù)據(jù)UserModelUserModel.updateOne({_id},{introduction,username,gender,avatar})刪除數(shù)據(jù)UserModelUserModel.deleteOne({_id})四、接口規(guī)范與業(yè)務(wù)分層 \h1.接口規(guī)范 \h2.業(yè)務(wù)分層\h

2.業(yè)務(wù)分層五、登錄鑒權(quán) \h1.Cookie&Session \h「HTTP無狀態(tài)」我們知道,HTTP是無狀態(tài)的。也就是說,HTTP請求方和響應(yīng)方間無法維護(hù)狀態(tài),都是一次性的,它不知道前后的請求都發(fā)生了什么。但有的場景下,我們需要維護(hù)狀態(tài)。最典型的,一個用戶登陸微博,發(fā)布、關(guān)注、評論,都應(yīng)是在登錄后的用戶狀態(tài)下的?!笜?biāo)記」那解決辦法是什么呢?constconstexpress=require("express");constsession=require("express-session");constMongoStore=require("connect-mongo");constapp=express();app.use(session({secret:"thisissession",//服務(wù)器生成session的簽名resave:true,saveUninitialized:true,//強(qiáng)制將為初始化的session存儲cookie:{maxAge:1000*60*10,//過期時間secure:false,//為true時候表示只有https協(xié)議才能訪問cookie},rolling:true,//為true表示超時前刷新,cookie會重新計時;為false表示在超時前刷新多少次,都是按照第一次刷新開始計時。store:MongoStore.create({mongoUrl:'mongodb://:27017/kerwin_session',ttl:1000*60*10//過期時間}),}));app.use((req,res,next)=>{if(req.url==="/login"){next()return;}if(req.session.user){req.session.garbage=Date();next();}else{res.redirect("/login")}})2.JSONWebToken(JWT) \h(1)介紹 \h我為什么要保存這可惡的session呢,只讓每個客戶端去保存該多好?當(dāng)然,如果一個人的token被別人偷走了,那我也沒辦法,我也會認(rèn)為小偷就是合法用戶,這其實(shí)和一個人的sessionid被別人偷走是一樣的。這樣一來,我就不保存sessionid了,我只是生成token,然后驗(yàn)證token,我用我的CPU計算時間獲取了我的session存儲空間!解除了sessionid這個負(fù)擔(dān),可以說是無事一身輕,我的機(jī)器集群現(xiàn)在可以輕松地做水平擴(kuò)展,用戶訪問量增大,直接加機(jī)器就行。這種無狀態(tài)的感覺實(shí)在是太好了!缺點(diǎn):占帶寬,正常情況下要比session_id更大,需要消耗更多流量,擠占更多帶寬,假如你的網(wǎng)站每月有10萬次的瀏覽器,就意味著要多開銷幾十兆的流量。聽起來并不多,但日積月累也是不小一筆開銷。實(shí)際上,許多人會在JWT中存儲的信息會更多;無法在服務(wù)端注銷,那么久很難解決劫持問題;性能問題,JWT的賣點(diǎn)之一就是加密簽名,由于這個特性,接收方得以驗(yàn)證JWT是否有效且被信任。對于有著嚴(yán)格性能要求的Web應(yīng)用,這并不理想,尤其對于單線程環(huán)境。注意:CSRF攻擊的原因是瀏覽器會自動帶上cookie,而不會帶上token;以CSRF攻擊為例:cookie:用戶點(diǎn)擊了鏈接,cookie未失效,導(dǎo)致發(fā)起請求后后端以為是用戶正常操作,于是進(jìn)行扣款操作;token:用戶點(diǎn)擊鏈接,由于瀏覽器不會自動帶上token,所以即使發(fā)了請求,后端的token驗(yàn)證不會通過,所以不會進(jìn)行扣款操作;(2)實(shí)現(xiàn) \h//jsonwebtoken//jsonwebtoken封裝constjsonwebtoken=require("jsonwebtoken")constsecret="kerwin"constJWT={generate(value,exprires){returnjsonwebtoken.sign(value,secret,{expiresIn:exprires})},verify(token){try{returnjsonwebtoken.verify(token,secret)}catch(e){returnfalse}}}}module.exports=JWT//Addaresponseinterceptor//Addaresponseerceptors.response.use(function(response){const{authorization}=response.headersauthorization&&localStorage.setItem("token",authorization)returnresponse;},function(error){const{status}=error.responseif(status===401){localStorage.removeItem("token")window.location.href="/login"}returnPromise.reject(error);});六、文件上傳管理 \hMulterMulter是一個node.js中間件,用于處理multipart/form-data類型的表單數(shù)據(jù),它主要用于上傳文件。注意Multer:不會處理任何非multipart/form-data類型的表單數(shù)據(jù)。Multer會添加一個body對象以及file或files對象到express的request對象中。body對象包含表單的文本域信息,file或files對象包含對象表單上傳的文件信息。npminstall--savemulter//前后端分離-前端constparams=newFormData()params.append('kerwinfile',file.file)params.append('username',this.username)constconfig={headers{:"Content-Type":"multipart/form-data"}}http.post('/api/upload',params,config).then(res=>{this.imgpath='http://localhost:3000'+res.data})//前后端分離-后端router.post('/upload',upload.single('kerwinfile'),function(req,res,next){console.log(req.file)})七、APIDOC-API文檔生成工具 \hapidoc是一個簡單的RESTfulAPI文檔生成工具,它從代碼注釋中提取特定格式的內(nèi)容生成文檔。支持諸如Go、Java、C++、Rust等大部分開發(fā)語言,具體可使用apidoclang命令行查看所有的支持列表。apidoc擁有以下特點(diǎn):跨平臺,linux、windows、macOS等都支持;支持語言廣泛,即使是不支持,也很方便擴(kuò)展;支持多個不同語言的多個項目生成一份文檔;輸出模板可自定義;根據(jù)文檔生成mock數(shù)據(jù);\h\hnpminstall-gapidoc注意:(1)在當(dāng)前文件夾下apidoc.json{{"name":"****接口文檔","version":"1.0.0","description":"關(guān)于****的接口文檔描述","title":"****"}(2)可以利用vscodeapidocsnippets插件創(chuàng)建api八、Koa2 \h1.簡介 \hkoa是由Express原班人馬打造的,致力于成為一個更小、更富有表現(xiàn)力、更健壯的Web框架。使用koa編寫web應(yīng)用,通過組合不同的generator,可以免除重復(fù)繁瑣的回調(diào)函數(shù)嵌套,并極大地提升錯誤處理的效率。koa不在內(nèi)核方法中綁定任何中間件,它僅僅提供了一個輕量優(yōu)雅的函數(shù)庫,使得編寫Web應(yīng)用變得得心應(yīng)手。2.快速開始 \h2.1安裝koa2 \h##初始化package.jsonnpminit#安裝koa2npminstallkoa2.2hellowo

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論