這即將是一篇很長很長的文章...html
// server.js const http = require('http') const server = http.createServer(() => { console.log('原地轉300圈並listen3000端口') }) server.listen(3000)
首先在終端運行node server.js
使用瀏覽器在地址欄輸入localhost:3000
會發現控制檯打印以下:前端
可是此時會發現瀏覽器地址欄始終在加載中,而且什麼也沒有顯示,接下來讓它顯示一些東西出來。node
http.createServer()
的回調函數實際上是有參數的,能夠用來處理瀏覽器的請求,並進行響應。mysql
const server = http.createServer((req, res) => { // 處理請求和響應 console.log('原地轉300圈並listen3000端口') res.write('lalala') res.end() })
此時在終端運行node server.js
使用瀏覽器在地址欄輸入localhost:3000
會發現瀏覽器顯示以下:git
每一次修改都要使用ctrl+c
中止服務器後再用node server.js
重啓很麻煩吶...那就推薦個工具:使用npm install -g supervisor
安裝supervisor,而後運行supervisor server.js
就能夠實現代碼更改以後頁面的自動更新。
而且會發現不論地址欄如何修改,頁面內容都會是這樣的:github
是由於服務器並無對瀏覽器的請求作出響應。因此http.createServer()
回調的第一個參數就派上了用場:sql
const server = http.createServer((req, res) => { console.log('原地轉300圈並listen3000端口') console.log(req.url) res.write('lalala') res.end() })
此時控制檯輸出:chrome
這裏的/a/b/c.html
和/favicon.ico
都是瀏覽器在訪問http://localhost:3000/a/b/c.html
時請求的內容,/favicon.ico
是頁面標題左側的小圖標,也就是使用<link rel="shortcut icon" href="/favicon.ico">
設置的那個圖標。數據庫
因此就能夠根據請求的url來進行相應的處理~express
const server = http.createServer((req, res) => { console.log('原地轉300圈並listen3000端口') switch(req.url) { case '/1.html': res.write('111') break case '/2.html': res.write('222') break default: res.write('404') break } res.end() })
發現能夠響應請求了是否是很棒棒!
可是每次都使用switch...case...
確定不是咱們的本意啊,因此接下來確定會進行一些懶人操做!
此次是另外一個模塊的使用:fs
。
fs
模塊主要有兩個方法:fs.readFile()
和fs.writeFile()
、
fs.readFile('文件名', (err, data) => { if (err) { // err的處理 console.log(err) } else { // data的處理 console.log(data.toString()) } })
這裏之因此要使用data.toString()
是由於readFile
的data
是buffer
類型,這個buffer
能夠往後再說,當前知道使用toString()
能夠轉換成咱們以前輸入的內容就行了。
這裏若是發現出現了亂碼則須要把
aaa.txt
的文件格式改爲UTF-8,至於怎麼改能夠自行百度。
fs.writeFile('文件名', '內容', (err) => { console.log(err) })
學習了fs
的兩個基本函數以後就能夠來訪問靜態文件啦!
能夠在當前目錄下新建一個public
文件夾,在public
文件放一些靜態文件,好比圖片啊,html文件之類的。
// server.js const http = require('http') const fs = require('fs') const server = http.createServer((req, res) => { console.log('原地轉300圈並listen3000端口') const file_name = '/public' + req.url fs.readFile(file_name, (err, data) => { if (err) { res.write('404') } else { res.write(data) } res.end() }) }) server.listen(3000)
此處注意
res.end()
的位置,readFile是異步操做,須要在回調中進行下一步的服務響應。此處返回的data不須要進行toString操做,由於瀏覽器能夠識別buffer格式。
例如在public下放兩個html文件:
<!-- 1.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> div { width: 100px; height: 100px; background-color: #f3f3f3; border: #ccc 1px solid; } </style> </head> <body> <div>11111</div> </body> </html>
<!-- 2.html,別的內容都不變,只修改body下面的 --> <body> <div>22222</div> </body>
而後對相應的路徑進行訪問:
啦啦啦能夠直接訪問文件啦!
前端請求後端數據的經常使用的兩種方式:GET和POST。GET的數據是放在url中,POST的數據不在url中。因此對這兩種方式,後端須要不一樣的處理方式。
修改一下上節的1.html:
<!-- 別的內容都不變,只修改body下面的 --> <body> <div>11111</div> <form action="http://localhost:3000" method="get"> 用戶:<input type="text" name="user" value=""><br> 密碼:<input type="password" name="pass" value=""><br> <input type="submit" value="提交"> </form> </body>
對於GET方式,須要處理的是req.url
部分。因此能夠先嚐試輸出:
// server.js const server = http.createServer((req, res) => { console.log('原地轉300圈並listen3000端口') res.write(req.url) res.end() }) server.listen(3000)
點提交以後,來觀察一下頁面的變化:
emmmm...雖然說這確實太不安全了,可是就先學習一下思路嘛...
對於/?user=user&pass=123
,問號前面的部分是路徑/
,問號後面的就是添加在url後面的參數啦,因此能夠用最基本的split()
方法處理數據啊...可是要考慮那個/favicon.ico
,它沒有?
沒有&
,因此是要進行排雷的。
const server = http.createServer((req, res) => { console.log('原地轉300圈並listen3000端口') var get = {} var url = req.url if (url.indexOf('?') !== -1) { var arr = url.split('?') var arr1 = arr[1].split('&') for (let i = 0; i < arr1.length; i++) { var arr2 = arr1[i].split('=') get[arr2[0]] = arr2[1] } res.write(JSON.stringify(get)) } res.end() }) server.listen(3000)
res.write()
參數只能是string或buffer,因此不能直接res.write(get)
。
此時會看到已經成功的處理了req.url
:
可是不想這麼麻煩啊...因而還真的有簡單的模塊能夠用哦!
例如:
const queryString = require('querystring') var query = queryString.parse('user=user&pass=123') console.log(query) // { user: 'user', pass: '123' }
因此就可使用這個模塊來將處理方式變得簡單點~
// server.js const http = require('http') const queryString = require('querystring') const server = http.createServer((req, res) => { console.log('原地轉300圈並listen3000端口') var get = {} var url = req.url if (url.indexOf('?') !== -1) { var arr = url.split('?') get = queryString.parse(arr[1]) res.write(JSON.stringify(get)) } res.end() }) server.listen(3000)
可是這裏還須要split()
一次,因此呢,還有個更簡單的模塊~
const urlLib = require('url') var url = urlLib.parse('localhost:4000/?user=user&pass=123') console.log(url) // Url { // protocol: 'localhost:', // slashes: null, // auth: null, // host: '4000', // port: null, // hostname: '4000', // hash: null, // search: '?user=user&pass=123', // query: 'user=user&pass=123', // pathname: '/', // path: '/?user=user&pass=123', // href: 'localhost:4000/?user=user&pass=123' }
主要看的就是Url.query
,可是如今並無根據&
切開怎麼辦呢,這時須要給urlLib.parse()
設置它的第二個參數爲true
,說明要解析query,以下:
const urlLib = require('url') var url = urlLib.parse('localhost:4000/?user=user&pass=123', true) console.log(url.query)//{ user: 'user', pass: '123' }
// server.js const http = require('http') const urlLib = require('url') const server = http.createServer((req, res) => { console.log('原地轉300圈並listen3000端口') var obj = urlLib.parse(url, true) var get = obj.query var url = obj.pathname res.write(JSON.stringify(get)) res.end() }) server.listen(3000)
上面的一路看過來已經get到點了...因此來post一下。
先修改以前的1.html,將get方式改成post便可
<form action="http:localhost:3000" method="post"> 用戶:<input type="text" name="user" value=""><br> 密碼:<input type="password" name="pass" value=""><br> <input type="submit" value="提交"> </form>
由於post的數據是放在HTTP報文主體中的,數據獲取就是一個很大的問題,因此首先看一下node怎麼接收post數據。
node接收post數據主要是使用req
的事件處理:
req.on('data', (data) => {}) // 每次接收數據時觸發 req.on('end', () => {}) // 接收完畢數據時觸發
由於post數據能夠很大,因此對較大的數據包會進行分塊再處理。分塊能夠避免因爲種種錯誤而產生的大塊數據從新傳輸,會浪費資源。
const server = http.createServer((req, res) => { console.log('原地轉300圈並listen3000端口') var post = '' req.on('data', (data) => { post += data }) req.on('end', () => { res.write(JSON.stringify(post)) res.end() }) }) server.listen(3000)
能夠看到顯示的數據是這樣的;
之因此要監聽兩個事件是由於POST數據較大時數據也是分塊發送的,因此讀者朋友能夠試一下增長個<texearea>
而後多輸入一些數據,好比設置個遞增的變量來感覺一下data
事件的觸發狀況。
這裏使用字符串拼接的方式來處理數據確實有一些low了,好比傳輸的是文件格式那就不能在字符串裏面放了啊,可是先這樣,目的是瞭解POST傳輸的特色啦~
對上圖中的數據的處理方式還記得不?固然是牛逼哄哄的queryString
啦!對數據進行如下方式的處理,就會獲得json格式的數據:
const queryString = require('querystring') //... req.on('end', () => { var POST = queryString.parse(post) res.write(JSON.stringify(POST)) res.end() })
吶吶...來應用一下噻,好比說製做一個註冊登陸的界面。目前由於沒有應用到數據庫,就能夠直接使用存儲幾個帳號密碼的map來模擬一下~
具體代碼能夠看reg&login。
接下來是express的部分~
使用以前應該先安裝npm install express
。
而後server.js
內容以下:
// server.js const express = require('express') const server = express() server.listen(3000)
emmmm...這個時候使用supervisor server.js
而後打開localhost:3000
會發現出現了Cannot GET /
,意思就是沒什麼東西嘛...那接下來就讓他顯示一些東西出來。
在server.listen(3000)
前加這樣的代碼:
server.use('/', (req, res) => { res.send('這是首頁') res.end() })
此時就會發現首頁上有東西啦!
express中的req
和res
實際上是進行封裝過的,和原生node回調中的req
和res
兼容,這裏的res.send
徹底能夠改爲res.write
,只是res.write
的參數必須是buffer
或string
類型,而res.send
則沒有這個限制。
發現了嗎,express使用的server.use('/', callback)
代替了原來對req.url
的複雜處理,不用進行parse
再switch...case
了,方便了很多~
接下來看express怎麼處理用戶請求~
html文件仍是以前的1.html,先<form>
元素的method
設置爲get,隨後再設置爲post,來觀察提交後的狀況。server.js中更改以下:
const server = express() server.get('/', (req, res) => { res.send('get到了') res.end() }) server.post('/', (req, res) => { res.send('post到了') res.end() }) server.listen(3000)
而使用server.use
的時候則是不管哪一種請求都會觸發,你們能夠本身試試。
在express中,讀取靜態文件的方法是使用express.static設置一個放置靜態文件的目錄。
server.use(express.static('public'))
有了上面一句以後就能夠訪問public
目錄下的靜態頁面1.html
啦~以下:
在express中處理url中的參數也有簡單的方法:req.query
,若代碼以下:
server.get('/', (req, res) => { res.send(req.query) res.end() })
就能夠像下圖同樣:
因此具有了用express搞註冊登陸的基礎知識,能夠來一波實踐哦~
代碼能夠見2-reg-login
上文學習了express使用req.query
能夠獲取到GET方式的請求內容,此次來學習怎麼接收POST請求並處理數據。
這裏你們能夠先去看看express的中間件的概念,上文中出現的express.static()
其實就是一個託管靜態文件的中間件。
這裏就須要用到一個叫作body-parser
的中間件,可是這個中間件就不是express內置的了,使用前須要npm install body-parser
。
使用方法很簡單,仍是使用以前的1.html
,將form元素的method改成post:
<form action="http://localhost:3000/" method="post"> 用戶:<input type="text" name="user" value=""><br> 密碼:<input type="password" name="pass" value=""><br> <input type="submit" value="提交"> </form>
server.js
代碼以下:
const bodyParser = require('body-parser') server.use(bodyParser.urlencoded()) server.use('/', (req, res) => { res.send(req.body) // 使用了bodyParser以後body屬性上纔會有值 res.end() })
而後打開1.html
輸入數據後,結果以下:
因而會發現控制檯會有這樣一段提示:
這裏的extended若是值爲true則是開啓擴展模式,還有另外一個參數是limit表示限制大小,默認100k。可是通常用不到擴展模式,能夠直接設置爲false,以下:
server.use(bodyParser.urlencoded({ extended: false, limit: 2 * 1024 * 1024 // 限制2M }))
這個時候就會發現上圖中的提示沒有了。
server.use('/', (req, res, next) => { console.log('a') // next() }) server.use('/', (req, res) => { console.log('b') })
如上述代碼,當沒有next時控制檯只打印一個a,可是有了next以後,控制權能夠交給下一個server.use('/', () => {})
,因此能夠看到控制檯打印了a和b。
server.use
參數爲函數時,是對全部的請求都做用到。如上面的body-parser
。
server.use((req, res, next) => { req.on('data', ) }) server.use('/', (req, res, next) => { res.send(req.body) // 下方能夠獲取到 })
運行後如圖:
那麼就能夠模仿body-parser
來搞一箇中間件~
server.use((req, res, next) => { var str = '' req.on('data', (data) => { str += data }) req.on('end', () => { req.body = str next() // 注意這裏next的位置 }) }) server.use('/', (req, res, next) => { res.send(req.body) })
還用以前那個1.html,結果以下圖:
這時候想處理成json的話就能夠用以前的querystring的parse。
吶吶此次要了解的是cookie和session~
能夠在chrome瀏覽器F12 -> Application -> Cookies看到網頁的cookie,可使用dcument.cookie
獲取或修改當前頁面的cookie。
server.use('/', (req, res) => { res.cookie('user', 'oneday', { maxAge: 24 * 1000 * 3600 // 設置時間 }) res.send() })
此時查看瀏覽器控制檯就會發現有了這樣一個cookie:
不設置maxAge的話cookie會在瀏覽器關閉後失效。
也能夠在res.cookie()
中爲cookie設置路徑以下:
server.use('/', (req, res) => { res.cookie('user', 'oneday', { path: '/one', // 設置路徑 }) res.send() })
若是使用path屬性設置了/one
的話,那麼當訪問localhost:3000
時沒法讀取到cookie,只有在localhost:3000/one
下的路徑均可以訪問到該cookie,如localhost:3000/one/a.html
。
在express中讀取cookie則須要一箇中間件cookie-parser
。
const cookieParser = require('cookie-parser') server.use(cookieParser()) server.use('/', (req, res) => { res.cookie('user', 'oneday', { path: '/one', maxAge: 24 * 1000 * 3600 }) res.send(req.cookies) // 使用了cookie-parser中間件後纔有的cookies屬性 })
這時若是訪問localhost:3000
是不會看到有輸出的,緣由同上,可是/one
下的路徑就會有,以下圖:
server.use(cookieParser()) server.use('/', (req, res) => { req.secret = '願全部的愛和付出都不會被辜負' // 簽名密鑰 res.cookie('user', 'oneday', { maxAge: 24 * 1000 * 3600, signed: true }) res.send(req.cookies) })
能夠看到以下圖的輸出:
能夠看到該簽了名的cookie很明顯的將原始的cookie包含在了內容中,emmmm內容仍是能夠看到的。可是一旦cookie被修改了就能看得出來啊,因此簽名只是能作到防篡改,不能作到直接加密。
簽過名的cookie以s
開頭,簽名後的cookie長度較長,所以經cookie-parser
處理過的帶簽名的cookie會放在req.signedCookies
中,未簽過名的就放在req.cookies
中。
// 告訴cookieParser加密使用的字符串 server.use(cookieParser('願全部的愛和付出都不會被辜負')) server.use('/', (req, res) => { req.secret = '願全部的愛和付出都不會被辜負' // 簽名密鑰 res.cookie('user', 'oneday', { maxAge: 24 * 1000 * 3600, signed: true }) res.send(req.signedCookies) // 就能夠將簽過名的cookie原樣輸出 })
刪除cookie則是使用res.clearCookie(name)
就能夠了。
想處理session則須要一個叫cookie-session的中間件。可是要記得在使用cookie-session中間件以前先使用cookie-parser中間件。由於不處理cookie哪來的session呢~
將server.js
代碼改爲以下所示:
const cookieParser = require('cookie-parser') const cookieSession = require('cookie-session') server.use(cookieParser()) server.use(cookieSession()) server.use('/', (req, res) => { res.send(req.session) })
會發現報錯了~~
吶吶來介紹一下這個keys。它的存在是爲了預防session劫持的發生,keys是一個密鑰數組,能夠用來加密,express會循環使用密鑰來加密cookie。
server.use(cookieSession({ keys: ['one', 'day', 'oneday'] // keys加密數組,注意使用方法 })) server.use('/', (req, res) => { if (req.session['count'] == null) { req.session['count'] = 1 } else { req.session['count']++ } console.log(req.session['count']) res.send('OK') })
會發現每次控制檯的session都會刷出來兩個數字,那是由於也訪問到了/favicon.ico
,這個能夠暫時不用管。
打開瀏覽器後能夠看到cookie的地方有兩個:
其中的session
表明的是訪問次數,session.sig
表明的是簽名後的session
,刷新會發現session
變化不大可是session.sig
則會發生很大的變化,這樣就能夠防止別人拿着上次的session假冒用戶進行操做了。
session也能夠進行一些自行的設置~
server.use(cookieSession({ name: 'ses', keys: ['one', 'day', 'oneday'], maxAge: 2 * 3600 * 1000 // 2小時 }))
服務器端使用delete req.session[name]
刪除session。
ejs相對來講是溫和性的,能夠和html共存的一個模板引擎,我的比較喜歡ejs,因此這裏不介紹jade的相關知識...你們能夠本身找教程~
首先固然是要使用npm install ejs
下載一下~
<!-- index.ejs --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> 個人名字叫:<%= name %> </body> </html>
// ciews下ejs.js const ejs = require('ejs') ejs.renderFile("./views/index.ejs", {name: "oneday"}, (err, data) => { if(err) { console.log("編譯失敗") } else { console.log(data) } })
使用node ejs.js
以後,能夠獲得控制檯的以下輸出:
<%= name %>
<% for (var i = 0; i < 3; i++) { %> <div><%= arr[i].name %></div> <% } %>
<%- <div></div> %>
<% include filepath+filename %>
,這裏的filepath+filename
不能是變量。emmmm能夠看express的文檔模板引擎。主要就是兩句代碼:
server.set('views', './views') server.set('view engine', 'ejs') server.get('/index', (req, res) => { res.render('index.ejs', {name: 'oneday'}) })
這節主要講怎麼用express實現一個文件上傳功能。
將1.html內容進行更改:
<form action="http://localhost:3000/" method="post"> 文件:<input type="file" name="f1"><br> <input type="submit" value="上傳"> </form>
const express = require('express') const bodyParser = require('body-parser') const server = express() server.use(bodyParser.urlencoded({extended: false})) server.post('/', (req, res) => { res.send(req.body) }) server.listen(3000)
此時運行一下試試會發現只打印出來了圖片的名稱,意思是隻上傳了文件名。緣由是body-parser
只能處理表單中enctype爲application/x-www-form-urlencoded
的項,上傳文件須要將enctype改成multipart/form-data
,且不能用body-parser
處理了。這時就須要引入一個新的中間件:multer
。
npm install multer
以後,能夠按以下操做:
<form action="http://localhost:3000/" method="post" enctype="multipart/form-data"> 文件:<input type="file" name="f1"><br> <input type="submit" value="上傳"> </form>
const multer = require('multer') var objmulter = multer() const server = express() server.use(objmulter.any()) // 能夠是single(指定的名稱) server.post('/', (req, res) => { res.send(req.files) })
這時再運行就會發現有不少buffers數據出來了...可是隻要這樣的數據確定是沒有什麼用處的啊,畢竟咱們要的是上傳的文件嘛...那就能夠像下面這樣對數據進行一下處理:
var objmulter = multer({dest: './www/upload'}) // 只改這一行代碼,dest指定目標文件夾
這是再運行會發現www/upload下面真的會有一個文件,可是是一個很長名字且沒有後綴,也就是沒有文件類型的一個文件,接下來咱們要作的就是給它加一個文件擴展名,要用到一個叫path
的包。
const path = require('path') var str = "c:\\www\\aaa\\nbb.png" var obj = path.parse(str) console.log(obj)
會發現輸出以下:
因此經過如下代碼能夠完成一個文件上傳的小應用~
const multer = require('multer') const pathLib = require('path') const fs = require('fs') var objmulter = multer({dest: './www/upload'}) const server = express() server.use(objmulter.any()) server.post('/', (req, res) => { var newName = req.files[0].path + pathLib.parse(req.files[0].originalname).ext // 重命名臨時文件 fs.rename(req.files[0].path, newName, (err) => { // fs.rename if (err) { res.send('上傳失敗') } else { res.send('上傳成功') } }) })
選擇文件並上傳後,會發如今www/upload下是真的有該文件的,並且能夠正常打開~
這裏用到的數據庫是mysql,管理工具是Navicat Premium。在node中須要首先npm install mysql
,隨後先進行數據庫鏈接:
const mysql = require('mysql') const db = mysql.createConnection({ host: 'localhost', // 目標 user: 'root', // 用戶名 port: 3306, // 端口號 password: '123456', // 密碼 database: 'user_table' // 數據庫名 })
數據庫操做語法統一格式爲:db.query('操做語句', (err, data) => {// callback})
SQL語句標準寫法要求:一、關鍵字要求大寫;二、庫名、表名、字段名須要加上``
記錄一些簡單操做語句:
-- INSERT INTO 表(字段列表) VALUES(值列表) INSERT INTO `user_table` (`username`, `password`) VALUES ('one', '123456')
-- DELETE FROM 表名 (WHERE 條件) DELETE FROM `user_table` WHERE `username`='one'
-- UPDATE 表名 SET 字段=值, 字段=值, 字段=值...(WHERE 條件) UPDATE `user_table` SET `username`='oneday', `password`='233333' WHERE `username`='one'
-- SELELT (內容) FROM 表名 (WHERE 條件) SELECT * FROM `user_table`
WHERE `age` >= 18 WHERE `age` >= 18 AND `score` < 60 WHERE `cash` > 100 OR `score` > 1000
-- ASC -> 升序 | DESC -> 降序 ORDER BY `age` ASC/DESC -- 按價格升序,隨後再按銷量降序,多條件排序就用逗號分隔 ORDER BY `price` ASC, `sales` DESC
-- 按班級計數 SELECT `class`, COUNT(class) FROM `student_table` GROUP BY `class` -- 每一個班級的平均分 SELECT `class`, AVG(score) FROM `student_table` GROUP BY `class` -- 每一個班級的最高分和最低分 SELECT `class`, MAX(score), MIN(score) FROM `student_table` GROUP BY `class`