请先了解:《阮一峰:浏览器同源政策及其规避方法》
注意:请首先更新之前用过的 nodejs-test 代码,否则会报错
nodejs-test 代码(饥人谷)
三种声明:window.x
,var x
,let x
的区别:
一、数据库是什么鬼
- 文件系统是一种数据库;
- MySQL 是一种数据库;
只要能长久地存数据,就是数据库
- 111
- 222
①. 111
②. 222
③. 333
二、用文件当数据库
例:用数据库做加法:
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 path = request.url
var query = ''
if(path.indexOf('?') >= 0){ query = path.substring(path.indexOf('?')) }
var pathNoQuery = parsedUrl.pathname
var queryObject = parsedUrl.query
var method = request.method
/******** 从这里开始看,上面不要看 ************/
console.log('HTTP 路径为\n' + path)
if(path == '/style.css'){
var string = fs.readFileSync('./style.css','utf8')
response.setHeader('Content-Type', 'text/css; charset=utf-8')
response.write(string)
response.end()
}else if(path == '/main.js'){
var string = fs.readFileSync('./main.js','utf8')
response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
response.write(string)
response.end()
}else if(path == '/'){
var string = fs.readFileSync('./index.html','utf8')
response.setHeader('Content-Type', 'text/html; charset=utf-8')
response.write(string)
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)
对应路径匹配到的html:
<body>
<h5>你的账户余额是:<span id="amount">100</span></h5>
<button id="button">付款1块钱</button>
<script>
button.addEventListener('click',(e)=>{
let n = amount.innerText
let number = n - 0 //将字符串转换为数字
let newNumber = n - 1
amount.innerText = newNumber
})
</script>
</body>
以上代码改变的余额只是 html 里的数据,而不是后台的数据库里 的数据,也就是刷新网页后数据又还原了;
第二步:接下来,尝试创建一个极简的数据库,然后通过前台的点击改变数据库的数据:明显改变后台数据库需要用到 post 请求,而不是get 请求,因此首先尝试用 form 表单;(如果是【post】,那么表单数据将放在请求体中被发送出去。如果是【get】,那么表单数据将会追加到查询字符串中,以查询字符串的形式提交到服务端。)
变更后增加代码如下:
else if(path === '/pay' && method.toUpperCase() === 'POST'){
var amount = fs.readFileSync('./db','utf8')
var newAmount = amount - 1
if(Math.random() > 0.5){
fs.writeFileSync('./db', newAmount)
response.write('success')
}else{
response.write('fail')
}
response.end()
}
<h5>你的账户余额是:<span id="amount">&&&amount&&&</span></h5>
<form action="/pay" method="post" target="result">
<input type="submit" value="付款">
</form>
<iframe name="result" src="about:blank" frameborder="1" height="200px"></iframe>
每次提交 form 表单后页面都会刷新,怎么解决???
如上:引入 iframe 标签,让提交 form 表单 后在iframe 里刷新。response.write('success') 与 response.write('fail') 也会显示在 iframe 里;
第三步:能不能不用刷新且不用引入 ifame 标签???
解决方法:不用 form 标签单发请求,用其他标签如:a 标签,img 标签,link标签,script 标签 等.......
三、用<img src="">
发请求
- 用
<img src="">
尝试:
console.log('HTTP 路径为\n' + path)
if(path === '/style.css'){
var string = fs.readFileSync('./style.css','utf8')
response.setHeader('Content-Type', 'text/css; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js','utf8')
response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/'){
var string = fs.readFileSync('./index.html','utf8')
var amount = fs.readFileSync('./db','utf8') // 100,那么db的类型是啥???
string = string.replace('&&&amount&&&',amount)
response.setHeader('Content-Type', 'text/html; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8')
var newAmount = amount - 1
if(Math.random() > 0.5){
fs.writeFileSync('./db', newAmount)
response.setHeader('Content-Type','image/png') //由于实际并没有响应一个图片文件,导致statusCode = 200 依然 alert('fail')
response.statusCode = 200
response.write('success')
}else{
response.statusCode = 400
response.write('fail')
}
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type','text/html;charset=utf-8')
response.write('找不到对应的路径')
response.end()
}
<h5>你的账户余额是:<span id="amount">&&&amount&&&</span></h5>
<!-- <form action="/pay" method="post" target="result">
<input type="submit" value="付款">
</form> -->
<!-- <iframe name="result" src="about:blank" frameborder="1" height="200px"></iframe> -->
<button id="button">付款1块钱</button>
<script>
button.addEventListener('click',(e)=>{
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){
alert('success')
}
image.onerror = function(){
alert('fail')
}
})
</script>
用<img src="">
的方式发请求时,只要 createElement 就会 get ,不需要添加到 html 里。实际测试时发现可以修改后台数据,但是只会执行alert('fail')
,原因可能是没有实际 响应一个图片文件。修改成功后,依然需要刷新页面才能显示新数据,alert('fail')
与alert('success)
可以提示用户付款结果。
接下来,实际在响应中返回一个图片测试,修改如下:
}else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8')
var newAmount = amount - 1
if(Math.random() > 0.5){
fs.writeFileSync('./db', newAmount)
response.setHeader('Content-Type','image/jpg')
response.statusCode = 200
response.write(fs.readFileSync('./000.jpg')) //传一个真实的图片
}else{
response.statusCode = 400
response.write('fail')
}
response.end()
在如实响应一个图片文件后,alert 结果就正常了;
目前还有一个问题: 用户需要手动刷新页面才能看到余额的变化,怎么解决????
下一步:不用手动刷新,如下:
index.html
image.onload = function(){
alert('success')
window.location.reload() // 加这句,自动刷新
}
不用手动刷新的另一种思路,在 image.onload 里面直接修改#amount
里面的内容,让页面显示的值与后台一样。
三、用<script></script>
发请求
如下:
index.html
<body>
<h5>你的账户余额是:<span id="amount">&&&amount&&&</span></h5>
<button id="button">付款1块钱</button>
<script>
button.addEventListener('click',(e)=>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(){
alert('success')
window.location.reload()
}
script.onerror = function(){
alert('fail')
}
})
</script>
<script src="./main.js"></script>
</body>
用<script></script>
发请求与 img 不同,script 仅仅只是创建的话不会发送请求,需要添加到body里面才行;
server.js变更:
}else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8')
var newAmount = amount - 1
if(Math.random() > 0.5){
fs.writeFileSync('./db', newAmount)
response.setHeader('Content-Type','application/javascript')
response.statusCode = 200
response.write('') // 响应一个空的js文件
}else{
response.statusCode = 400
response.write('fail')
}
response.end()
}
测试中发现,每success一次,都会在body里添加一个src='/pay'
的script的标签;
然后将response.write('') // 响应一个空的js文件
变更为response.write('alert("success")') // 响应一个空的js文件
,后发现响应的js文件会被执行...............因此可以将点击后的监听放到响应里;
如下:
}else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8')
var newAmount = amount - 1
fs.writeFileSync('./db', newAmount)
response.setHeader('Content-Type','application/javascript')
response.statusCode = 200
response.write(`
alert("付款成功")
window.location.reload()
`) //此处使用了ES6的书写规则..
response.end()
如果不想刷新整个页面可以改成:
response.write(`
alert("付款成功")
//window.location.reload()
amount.innerText = amount.innerText - 1
`)
最后还有个问题:没点击一次,就会在body里面增加一个script标签.........................怎么解决?????
四、删除script标签(请求完成后)
在script里的内容执行完后删除script标签:
在前端监听script即可:
script.onload = function(e){
// alert('success')
// window.location.reload()
e.currentTarget.remove()
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
}
以上都是请求的同一域名下的文件,接下来尝试请求不同域名下的js文件。
五、请求另一个网站的script
注意:请首先更新你的 nodejs-test 代码(server.js),否则会报错
环境配置:在hosts文件里配置:
127.0.0.1 frank.com
127.0.0.1 jack.com
这样就有两个域名了,虽然ip地址一样;
然后在 git bash开两个node server:
Administrator@PC-20160402SHGE MINGW64 /j/web-project/node-demo (master)
$ PORT=8001 node server.js
监听 8001 成功
请用在空中转体720度然后用电饭煲打开 http://localhost:8001
Administrator@PC-20160402SHGE MINGW64 /j/web-project/node-demo (master)
$ PORT=8002 node server.js
监听 8001 成功
请用在空中转体720度然后用电饭煲打开 http://localhost:8002
在浏览器可以分别打开http://jack.com:8002/和http://frank.com:8001/,只是这两个网址的源代码一样;
到此,环境就准备好了。
测试:让frank.com的前端访问jack.com的后端。
变更代码如下:
html
<body>
<h5>你的账户余额是:<span id="amount">&&&amount&&&</span></h5>
<button id="button">付款1块钱</button>
<script>
button.addEventListener('click',(e)=>{
let script = document.createElement('script')
script.src = 'http://jack.com:8002/pay'
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
}
})
</script>
</body>
server
else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8')
var newAmount = amount - 1
fs.writeFileSync('./db', newAmount)
response.setHeader('Content-Type','application/javascript')
response.statusCode = 200
response.write(`
alert("付款成功")
// window.location.reload()
amount.innerText = amount.innerText - 1
`)
response.end()
}
测试结果,在frank.com的页面点击时能请求到jack.com的后端响应的script文件。
以上代码还存在一个问题:jack.com的后端需要对frank.com的页面细节了解很清楚,并且需要在后端写一段js代码,这样 耦合 成都太紧,可操作性太差;
怎么解除这种耦合的紧密性?????
方法:
frank.com的前端事先在click里面定义一个方法:
window.xxx = function(result){
if(result === 'success'){
alert("付款成功")
// window.location.reload()
amount.innerText = amount.innerText - 1
}else{
alert("付款失败")
}
}
然后,jack.com的后端的响应中调用这一个方法:
response.write(` //这个不是单引号....
xxx.call(undefined,'success')
`)
以上还有点小问题:jack.com的后端得找frank.com的前端询问事先定义的方法名,怎么解决???
优化:可以通过在请求中加入查询参数将函数名传过去;
如下:
html
script.src = 'http://jack.com:8002/pay?callbackName=xxx'
server
response.write(` //这个不是单引号....
${query.callbackName}.call(undefined,'success')
`)
这样,解耦完成。。。。
总结:需要解决两个网站之间的交流问题时,由于 script 标签可以请求其他网站的文件,因此可以使用 script 来解决;基本思路就是:如果A站要请求B站的一个数据,那么A站首先通过 script 标签向B站发一个请求,B站收到请求后准备把数据传给A站时,只要调用A站传过来的一个函数,并将数据作为被调用函数的参数,最后B站将这个调用作为响应内容响应给A站,这样交流就完成了。------------------如果被调用函数的参数是JSON,那么,这就是JSONP了。
六、什么是JSONP
JSONP出现的目的:为了要解决两个网站之间的交流。
JSONP的工作过程:
假定现在有两个网站frank.com和jack.com,并且frank.com 的前端要向jack.com 的后端请求一个数据。
请求方:frank.com 的前端程序员(浏览器)
响应方:jack.com 的后端程序员(服务器)
其工作过程如下:
- 请求方创建script,script的src 指向响应方,同时传递一个查询参数?callbackName=yyy;
- 响应方根据查询参数callbackName=yyy,构建形如:
- yyy.call(undefined,'请求方要的数据')
- yyy('请求方要的数据')
这种格式的响应;
- 由于响应的内容是script类型的,请求方(浏览器)收到响应后,会直接执行响应的内容,也就是执行yyy.call(undefined,'请求方要的数据');
- 这样请求方就得到了不同网址的后端的数据。
在书写JSONP时有些默认的约定:
- callbackName -> callback
- yyy -> 随机数,比如abc48489312668()
上面整个过程就是 JSONP。
一个符合约定的JSONP:
html
<body>
<h5>你的账户余额是:<span id="amount">&&&amount&&&</span></h5>
<button id="button">付款1块钱</button>
<script>
button.addEventListener('click',(e)=>{
let functionName = 'xxx' + parseInt(Math.random() * 1000000,10)
window[functionName] = function(result){
if(result === 'success'){
alert("付款成功")
amount.innerText = amount.innerText - 1
}else{
alert("付款失败")
}
}
let script = document.createElement('script')
script.src = 'http://jack.com:8002/pay?callback=' + functionName
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
alert('fail')
e.currentTarget.remove()
}
})
</script>
<script src="./main.js"></script>
</body>
server
var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.env.PORT || 8888;
var server = http.createServer(function(request, response){
var temp = url.parse(request.url,true)
var path = temp.pathname
var query = temp.query
var method = request.method
/******** 从这里开始看,上面不要看 ************/
console.log('HTTP 路径为\n' + path)
if(path === '/style.css'){
var string = fs.readFileSync('./style.css','utf8')
response.setHeader('Content-Type', 'text/css; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js','utf8')
response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/'){
var string = fs.readFileSync('./index.html','utf8')
var amount = fs.readFileSync('./db','utf8') // 100,那么db的类型是啥???
string = string.replace('&&&amount&&&',amount)
response.setHeader('Content-Type', 'text/html; charset=utf-8')
response.write(string)
response.end()
}else if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8')
var newAmount = amount - 1
fs.writeFileSync('./db', newAmount)
response.setHeader('Content-Type','application/javascript')
response.statusCode = 200
response.write(`
${query.callback}.call(undefined,'success')
`)
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)
用jQuery重新写,如下:
html
<body>
<h5>你的账户余额是:<span id="amount">&&&amount&&&</span></h5>
<button id="button">付款1块钱</button>
<script>
button.addEventListener('click',(e)=>{
$.ajax({
url:"http://jack.com:8002/pay",
dataType:"jsonp",
success:function(response){
if(response === 'success'){
amount.innerText = amount.innerText - 1
}
}
})
})
</script>
- 111
- 222
①. 111
②. 222
③. 333