内容简介
Windows 如何修改hosts
Windows 没有 /etc/hosts 文件,请按照如下方法修改 hosts:
- 用管理员身份打开记事本(在记事本的快捷方式图标上右键可以看到)
-
用这个记事本打开 hosts(菜单->文件->定位到C:\Windows\System32\drivers\etc目录,然后切换到所有文件)
数据库是什么鬼
- 文件系统是一种数据库
- MySQL 是一种数据库:用的最广泛,My是MySQL作者的女儿的名字的缩写
只要能长久地存数据,就是数据库
同源策略
浏览器故意设置的一个功能限制
同源与同源策略
同源
- 源
- window.origin 或 location.origin 可以得到当前源
- 源 = 协议 + 域名 + 端口号
- 如果两个url的
- 协议
- 域名
- 端口号
- 完全一致,那么这两个 url 就是同源的
- 举例
同源策略
- 浏览器规定
- 如果 JS 裕兴在源 A 里,那么久只能获取源 A 的数据
- 不能获取源 B 的数据,即不允许跨域
- 举例(省略 http 协议)
- 假设 frank.com/index.html 引用了 cdn.com/1.js
- 那么就说,1.js 运行在 源 frank.com 里
- 注意这跟 cdn.com 没有关系,虽然 1.js 从它那下载
- 所以 1.js 只能获取 frank.com数据
- 不能获取 1.frank.com 或者 qq.com 的数据
- 这是浏览器的功能!
- 浏览器故意要这样设计的
浏览器这样做的目的是?
- 为了保护隐私
- 怎么保护的?
- 假如没有同源策略
- 以QQ空间为例
- 源为https://user.qzone.qq.com
- 假设,当前用户已经登录(用Cookie,后面会讲)
- 假设:AJAX请求 /friends.json可获取用户好友列表
- 到目前为止都很正常
- 黑客来了
- 假装你的女神分享https://qzone-qq.com给你
- 实际上是一个钓鱼网站
- 你点开这个网页,这个网页也请求你的好友列表https://user.qzone.qq,com/friends.json
- 请问,你的好友列表是不是就被黑客拿到了
- 以QQ空间为例
- 假如没有同源策略
问题的根源
- 无法区分发送者
- QQ空间页面里的JS和黑客页面里的JS发的请求几乎没有区别(referrer有区别)
- 如果后台开发者没有检查referer,那么就完全没区别
- 所以,没有同源策略,任何页面都能偷QQ空间的数据,甚至支付宝余额
- 那检查referer不就好了?
- 安全原则:安全链条的强度取决于最弱一环
- 万一这个网站的后端开发工程师忽略了这一点呢
- 所以浏览器应该主动预防这种偷数据的行为
- 总之,浏览器为了用于隐私,设置了严格的同源策略
- referer
- 发送请求的时候,会有这个标识,我们可以后台看到
疑问
- 为什么 a.qq.com访问qq.com也算跨域
- 因为历史上,出现过不同公司共用域名,a.qq.com 和 qq.com 不一定是同一个网站,浏览器谨慎起见,认为这是不同的源
- 为什么不同端口也算跨域?
- 原因同上,一个端口一个公司。记住安全链条的强度取决于左若的一环,粉盒安全相关的问题都要谨慎对待
- 为什么两个网站的IP一样的,也算跨域?
- 原因同上,IP可以共用
- 为什么可以跨域使用CSS、JS和图片等?
- 同源策略限制的是数据访问,我们引用CSS、JS和图片的时候,其实并不知道其内容,我们只是在引用。
CORS(跨域资源共享)
突破浏览器限制的一个方法
- 问题根源
- 浏览器默认不同源之间不能互相访问数据
- 用 CORS
- 浏览器说,如果要共享数据,需要提前声明
- 那么怎么声明呢?
- 在被访问的网站那边的响应头里面写Access-Control-Allow-Orign
- response.setHeader('Access-Control-Allow-Origin', '网址')
一个简单的服务器
- 代码如下
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('有个傻子发请求过来啦!路径(带查询参数)为 ' + pathWithQuery)
response.write(`Hi\n`)
response.end()
/************** 从这里结束,下面不要看 ****************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度后然后用电饭煲打开 http://localhost:' + port)
-
进入目录中,使用node server 端口号可以查看服务器是不是已经创建成功了,同时我们可以看到浏览器打开的网页就是有一个Hi
-
我们发现不论我们请求什么,比如请求index.html,网页打印的都是Hi,这是因为我们写的服务器代码中,不管我们请求什么,返回的response就是一个Hi。
我们丰富一下返回的内容,以及针对不同的请求,让返回的内容不一样;记住访问的路径跟返回的response文件名称及路径是不一样的,没有任何关系
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('有个傻子发请求过来啦!路径(带查询参数)为 ' + pathWithQuery)
if(path === '/'){
var string = fs.readFileSync('./index.html', 'utf-8')
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/style.css'){
var string = fs.readFileSync('./style.css', 'utf-8')
response.setHeader('Content-Type', 'text/css')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js', 'utf-8')
response.setHeader('Content-Type', 'application/javascript')
response.write(string)
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('找不到对应的路径,你需要自行修改 index.js')
}
console.log(method + ' ' + request.url)
/************** 从这里结束,下面不要看 ****************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度后然后用电饭煲打开 http://localhost:' + port)
-
我们访问这个网页的时候,由于网页链接了main.js以及style.css,所以会访问这两个文件的
做一个网页加减与服务器交互
- 我们先写一个网页,让其有一个按钮,点击的时候会减少金额
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">100</span></h5>
<button id="button">付款1块钱</button>
<script>
button.addEventListener('click', (e)=>{
let n = amount.innerText // innerText一定是String
let number = parseInt(n, 10) // 可以直接减,但是为了保险起见,还是转换一下
let newNumber = number - 1
amount.innerText = newNumber
})
</script>
- 但是这个是假的,是网页上面的变化,没有跟任何服务器交互有关。当刷新页面的时候,会变回原样的。数据并没有永远的存储在一个地方。
- 我们如果想要数据永远存储到一个地方,需要使用硬盘存储。数据库就是硬盘文件。我们可以创建一个db,后缀没有无所谓的,我们在这个文件中只存储一个100。
-
然后我们是想在服务器读取文件的时候需要将db中的文件放入db中,我们在网页中对应的位置使用特殊的占位符,如&&&amount&&&,使用这个占位符可以将占位符替换成真正的100。
-
这下我们需要调整一下nodejs的代码,下面这个做法就是在启动server的时候,读取db文件,将这个文件放入网页中,这样我们读取出来的值虽然是100,但是这个100,其实是db中的100
- 当我们点击的时候,我们希望告诉服务器,请更改db内容,并刷新页面;这样就是在点击的时候,需要发送post请求,存储数据。目前button做不到,我们需要使用form
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">&&&amount&&&</span></h5>
<form action="/pay" method="post">
<input type="submit" value="付款">
</form>
-
当我们点击付款的时候,会发送一个post请求。此时会是404状态,因为我们并没有说明再此路径下要做什么
- 我们更改一下server。让当路径是/pay的时候,服务器读取db文件并减1存入。
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('有个傻子发请求过来啦!路径(带查询参数)为 ' + pathWithQuery)
if(path === '/'){
var string = fs.readFileSync('./index.html', 'utf-8') // 同步读取文件
var amount = fs.readFileSync('./db', 'utf-8') // 同步读取出来的数据类型还是string的
string = string.replace('&&&amount&&&', amount) // 这边是使用替换将原先字符串中的&&&amount&&&替换成db
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/style.css'){
var string = fs.readFileSync('./style.css', 'utf-8')
response.setHeader('Content-Type', 'text/css')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js', 'utf-8')
response.setHeader('Content-Type', 'application/javascript')
response.write(string)
response.end()
}else if(path === '/pay' && method.toUpperCase() === 'POST'){
var amount = fs.readFileSync('./db', 'utf-8')
var newAmount = amount - 1
fs.writeFileSync('./db', newAmount) // 将值写入db文件中
response.write('success')
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('找不到对应的路径,你需要自行修改 index.js')
}
console.log(method + ' ' + request.url)
/************** 从这里结束,下面不要看 ****************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度后然后用电饭煲打开 http://localhost:' + port)
-
当我们点击付款的时候,会跳转页面,然后返回去,刷新页面,就能看到金额变了,读取文件系统,返回的也是对应的值
- 这就是旧时代前后端配合的方法,2005年之前全世界的网站都是这样,点击会返回成功还是失败,然后刷新就可以看到的
- 一个细节,form表单提交之后一定会刷新页面。
-
我们可以稍微优化一下,使用iframe,给form添加target就是添加刷新位置。承担刷新的是iframe;这个套路已经没人用了
使用img创建请求
- 我们想发请求的标签不用form,用什么呢?可以使用a,CSS的link,script,img等
- 当我们动态创建一个img的时候,浏览器一定会去请求这个地址
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<script>
button.addEventListener('click', (e)=>{
let image = document.createElement('img')
image.src = '/pay'
})
</script>
-
这个请求的缺陷是,没有办法发POST请求,并且返回的response必须是图片,只给状态码200是不够的,容易出问题。
-
我们怎么监控这个图片请求成功或者失败呢?我们可以监控状态码
我们操作这个图片请求的时候,需要返回一张真正的图片,我们成功之后,可以直接在玩野上操作内容变化,而不是刷新网页,这就是一个局部刷新
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<script>
button.addEventListener('click', (e)=>{
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){
alert('打钱成功')
amount.innerText = amount.innerText - 1
}
image.onerror = function(){
alert('打钱失败')
}
})
</script>
所对应的server
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('有个傻子发请求过来啦!路径(带查询参数)为 ' + pathWithQuery)
if(path === '/'){
var string = fs.readFileSync('./index.html', 'utf-8') // 同步读取文件
var amount = fs.readFileSync('./db', 'utf-8') // 同步读取出来的数据类型还是string的
string = string.replace('&&&amount&&&', amount) // 这边是使用替换将原先字符串中的&&&amount&&&替换成db
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/style.css'){
var string = fs.readFileSync('./style.css', 'utf-8')
response.setHeader('Content-Type', 'text/css')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js', 'utf-8')
response.setHeader('Content-Type', 'application/javascript')
response.write(string)
response.end()
}else if(path === '/pay'){
var amount = fs.readFileSync('./db', 'utf-8')
var newAmount = amount - 1
if(Math.random()>0.5){
fs.writeFileSync('/db', newAmount)
response.setHeader('Content-Type', 'image/png')
response.statusCode = 200
response.write(fs.readFileSync('cat.jpg'))
}else{
response.statusCode = 400
response.write('fail')
}
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('找不到对应的路径,你需要自行修改 index.js')
}
console.log(method + ' ' + request.url)
/************** 从这里结束,下面不要看 ****************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度后然后用电饭煲打开 http://localhost:' + port)
使用script发请求
- 我们上面通过image发请求的,能不能通过script发请求呢?
- 使用script标签,光创建,添加src属性是不够的,创建出来必须加入到body中才可以有用
- 下面是代码
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<script>
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(){
alert('success')
}
script.onerror = function(){
alert('fail')
}
})
</script>
4.这样我们在发现点击的时候,会出现多个script标签,script标签里面的内容会执行的,但是我们发现服务器会弹出一个框提示成功与否,而script执行会弹出一个框,提示成功与否。这样就重复了,即下面两张图的内容重复了
- 也就是上面的onload我们可以不写的。我们也可以在服务器端直接局部更改页面中值
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<script>
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onerror = function(){
alert('fail')
}
})
</script>
对应服务器的代码
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('有个傻子发请求过来啦!路径(带查询参数)为 ' + pathWithQuery)
if(path === '/'){
var string = fs.readFileSync('./index.html', 'utf-8') // 同步读取文件
var amount = fs.readFileSync('./db', 'utf-8') // 同步读取出来的数据类型还是string的
string = string.replace('&&&amount&&&', amount) // 这边是使用替换将原先字符串中的&&&amount&&&替换成db
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/style.css'){
var string = fs.readFileSync('./style.css', 'utf-8')
response.setHeader('Content-Type', 'text/css')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js', 'utf-8')
response.setHeader('Content-Type', 'application/javascript')
response.write(string)
response.end()
}else if(path === '/pay'){
var amount = fs.readFileSync('./db', 'utf-8')
var newAmount = amount - 1
fs.writeFileSync('/db', newAmount)
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200
// response.write(`
// alert("success")
// window.location.reload()
// `) // 服务器返回的在浏览器执行的JS代码,提示成功并刷新网页
response.write('amount.innerText = amount.innerText - 1') //直接局部刷新,不做整体刷新
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('找不到对应的路径,你需要自行修改 index.js')
}
console.log(method + ' ' + request.url)
/************** 从这里结束,下面不要看 ****************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度后然后用电饭煲打开 http://localhost:' + port)
- 我们发现当我们每打一次钱二代时候,会创建一个script,这样页面中会出现多个script,我们可以在响应之后将script删除掉。使用script.onload和script.onerror在刷新或者失败的时候将其自身删除就好
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<script>
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove() // 从页面中移除元素
}
script.onerror = function(e){
e.currentTarget.remove()
alert('fail')
}
})
</script>
- 对这个方案的总结:当用户发生一个点击动作的时候,我们可以生成一个script,然后script的src属性就是我们要请求的路径,将这个script放入页面中,浏览器就会发起一个get请求(此处依旧无法使用POST),如果get成功了,首先会运行服务器返回的javascript响应,这个服务器返回的javascript响应就是操作局部数据进行刷新,那段代码会被当做JS执行是基于HTTP协议。
这边能够执行是有两个条件的:- 第一个条件是基于HTTP协议,我们基于这个HTTP指定response是javascript
- 第二个条件是,这个路径的引入方式是script里面。所以前面返回的response一定会在script里面执行的
执行之后,如果成功,用户就可以看到金额变化了,执行之后,script就删除了
- 上面这个方案被称为SRJ(Server Rendered Javascript),服务器返回的javascript;这个在AJAX出现之前,一些很牛逼的后端程序员想出来的无刷新局部更新页面内容的方案。
一个域名可以访问另一个域名吗?
-
首先有一个问题,我们在一个页面中引用一个script的时候,src的域名重要吗?如我们需要在一个fangfang.com里面访问baidu.com是可以的吗?
- 答案是可以的。比如我们在引用jQuery的时候就是不一样的域名;Script是不受域名限制的,任何一个网站都可以使用另一个域名网站的JS文件
-
我们大部分的pay是使用post请求的,因为get请求太容易被伪造了,因为任何一个网站都可以请求这个API。重要的操作一定不要使用get,否则别人就可以使用图片或者script来驱动,我们只要将对应的src链接到相应的地址就好了
做两个网页进行交互-->JSONP
-
我们首先更改一下host文件,添加两个局域的域名,两个域名不一样,但是都是访问一个IP
-
我们开两个server,端口号可以这样进行设置:PORT=8001 node server.js,但是此处我们写死了,不可以这样,两个服务器端口号不一样,所以是不同的网站,只不过这两个网站的源代码是一样的
-
我们如果想要frank.com的前端去访问jack.com的后端,由于不分域名,这个技术是可以实现的。我们可以更改src链接地址到jack.com就好了
-
当我们在frank.com页面点击打钱的时候,就会发送请求到jack.com,操作jack.com;这两个是完全不同的网站,但是它们是可以调对方的script的
这种做法其实是有矛盾的,我们看到其实在后端的时候,是操作前端的,也就是我想访问另一个网页的后端,需对其前端也要了解,这就叫做耦合,当我对你的页面了解不够深的时候,就写不下去了,彼此之间太紧密了。
我们需要解耦的,我们将要执行的代码放入函数中,函数内容不管,我们只要函数名,去调用就好了
-
具体的情况是这样的,要访问的网站先将xxx函数写好,然后我们访问端只需要调用就好了
现在点打钱就能请求到相应的函数,及调用,而对内部的函数是什么样的根本不关心,两个网站就可以无缝进行交互
-
对于xxx这个函数怎么知道的,其实可以在网页调用的时候传参进去
这样就只需告诉成功失败的结果,不需要细节,这个就是JSONP
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<script>
window.xxx = function(result){
alert('这是frank写的前端代码')
alert(`我得到的结果是${result}`)
}
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){
e.currentTarget.remove()
alert('fail')
}
})
</script>
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('有个傻子发请求过来啦!路径(带查询参数)为 ' + pathWithQuery)
if(path === '/'){
var string = fs.readFileSync('./index.html', 'utf-8') // 同步读取文件
var amount = fs.readFileSync('./db', 'utf-8') // 同步读取出来的数据类型还是string的
string = string.replace('&&&amount&&&', amount) // 这边是使用替换将原先字符串中的&&&amount&&&替换成db
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if(path === '/style.css'){
var string = fs.readFileSync('./style.css', 'utf-8')
response.setHeader('Content-Type', 'text/css')
response.write(string)
response.end()
}else if(path === '/main.js'){
var string = fs.readFileSync('./main.js', 'utf-8')
response.setHeader('Content-Type', 'application/javascript')
response.write(string)
response.end()
}else if(path === '/pay'){
var amount = fs.readFileSync('./db', 'utf-8')
var newAmount = amount - 1
fs.writeFileSync('/db', newAmount)
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200
// response.write(`
// alert("success")
// window.location.reload()
// `) // 服务器返回的在浏览器执行的JS代码,提示成功并刷新网页
// 说明jack.com的后端程序员需要对frank.com的页面细节了解的很清楚
// 耦合
// response.write('amount.innerText = amount.innerText - 1') //直接局部刷新,不做整体刷新
// 这一步很重要,一定要记住
response.write(`
${query.callbackName}.call(undefined, 'success')
`)
response.end()
}else{
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write('找不到对应的路径,你需要自行修改 index.js')
}
console.log(method + ' ' + request.url)
/************** 从这里结束,下面不要看 ****************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度后然后用电饭煲打开 http://localhost:' + port)
- JSONP要接觉得问题是两个网站之间怎么交流,我们可以使用script,因为script是不受域名限制的,而AJAX是受域名限制的;既然不受限制,我们就可以请求对方的网站,将数据给它,并调用xxx,然后将参数传入xxx的第一个参数里面;其中接受参数那一步很重要
-
服务器这端,函数名是不知道的,调用的时候其实只传了一个参数,就是结果'success'。如果穿的这个参数是json(json是一门新的语言,很JS很像,但是一定要加双引号,前面有一个{,后面有一个},分别叫左padding和右padding),所以可以理解成JSON+Padding,叫JSONP。但是大部分情况下,可以不是JSON,使用String,就会是StringP。高亮的部分就是JSON,我们的核心就是返回JSON或者String,但是我们需要加padding才可以,JSON前面的就是左padding,JSON右边的就是右Padding
- 整个过程跟JSON没有关系的。
JSONP过程
过程
我们分为请求方和响应方,请求方是一个网站的前端,响应方是另一个网站的后端
请求方需要创建一个script标签,src指向响应方,同时传一个查询参数 ?callbackName=xxx
-
响应方根据查询参数callbackName,构造形如
- xxx.call(undefined, '你要的数据')
- xxx.('你要的数据')
这样的响应
浏览器接收到响应,就会执行xxx.call(undefined, '你要的数据')
那么请求方就知道了他要的数据
约定
- 我们上面写的callbackName必须交callback,即callbackName -> callback
- xxx -> 随机数:因为我可能调用了10个网站的JSONP,但是每个网站我们都要想个名字太麻烦,我们就使用随机数,随便取一个,如jQuery123456785(),每到下一个我们就加个1,以避免重复,但是名字不要以数字开头
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<script>
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
// 实现名称+随机数,random返回的是小数,我们将其改装成整数
let functionName = 'frank'+parseInt(Math.random()*10000, 10)
// 将随机名称绑定成函数
window[functionName] = function(result){
if(result === 'success'){
amount.innerText = amount.innerText - 1
}else{}
}
script.src = 'http://jack.com:8002/pay?callback=' + functionName
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove() // 从页面中移除元素
delete window[functionName] // 删除函数,避免污染环境
}
script.onerror = function(e){
e.currentTarget.remove()
alert('fail')
delete window[functionName]
}
})
</script>
使用jQuery实现JSONP
-
先在环境中下载jQuery,npm i jquery
- 代码如下
<title>首页</title>
<link rel="stylesheet" href="/style.css">
<h5>您 的 账 户 余 额 是 <span id="amount">&&&amount&&&</span></h5>
<button id="button">打钱</button>
<!-- 此处由于没有搁jQuery做路由,所以访问不到,所谓路由就是给一个if else -->
<!-- <script src='./node_modules/jquery/dist/jquery.min.js'></script> -->
<script src='https://cdnjs.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>
<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>
-
jQuery自己做了callback,服务器返回的也是callback函数
注意一点,JSONP不是ajax
JSONP为什么不能使用POST请求
- 因为JSONP是通过动态创建script来创建的
- 动态创建的script是不能发送post请求的。
局部刷新怎么做?
有没有想过,不返回 HTML,返回 JS
方案一:用图片造 get 请求
button.addEventListener('click', (e)=>{
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){ // 状态码是 200~299 则表示成功
alert('成功')
}
image.onerror = function(){ // 状态码大于等于 400 则表示失败
alert('失败')
}
})
方案二:用 script 造 get 请求
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(e){ // 状态码是 200~299 则表示成功
e.currentTarget.remove()
}
script.onerror = function(e){ // 状态码大于等于 400 则表示失败
e.currentTarget.remove()
}
})
//后端代码
...
if (path === '/pay'){
let amount = fs.readFileSync('./db', 'utf8')
amount -= 1
fs.writeFileSync('./db', amount)
response.setHeader('Content-Type', 'application/javascript')
response.write('amount.innerText = ' + amount)
response.end()
}
...
这种技术叫做 SRJ - Server Rendered JavaScript
域名什么的无所谓
跨域 SRJ
确定函数名
JSONP
请求方:frank.com 的前端程序员(浏览器)
响应方:jack.com 的后端程序员(服务器)
- 请求方创建 script,src 指向响应方,同时传一个查询参数 ?callbackName=yyy
- 响应方根据查询参数callbackName,构造形如
- yyy.call(undefined, '你要的数据')
- yyy('你要的数据')
这样的响应
- 浏览器接收到响应,就会执行 yyy.call(undefined, '你要的数据')
- 那么请求方就知道了他要的数据
这就是 JSONP
方案3:JSONP
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
let functionName = 'frank'+ parseInt(Math.random()*10000000 ,10)
window[functionName] = function(){ // 每次请求之前搞出一个随机的函数
amount.innerText = amount.innerText - 0 - 1
}
script.src = '/pay?callback=' + functionName
document.body.appendChild(script)
script.onload = function(e){ // 状态码是 200~299 则表示成功
e.currentTarget.remove()
delete window[functionName] // 请求完了就干掉这个随机函数
}
script.onerror = function(e){ // 状态码大于等于 400 则表示失败
e.currentTarget.remove()
delete window[functionName] // 请求完了就干掉这个随机函数
}
})
//后端代码
...
if (path === '/pay'){
let amount = fs.readFileSync('./db', 'utf8')
amount -= 1
fs.writeFileSync('./db', amount)
let callbackName = query.callback
response.setHeader('Content-Type', 'application/javascript')
response.write(`
${callbackName}.call(undefined, 'success')
`)
response.end()
}
...
约定:
1. callbackName -> callback
2. yyy -> 随机数 frank12312312312321325()
$.ajax({
url: "http://jack.com:8002/pay",
dataType: "jsonp",
success: function( response ) {
if(response === 'success'){
amount.innerText = amount.innerText - 1
}
}
})
$.jsonp()
有关JSPNP优缺点的总结
- 优点:
- 支持IE
- 可以跨域
- 缺点:
- script标签,拿不到状态码
- 不支持POST,只支持GET
封装JSONP
function jsonp(url) {
return new Promise((resolve, reject) => {
const random = "callbackName" + Math.random()
window[random] = data => {
resolve(data)
}
const script = document.createElement("script");
script.src = `${url}?functionName=${random}`
script.onload = () => {
script.remove()
}
script.onerror = () => {
reject()
}
document.body.appendChild(script)
})
}
jsonp('https://www.baidu.com/load.js')
.then((data => {
console.log(data)
}))