相關代碼javascript
cookie , cache-control(cache是緩存的意思,control是控制的意思,連起來就是緩存控制)是屬於響應頭裏面的,請求頭裏面是沒有的,固然cooke是請求頭和響應頭都有的html
注意:前端
1.服務器端接收請求,其中80端口是接收http請求,443端口是接收https請求java
2.請求和響應都是由4部分組成,其中第三部分都是回車node
3.常見的httpq請求的頭部Content-Type i.application/x-www-form-urlencoded ii.multipart/form-data iii.application/json iv.application/xmlgit
4.content-type:是指請求的內容有多長,xxxkbgithub
5.請求的第四部分,通常是以xxx&&yyy&&zzz的形式展現.響應的第四部分通常是ajax
用JS發請求:數據庫
// 1.聲明一個request對象
var request = new XMLHttpRequest()
// 2.打開鏈接
request.open('POST','/xxx')
// 3.監聽狀態的變化
request.onreadystatechange = function () {
if (request.readyState == 4 && request.status == 200) { // 請求下載成功而且請求成功
// 該作什麼作什麼
}
};
// 4.發送請求
request.send('a=1&b=2') // send裏面是請求的第四部分,固然GET是沒有第四部分的
複製代碼
nodejs接收請求:json
var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]
if (!port) {
console.log('請指定端口號好不啦?\nnode server.js 8888 這樣不會嗎?')
process.exit(1)
}
var server = http.createServer(function (request, response) {
var parsedUrl = url.parse(request.url, true)
var pathWithQuery = request.url
var queryString = ''
if (pathWithQuery.indexOf('?') >= 0) { queryString = pathWithQuery.substring(pathWithQuery.indexOf('?')) }
var path = parsedUrl.pathname
var query = parsedUrl.query
var method = request.method
/******** 從這裏開始看,上面不要看 ************/
console.log('方方說:含查詢字符串的路徑\n' + pathWithQuery)
if (path === '/') {
let string = fs.readFileSync('./index.html', 'utf-8')
response.statusCode = 200
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
} else if(path === '/sign-up'){ // sign-up的路由也就是註冊
let string = fs.readFileSync('./sign-up.html', 'utf-8')
response.statusCode = 200
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if (path === '/main.js') {
let string = fs.readFileSync('./main.js', 'utf-8')
response.statusCode = 200
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
response.write(string)
response.end()
} else if (path === '/xxx') {
response.statusCode = 200
response.setHeader('Content-Type', 'text/json;charset=utf-8')
response.setHeader('Access-Control-Allow-Origin', ' http://tom.com:8001')
response.write(`
{
"note": {
"to": "小谷",
"from": "Reagen",
"heading": "打招呼",
"content": "你好JSON"
}
}
`)
response.end()
} else if (path === '/index') {
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('<!Doctype>\n<html><head></head><body><h1>Hello 這是個人第一個服務器響應頁面!</h1></body></html>')
response.end()
} else {
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(`{
"錯誤": "中文寫的響應的第四部分,你的響應跑到火星去了"
}`)
response.end()
}
/******** 代碼結束,下面不要看 ************/
})
server.listen(port)
console.log('監聽 ' + port + ' 成功\n請用在空中轉體720度而後用電飯煲打開 http://localhost:' + port)
複製代碼
什麼是路由?server.js裏面的if...else...就是路由,如例子中的註冊路由就是
else if(path === '/sign-up'){
// 省略
}
複製代碼
(一) 用ajax發POST請求
首先看一下main.js裏的$.post('/sign_up',hash)
獲得的是404 ?仍是一個字符串?
// html
<div class="form-wrapper">
<form id="signupForm">
<h1>註冊</h1>
<div class="row">
<label>郵箱</label>
<input type="text" name="email">
</div>
<div class="row">
<label>密碼</label>
<input type="password" name="password">
</div>
<div class="row">
<label>確認密碼</label>
<input type="password" name="password_confirmation">
</div>
<div class="row">
<input type="submit" value="註冊">
</div>
</form>
</div>
// main.js
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash) // 404? 仍是 一串html? POST??? GET???
.then(()=>{
console.log('success')
},()=>{
console.log('fail')
})
})
// server.js
......
else if(path === '/sign-up'){ // sign-up的路由也就是註冊
let string = fs.readFileSync('./sign-up.html', 'utf-8')
response.statusCode = 200
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}
......
複製代碼
答案是一個字符串
由於server.js的關於註冊的路由並無限制是POST請求仍是GET請求,這裏$.post('/sign_up',hash)
只寫了路徑是/sign_up,不論是POST仍是GST仍是DELEATE都會獲得響應,響應的內容爲fs.readFileSync('./sign-up.html', 'utf-8')
ajax響應獲得的是一個字符串ajax接下來應該怎麼辦?
咱們不防打印出這個響應:
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash)
.then((response)=>{
console.log(response) // 打印出這個response
},()=>{
console.log('fail')
})
})
複製代碼
咱們總會以爲JSON和html不同,其實沒什麼不同,對於http來講都只是一串字符串而已,ajax返回的是符合JSON格式的字符串,server.js響應的是符合JS語法的字符串
若是咱們不想獲得這個字符串怎麼辦?給GET和POST單獨作一個路由唄!
若是咱們把server.js裏面的路由限制爲GET
else if(path === '/sign_up'&& method ==="GET"){
let string = fs.readFileSync('./sign-up.html', 'utf-8')
response.statusCode = 200
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}
複製代碼
那麼咱們再發請求就會報404錯,由於咱們實際發起的是POST請求,服務器並無針對POST作路由,不知足 method ==="GET",一旦不能知足server.js裏就會走到最後一個else的邏輯
else {
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(`{
"錯誤": "中文寫的響應的第四部分,你的響應跑到火星去了"
}`)
response.end()
複製代碼
單獨爲POST寫一個路由
+++
else if(path === '/sign_up'&& method ==="POST"){
response.statusCode = 200
response.end()
}
+++
複製代碼
這時候發起POST請求,獲得的響應就是響應空字符串狀態碼爲200
(二) 讀用戶輸入的郵箱和密碼
怎麼讀到form data裏用戶輸入的郵箱和密碼呢?
nodejs是讀不到的,由於form data是一段一段的上傳的,因此讀的時候就可能只能讀到一段,Google: node http get post data
解決辦法:
let body = []; // 請求體
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
});
複製代碼
註釋: 首先聲明一盒叫body的變量做爲咱們的請求體,request監聽它的data事件,每一次返回一小塊數據,而後每次把這一小塊數據放到body數組裏面,當它end的時候也就是數組都上傳完的時候body就能夠把裏面的用戶信息弄出來
+++
else if (path === '/sign_up' && method === "POST") {
let body = []; // 請求體
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
console.log(body) // 注意這裏的log不是在瀏覽器打印的而是在服務器端打印的
response.statusCode = 200
response.end()
});
}
+++
複製代碼
能夠看出打印出了form data裏的所有信息,這樣就能夠從body拿到用戶POST請求的第四部分,爲何第四部分不是一會兒上傳的而是一個一個的上傳的呢?萬一用戶上傳了一個一萬個字符長度的用戶名,不可能一會兒就能上傳完,因此只能一點一點上傳,上傳的過程當中就觸發了data事件
或者咱們能夠將讀請求體封裝成函數
+++
else if (path === '/sign_up' && method === "POST") {
readBody(request).then((body) => {
console.log(body)
response.statusCode = 200
response.end()
})
}
+++
function readBody(request) {
return new Promise((resolve, reject) => {
let body = []; // 請求體
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
resolve(body)
})
})
}
複製代碼
這樣用戶發請求到sign_up,服務端就會讀取它的第四部分body,而後從第四部分獲得字符串,字符串的格式爲:'email=1&password=2&password_confirmation=3'
那麼服務端如何從字符串中獲得這個email呢?
else if (path === '/sign_up' && method === "POST") {
readBody(request).then((body) => {
let strings = body.split('&') //['email=1','password=2','password_confirmation=3']
let hash = {}
strings.forEach((string)=>{ // 'email=1' 'password=2' 'password_confirmation=3'
let parts = string.split('=') // ['email': '1'] ['password': '2'] ['password_confirmation': '3']
let key = parts[0]
let value = parts[1]
hash[key] = value // hash['email'] = 1 hash['password'] = 2 hash['password_confirmation'] = 3
})
console.log(hash) //{ email: '1', password: '2', password_confirmation: '3' }
response.statusCode = 200
response.end()
})
}
複製代碼
服務器端獲得的hash爲:
回顧一下整個過程:
客戶端千辛萬苦的拿到用戶輸入的信息,而且將其變成字符串,字符串格式爲'email=1&password=2&password_confirmation=3'
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash) // 發送請求
.then((response)=>{
console.log(response)
},()=>{
console.log('fail')
})
})
複製代碼
服務端千辛萬苦的將字符串轉化爲一個hash對象,格式爲{ email: '1', password: '2', password_confirmation: '3' }
else if (path === '/sign_up' && method === "POST") {
readBody(request).then((body) => {
let strings = body.split('&') // ['email=1','password=2','password_confirmation=3']
let hash = {}
strings.forEach((string)=>{ // 'email=1' 'password=2' 'password_confirmation=3'
let parts = string.split('=') // ['email': '1'] ['password': '2'] ['password_confirmation': '3']
let key = parts[0]
let value = parts[1]
hash[key] = value // hash['email'] = 1 hash['password'] = 2 hash['password_confirmation'] = 3
})
response.statusCode = 200
response.end()
})
}
function readBody(request) {
return new Promise((resolve, reject) => {
let body = []; // 請求體
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
resolve(body)
})
})
}
複製代碼
以上就是前端向後盾傳數據的過程,前端將數據信息變成字符串,後端從這個字符串裏按照必定的結構(通常爲hash)解析出來
一個問題: 後端是如何拿到前端發起的POST請求的第四部分的? 雖然沒有學過nodejs可是應該能夠推測出是:
var server = http.createServer(function (request, response) {
// 此處省略具體內容
}
複製代碼
接下來就是去註冊驗證:
js寫代碼,除了if...else和forEach等循環其餘也就沒什麼了,其餘都是數據結構,要麼來個hash或數組,將拿到的信息放進有結構的hash或數組裏,或者聲明變量保存信息,而後再去操做這些變量,好像js作的就是從頁面獲取數據,而後存儲數據,加工數據,拿出數據,像極了加工廠將原產品加工成商品
else if (path === '/sign_up' && method === "POST") {
readBody(request).then((body) => {
let strings = body.split('&') // ['email=1','password=2','password_confirmation=3']
let hash = {}
strings.forEach((string)=>{ // 'email=1' 'password=2' 'password_confirmation=3'
let parts = string.split('=') // ['email': '1'] ['password': '2'] ['password_confirmation': '3']
let key = parts[0]
let value = parts[1]
hash[key] = value // hash['email'] = 1 hash['password'] = 2 hash['password_confirmation'] = 3
})
let {email,password,password_confirmation} = hash
if(email.indexOf('@') === -1){ // 註冊驗證
response.statusCode = 400
response.write('email is bad')
} else if(password !== password_confirmation){
response.statusCode = 400
response.write('password not match')
}else {
response.statusCode = 200
}
response.end()
})
}
複製代碼
咱們如何給用戶一個提示,錯在哪裏,怎樣將後端處理註冊錯誤的提示展現給用戶呢?
經過在main.js裏的回調函數的參數打log知道第一個參數a就是咱們想要的
// main.js
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash) // 404? 仍是 一串html?
.then((response)=>{
console.log(response)
},(a,b,c)=>{
console.log(a)
console.log(b)
console.log(c)
})
})
複製代碼
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash)
.then((response)=>{
alert(response) // 若是郵箱格式錯誤,會提示email is bad
},(request)=>{
alert(request.responseText)
})
})
複製代碼
若是郵箱格式錯誤,會提示email is bad,可是這個提示用戶可能會不理解,可是若是後端也不想去改怎麼辦?只能用一個比較
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash)
.then((response)=>{
alert(response)
},(request)=>{
if(request.responseText === 'email is bad'){
alert('郵箱格式錯誤') // 若是郵箱格式錯誤,會提示郵箱格式錯誤
}
})
})
複製代碼
再回顧一下這個過程: 咱們把用戶輸入的信息保存到hash,而後將hash發給/sign_up,後端解析sign_up的這個hash發現email的格式寫錯了,因而響應一個400而且響應的第四部分的內容爲'email is bad'.前端拿到響應的第四部分,而後作出處理並提示用戶'郵箱格式錯誤'.代碼就涉及到forEach, if..else, Promise以及結構相關的hash和數組等
這時候咱們實現了基本的先後端分離,先後端只使用http和ajax進行先後端的交流
若是前端對後端提需求,後盾不答應怎麼辦?這時候就須要定協議,也就是怎麼劃分是前端的鍋仍是後端的鍋,也就是說若是郵箱錯了後端不要返回一個字符串'email is bad'而是返回一個固定格式的協議,返回一個符合JSON對象語法的字符串
// server.js
+++
else if (path === '/sign_up' && method === "POST") {
readBody(request).then((body) => {
let strings = body.split('&') // ['email=1','password=2','password_confirmation=3']
let hash = {}
strings.forEach((string)=>{ // 'email=1' 'password=2' 'password_confirmation=3'
let parts = string.split('=') // ['email': '1'] ['password': '2'] ['password_confirmation': '3']
let key = parts[0]
let value = parts[1]
hash[key] = value // hash['email'] = 1 hash['password'] = 2 hash['password_confirmation'] = 3
})
let {email,password,password_confirmation} = hash
if(email.indexOf('@') === -1){
response.statusCode = 400
response.write(`{
"errors":{
"email": "invalid"
}
}`)
}
+++
// main.js
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash)
.then((response)=>{
alert(response)
},(request)=>{
console.log(request.responseText) // 結果是對象仍是字符串???
})
})
複製代碼
答案是返回的是符合JSON對象語法的字符串(由於它是一個response,無論它是什麼語法,結果都是字符串)
如何將字符串變成對象呢?有一個APIJSON.parse(request.responseText)
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash)
.then((response)=>{
alert(response)
},(request)=>{
let object = JSON.parse(request.responseText) // JSON.parse
console.log(object)
})
})
複製代碼
// ES5的寫法
let errors = object.errors
// ES6的寫法
let {errors} = object
複製代碼
既然:
let object = JSON.parse(request.responseText)
let {errors} = object
複製代碼
爲何不:
let {errors} = JSON.parse(request.responseText)
// 等價於 let errors = JSON.parse(request.responseText).errors
複製代碼
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash) // 404? 仍是 一串html?
.then((response)=>{
alert(response)
},(request)=>{
let {errors} = JSON.parse(request.responseText)
console.log(errors)
})
})
複製代碼
還有一種方法就是不用JSON.parse,只須要在後端中讓後端對響應的文件類型告知爲json
// server.js
+++
else if (path === '/sign_up' && method === "POST") {
readBody(request).then((body) => {
let strings = body.split('&') // ['email=1','password=2','password_confirmation=3']
let hash = {}
strings.forEach((string)=>{ // 'email=1' 'password=2' 'password_confirmation=3'
let parts = string.split('=') // ['email': '1'] ['password': '2'] ['password_confirmation': '3']
let key = parts[0]
let value = parts[1]
hash[key] = value // hash['email'] = 1 hash['password'] = 2 hash['password_confirmation'] = 3
})
let {email,password,password_confirmation} = hash
if(email.indexOf('@') === -1){
response.statusCode = 400
response.setHeader('content-type','application/json;charset=utf-8') // 告知響應文件格式
response.write(`{
"errors":{
"email": "invalid"
}
}`)
}
// main/.js
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash) // 404? 仍是 一串html?
.then((response)=>{
alert(response)
},(request)=>{
// let {errors} = JSON.parse(request.responseText)
let {errors} = request.responseJSON // jQ提供的API
console.log(errors)
})
})
複製代碼
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash) // 404? 仍是 一串html?
.then((response)=>{
alert(response)
},(request)=>{
// let {errors} = JSON.parse(request.responseText)
let {errors} = request.responseJSON
if(errors.email&&errors.email === 'invalid'){
alert('你的郵箱寫錯了')
}
})
})
複製代碼
在優化,錯誤的提示能不能不這麼暴力,能不能友好一點?
let hash = {}
$('#signupForm').on('submit',(e)=>{
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $('#signupForm').find(`[name = ${name}]`).val()
hash[name] = value
})
$.post('/sign_up',hash) // 404? 仍是 一串html?
.then((response)=>{
alert(response)
},(request)=>{
// let {errors} = JSON.parse(request.responseText)
let {errors} = request.responseJSON
if(errors.email&&errors.email === 'invalid'){ // 在輸入框
$('#signupForm').find('[name = "email"]').siblings('.error').text('郵箱格式錯誤')
}
})
})
複製代碼
怎樣處理註冊是不填郵箱?若是不填郵箱就註冊,那麼就不向服務器發送請求也就是在post以前去處理錯誤
let $form = $('#signupForm')
$form.on('submit',(e)=>{
let hash = {}
e.preventDefault()
let need = ['email','password','password_confirmation']
need.forEach((name)=>{
let value = $form.find(`[name = ${name}]`).val()
hash[name] = value
})
// 一開始就將span裏的error致空
$form.find('.error').each((index,span)=>{
$(span).text('')
})
// 註冊驗證
if(hash['email'] === ''){
$form.find('[name = "email"]').siblings('.error').text('填郵箱啊同窗')
return
}
if(hash['password'] === ''){
$form.find('[name = "password"]').siblings('.error').text('填密碼啊同窗')
return
}
if(hash['password_confirmation'] === ''){
$form.find('[name = "password_confirmation"]').siblings('.error').text('確認密碼啊同窗')
return
}
if(hash['password'] !== hash['password_confirmation']){
$form.find('[name = "password_confirmation"]').siblings('.error').text('先後密碼不一致啊同窗')
return
}
$.post('/sign_up',hash)
.then((response)=>{
alert(response)
},(request)=>{
// let {errors} = JSON.parse(request.responseText)
let {errors} = request.responseJSON
if(errors.email&&errors.email === 'invalid'){
$form.find('[name = "email"]').siblings('.error').text('郵箱格式錯誤')
}
})
})
複製代碼
發現一個問題若是已經填完郵箱可是以前沒填郵箱的錯誤還在怎麼辦呢?
只能在在提示error以前將全部的error至空
註冊驗證前端也能夠驗證,可是前端能夠不驗證,可是後端必定要驗證,由於有些人可能會經過curl發請求,繞開了js直接和後端進行交流,也能拿到服務器返回的結果,因此不能依賴瀏覽器上的JS,黑客能夠很簡單的就跨過瀏覽器上的JS,咱們應該確保後端是安全的前端安不安全無所謂
// 註冊的後端路由
+++
else if (path === '/sign_up' && method === "POST") {
readBody(request).then((body) => {
let strings = body.split('&') // ['email=1','password=2','password_confirmation=3']
let hash = {}
strings.forEach((string) => { // 'email=1' 'password=2' 'password_confirmation=3'
let parts = string.split('=') // ['email': '1'] ['password': '2'] ['password_confirmation': '3']
let key = parts[0]
let value = parts[1]
hash[key] = decodeURIComponent(value) // hash['email'] = 1 hash['password'] = 2 hash['password_confirmation'] = 3
})
let { email, password, password_confirmation } = hash
if (email.indexOf('@') === -1) {
response.statusCode = 400
response.setHeader('content-type', 'application/json;charset=utf-8')
response.write(`{
"errors":{
"email": "invalid"
}
}`)
} else if (password !== password_confirmation) {
response.statusCode = 400
response.write('password not match')
} else {
var users = fs.readFileSync('./db/users', 'utf8') // 讀users文件,它是一個符合JSON語法的字符串
try {
users = JSON.parse(users) // 將字符串轉化成對象 目前是[]
} catch (exception) {
users = [] // 意思就是若是往數據庫存的數據有問題就清空數據庫
}
let inUse = false
for (let i = 0; i < users.length; i++) {
let user = users[i]
if (user.email === email) {
inUse = true
break;
}
}
if (inUse) {
response.statusCode = 400
response.write('email in use')
} else {
users.push({ "email": email, "password": password })
var userString = JSON.stringify(users) // 往[]裏push數據,此時users是對象不能直接存,得轉成字符串
fs.writeFileSync('./db/users', userString) // push完以後要存
response.statusCode = 200
}
}
response.end()
})
+++
複製代碼
若是用戶輸入的註冊信息都是對的,接下來咱們應該作什麼?
首先應該從服務器開始,若是它發現用戶輸入的信息都是對的,服務器就應該把用戶的信息存下來(固然密碼通常是不存的),先作一個存密碼的版本?但是咱們怎麼存數據呢?用數據啊!哪來的數據庫?數據庫就是個文件而已,搞個文件叫db/users.json(沒有後綴也行,只要知道它是一個json就好了),咱們把這個叫users的文件當作咱們的數據庫,首先初始化一下它,它是一個符合JSON語法的空對象/數組,JSON是支持數組的不要覺得只支持對象
一個bug: 當咱們去註冊的時候,明明郵箱的格式帶@,爲何log出來的倒是沒有@的,由於http的做者李爵士在發明URL的時候說了URL裏面不能有@符號,若是有須要用40%來代替,也就是放咱們拿到用戶輸入的信息的時候,應該處理一下
hash[key] = value
=> hash[key] = decodeURIComponent(value)
數據庫裏只能存字符串不能存對象,對象是內存裏的東西,只能轉成字符串
var userString = JSON.stringify(users)
fs.writeFileSync('./db/users.json',userString)
複製代碼
可是咱們仍然發現會報錯,由於若是咱們以前把錯誤的對象存到了數據庫裏,也就是忘了將對象轉成字符串var userString = JSON.stringify(users)
,直接把對象存到數據庫了.這時候須要try..catch一下users,由於users有可能不符合JSON語法的,好比users是[object object],那麼 JSON.parse(users)就會報錯,try...catch就是一旦捕獲這個異常就將數據庫./db/users裏面數據清空
var users = fs.readFileSync('./db/users','utf8')
try {
users = JSON.parse(users) // 將字符串轉化成對象 目前是[]
} catch (exception) {
users = [] // 意思就是若是往數據庫存的數據有問題就清空數據庫
}
複製代碼
因爲存數據的時候只能存字符串,因此咱們得將對象轉成字符串JSON.stringify()
操做數據,是操做內存裏的數據,因此咱們得將字符串轉成對象,JSON.parse()
發現有多了一個bug,同一個郵箱若是註冊了2次,那麼數據庫中會存重複的2次,應該若是同一個email註冊了,那麼下一次就不容許它再註冊了,怎麼作呢?先不急着push,再push以前循環一下users而後作一下判斷 拿數據庫裏的內容和用戶發請求的內容相比,若是數據庫中已經存在了,就不不容許用戶再次註冊,響應404,而且響應的第四部分的內容爲'email in use'
理論上數據庫是不該該存用戶的敏感信息的好比用戶的登陸密碼,應該對密碼進行加密,下一次登陸的時候就對這個登陸的密碼進行加密而後和數據庫中加密的密碼進行對好比果同樣就能夠登陸成功
當用戶註冊成功後就應該容許用戶進行登陸,接下來就要作一個登陸頁面的路由
登陸就是拿到用戶輸入的用戶名和密碼去和數據庫裏的數據進行比較,若是沒有就登陸失敗.若是有就登陸成功,登陸的代碼和註冊仍是很類似的
存在的第一個問題,若是沒有登陸咱們依然能夠訪問首頁,那麼爲何還要登陸呢?怎樣讓必須登陸才能訪問首頁?第二個問題:登陸以後能夠把用戶的用戶名展現出來?
引入主題:須要cookie
何時須要設置Cookie呢?就是用戶在登陸成功的一瞬間
當用戶登陸成功後加上cookie,就會發現一個特殊的響應頭有set-Cookie,它有什麼用呢?這個會在後面起到很大的做用
// 登陸頁面的路由
+++
else if (path === '/sign_in' && method === 'POST') {
readBody(request).then((body) => {
let strings = body.split('&')
let hash = {}
strings.forEach((string) => {
let parts = string.split('=')
let key = parts[0]
let value = parts[1]
hash[key] = decodeURIComponent(value)
})
let {email, password} = hash
var users = fs.readFileSync('./db/users', 'utf8')
try {
users = JSON.parse(users)
} catch (exception) {
users = []
}
let found
for(let i = 0;i<users.length;i++){
if(users[i].email === email && users[i].password === password){
found = true
break
}
}
if(found){
// Set-Cookie: <cookie-name>=<cookie-value>
response.setHeader('set-Cookie',`sign_in_email = ${email}`)
response.statusCode = 200
}else{
response.statusCode = 401
}
response.end()
})
}
+++
複製代碼
查看首頁的請求頭會發現,請求頭裏面也多了一個cookie,也就是說若是以前給了一個set-Cookie頭(登陸頁面的sign_in)那麼今後以後因此的請求只要是相同的源(也就是相同的域名)請把那個cookie戴上,首頁(localhost)的cookie就是sign_in登陸頁面成功後響應頭設置的setCookie傳進來的,cookie就比如是景點的沒票,若是每次都想進這個景點,請帶上這個票
再看一下上面的代碼,當用戶登陸成功後,就給用戶setCookie(至關於給了他一張門票)下一次就不要再重複的登陸了(門票上保存了你的我的信息),相同的域名就至關於整個景點,這個域名下的字頁面就至關於小景點,無論訪問哪一個景點請帶上票來訪問,對於瀏覽器來上,只要是相同域名下的子頁面不論是哪一個頁面都是沒有區別的,無論你訪問哪一個頁面(訪問景點的哪一個子景點),就算是看景點的一個樹葉你也須要把票帶上
Cookie 的特色
1.服務器經過 Set-Cookie 響應頭設置 Cookie 2.瀏覽器獲得 Cookie 以後,每次請求都要帶上 Cookie 3.服務器讀取 Cookie 就知道登陸用戶的信息(email)
問題
1.我在 Chrome 登陸了獲得 Cookie,用 Safari 訪問,Safari 會帶上 Cookie 嗎 no,cookie會跟着瀏覽器走的 2.Cookie 存在哪 Windows 存在 C 盤的一個文件裏
3.Cookie會被用戶篡改嗎?
能夠
好比把登陸頁面的cookie改了,刷新頁面再次請求這個頁面的時候,cookie就變了,因此說cookie是不安全的,若是在cookie裏存了值是不能相信這個值的
Session 能夠解決這個問題,防止用戶篡改
4.Cookie 有效期嗎?
默認有效期20分鐘左右,不一樣瀏覽器策略不一樣,若是一直開着瀏覽器,那麼cookie就會長期有效,有的瀏覽器爲了安全,當關掉瀏覽器就會刪除cookie,多長時間呢?不肯定,若是關了立馬打開,可能不會刪,可是若是關了,次日再打開,以前的cookie可能就不在了
後端能夠強制設置有效期,若是強制設置,瀏覽器就不會刪了,具體語法看 MDN 如:
// 能夠寫幾月幾號過時
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
// 也能夠寫最長保存多長時間
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
// 設置針對哪一個域名
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
// 設置路徑
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
// 必須使用https才能看cookie
Set-Cookie: <cookie-name>=<cookie-value>; Secure
// JS不容許訪問,防止用戶修改cookie,在控制檯document.cookie能夠拿到cookie
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
複製代碼
5.Cookie 遵照同源策略嗎? 也有,不過跟 AJAX 的同源策略稍微有些不一樣。 當請求 qq.com 下的資源時,瀏覽器會默認帶上 qq.com 對應的 Cookie,不會帶上 baidu.com 對應的 Cookie 當請求 v.qq.com 下的資源時,瀏覽器不只會帶上 v.qq.com 的Cookie,還會帶上 qq.com 的 Cookie 另外 Cookie 還能夠根據路徑作限制,請自行了解,這個功能用得比較少。
解決一個問題: 登陸成功後跳轉到首頁以後,能不能在首頁展現用戶的信息?
咱們須要在首頁的路由裏作一下讀數據,須要拿到cookie在Cookie裏讀用戶的數據用戶名密碼,而後去和存在數據庫裏的用戶的信息(./db/users)作對好比果找到了,就說明已經註冊過了准許登陸
// 首頁的路由
+++
if (path === '/') {
let string = fs.readFileSync('./index.html', 'utf-8')
// 讀取cookie的email
let cookies = request.headers.cookie.split(';') // ['email = 1@...', 'xxx= 1','yyy=2']
let hash = {}
for(let i = 0;i<cookies.length;i++){
let parts = cookies[i].split('=')
let key = parts[0]
let value = parts[1]
hash[key] = value
}
let email = hash[' sign_in_email'] // 註冊的時候不當心多了個空格
// 讀取數據庫裏面的email
let users = fs.readFileSync('./db/users', 'utf8')
users = JSON.parse(users)
let foundUser
for(let i = 0;i<users.length;i++){
if(users[i].email === email){
foundUser = users[i]
break
}
}
if(foundUser){
string = string.replace('__password__',foundUser.password)
}else{
string = string.replace('__password__','不知道')
}
response.statusCode = 200
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}
+++
複製代碼
若是已經登陸能夠點x退出登陸
以上就是用http,ajax,promise,js作的全部代碼手寫的沒有一個最簡單的註冊登陸
回顧一下整個過程: