相关代码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作的全部代码手写的没有一个最简单的注册登陆
回顾一下整个过程: