摘要:高級部分前后端聯動瀏覽器服務器數據庫是什么文件系統是一種數據庫是一種數據庫也是一個軟件只要能長久地存數據,就是數據庫前后端如何配合接下來我們用一個文件充當數據庫實際上數據庫的存儲內容本質就是一個帶有結構的文件捋一捋前后端交互的過程代碼在這里
jsonp
javascript高級部分:前后端聯動,瀏覽器+服務器
1數據庫是什么文件系統是一種數據庫
MySQL 是一種數據庫,也是一個軟件
只要能長久地存數據,就是數據庫
接下來我們用一個文件充當數據庫(實際上數據庫的存儲內容本質就是一個帶有結構的文件),捋一捋前后端交互的過程.
代碼在這里
幾次代碼的變更在歷史commit里
首先寫一個首頁
首頁 我的余額為&&&amount&&&
再寫服務器的代碼
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 //從這里開始看,上面不要看 if (path === "/") { // 如果用戶請求的是 / 路徑 var string = fs.readFileSync("./index.html") // 就讀取 index.html 的內容 var amount = fs.readFileSync("./db","utf-8")//****從數據庫(db文件)同步讀取數據,放到amount變量里 string = string.toString().replace("&&&amount&&&",amount)//將后臺的amount替換為前臺的占位符&&&amount&&& response.setHeader("Content-Type", "text/html;charset=utf-8") // 設置響應頭 Content-Type response.write(string) // 設置響應消息體 response.end(); } else if (path === "/style.css") { // 如果用戶請求的是 /style.css 路徑 var string = fs.readFileSync("./style.css") response.setHeader("Content-Type", "text/css") response.write(string) response.end() } else if (path === "/main.js") { // 如果用戶請求的是 /main.js 路徑 var string = fs.readFileSync("./main.js") response.setHeader("Content-Type", "application/javascript") response.write(string) response.end() } else if(path === "/pay" && method.toUpperCase() === "POST"){//如果請求的路徑是pay且方法為post var amount = fs.readFileSync("./db","utf-8") var newAmount = amount-1; fs.writeFileSync("./db",newAmount); response.write("success") response.end() }else { // 如果上面都不是用戶請求的路徑 response.statusCode = 404 response.setHeader("Content-Type", "text/html;charset=utf-8") // 設置響應頭 Content-Type response.write("找不到對應的路徑,你需要自行修改 index.js") response.end() } // 代碼結束,下面不要看 console.log(method + " " + request.url) }) server.listen(port) console.log("監聽 " + port + " 成功,請用在空中轉體720度然后用電飯煲打開 http://localhost:" + port)
最后在創建一個名為db的文本文件充當數據庫
接著我們開啟服務器
node index.js 8888
接著在瀏覽器中輸入地址http://localhost:8888
當用戶打開瀏覽器輸入ip地址,就會向后臺發送請求,服務器發現地址是/之后執行if else的path==="/"分支,執行下面的代碼:
if (path === "/") { // 如果用戶請求的是 / 路徑 var string = fs.readFileSync("./index.html") // 就讀取 index.html 的內容 var amount = fs.readFileSync("./db","utf-8")//****從數據庫(db文件)同步讀取數據,放到amount變量里 string = string.toString().replace("&&&amount&&&",amount)//將后臺的amount替換為前臺的占位符&&&amount&&& response.setHeader("Content-Type", "text/html;charset=utf-8") // 設置響應頭 Content-Type response.write(string) // 設置響應消息體 response.end(); }
接著點擊提交按鈕,就會向后臺發起post請求,請求路徑為/pay,于是進入path === "/pay"分支,執行下面的代碼,使數據庫(這里數據庫用一個文本文件代替)中的金額減一
else if(path === "/pay" && method.toUpperCase() === "POST"){//如果請求的路徑是pay且方法為post var amount = fs.readFileSync("./db","utf-8") var newAmount = amount-1; fs.writeFileSync("./db",newAmount); response.write("success") response.end() }
然后會發現/pay頁面返回了success
返回剛才的首頁,刷新后,會發現金額減少了1.
也可以模擬后臺修改數據庫成功或失敗
else if(path === "/pay" && method.toUpperCase() === "POST"){//如果請求的路徑是pay且方法為post var amount = fs.readFileSync("./db","utf-8") var newAmount = amount-1; if(Math.random()>0.5){//模擬成功或失敗 fs.writeFileSync("./db",newAmount); response.write("success") }else{ response.write("fail") } response.end() }3 優化體驗:用image和script發送請求
但是剛才那樣體驗非常不好,因為要返回,要刷新.在2005年以前,網頁的前后端交互都是這樣.但現在是8012年了,我們需要優化一下用戶體驗
不光form表單可以發送請求,a標簽(需要點擊),link標簽,script,圖片,都可以發送請求
3.1用image發送請求那我們先試試用img發送一個請求
當瀏覽器發現html里面有一個img時,會自動發送請求,請求路徑就是img的src.但是有一個缺點.發送的請求只能是get,沒辦法改成post.
使用img.onload和img.onerror方法判斷請求是成功還是失敗,具體操作看下方代碼
復習一下返回的狀態碼:
2開頭:成功
3開頭:重定向
4開頭:客戶端錯誤
5開頭:服務器錯誤
修改首頁
首頁 我的余額為&&&amount&&&
修改/pay路徑的響應
else if(path === "/pay"){//如果請求的路徑是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/jpg")//設置返回文件類型為jpg response.statusCode = 200;//返回碼為200,說明成功 response.write(fs.readFileSync("./dog.jpg"))//必須返回一個真的圖片,不然瀏覽器還是會認為是失敗 }else{ response.statusCode = 400;//否則返回碼為400,說明失敗 response.write("fail") } response.end() }
然后開啟服務器,瀏覽器輸入地址
點擊付錢,成功,并刷新
付錢失敗時,彈框后瀏覽器就不刷新了:
也可局部刷新
img.onload = function(){//如果圖片請求成功(返回碼以2開頭) alert("付錢成功") amount.innerText = amount.innerText-1;//局部刷新/因為已經知道數據庫修改成功,所以無需再進行刷新,直接修改數值即可 }
還有一個需要注意的點
在下面這段代碼中
response.setHeader("Content-Type","image/jpg")//設置返回文件類型為jpg, response.statusCode = 200;//返回碼為200,說明成功 response.write(fs.readFileSync("./dog.jpg"))//必須返回一個真的圖片,不然瀏覽器還是會認為是失敗
看看回復的響應
response.write(fs.readFileSync("./dog.jpg"))這句代碼中fs.readFileSync("./dog.jpg")參數里只有路徑,沒有加別的,那么讀出來的數據類型為2進制數據,然后"Content-Type","image/jpg"這句代碼表示以jpg圖片的形式讀取這段二進制代碼.
復習一下響應的四個部分:
1 協議/版本號 狀態碼 狀態解釋 2 Key1: value1 2 Key2: value2 2 Content-Length: 17931 2 Content-Type: text/html 3 4 響應的內容
response.write(fs.readFileSync("./dog.jpg"))這句代碼就是響應的第四部分,即響應的內容
3.2用script發送請求那么我們也可以用script動態的發送請求.原理和img類似,但是必須得把script標簽加入到body里面,才會發送請求,而img標簽只要創建,就會發送請求.
接下來看代碼
首頁的發送請求代碼
服務器端的代碼:
else if(path === "/pay"){//如果請求的路徑是pay,接受script的請求 var amount = fs.readFileSync("./db","utf-8") var newAmount = amount-1; if(Math.random()>0.5){//模擬成功或失敗 fs.writeFileSync("./db",newAmount);//成功了就把數據寫入數據庫 response.setHeader("Content-Type","applacation/javascript")//設置返回文件類型為javascript response.statusCode = 200;//返回碼為200,說明成功 response.write("alert("我是創建的script請求里面響應的內容")")//響應內容為創建的script標簽里面的內容 }else{ response.statusCode = 400;//否則返回碼為400,說明失敗 response.write("fail") } response.end() }
當我點擊打錢的時候,他會動態的創建一個script,這個script會根據src發起請求
進入失敗分支:
成功:
先執行請求使用的script里面的語句
每次請求就會創建一個新的script標簽,如果響應成功了,就執行響應里面的javascript語句,這個script里面的語句就是返回的響應的第四部分的內容,執行完之后才會觸發onload事件
那么既然請求返回的響應可以執行,我們就不需要寫onload了,直接把onload里面的代碼寫在響應的第四部分不就可以了?沒錯
接來下修改一下代碼,刪掉onload代碼,將onload里面的代碼寫在服務器端.將前臺請求成功要做的事情交給后臺來做.
btn.onclick = function(){ let script = document.createElement("script"); script.src = "/pay"; document.body.appendChild(script);//這句話一定要加上,這是與使用圖片請求不一樣的地方 //刪除onload代碼,交給后臺來做 script.onerror = function(){//返回碼是4開頭 alert("付錢失敗"); } }
后臺只需要將金額數量減1即可,不需要刷新,增加用戶體驗
else if(path === "/pay"){//如果請求的路徑是pay,接受script的請求 var amount = fs.readFileSync("./db","utf-8") var newAmount = amount-1; if(Math.random()>0.5){//模擬成功或失敗 fs.writeFileSync("./db",newAmount);//成功了就把數據寫入數據庫 response.setHeader("Content-Type","applacation/javascript")//設置返回文件類型為javascript response.statusCode = 200;//返回碼為200,說明成功 response.write("amount.innerText = amount.innerText-1")//響應內容為創建的script標簽里面的內容,返回后會立即執行********* }else{ response.statusCode = 400;//否則返回碼為400,說明失敗 response.write("fail") } response.end() }else
最后在做一件事情
因為每次不管成功失敗,都會創建一個script標簽,那么可否在請求結束后,將標簽刪除?
可以.
修改前臺onload和onerror代碼即可,不管請求成功失敗,都移除script標簽
script.onload = function(e){ e.currentTarget.remove();//添加移除代碼 } script.onerror = function(e){ e.currentTarget.remove(); }3.2.2總結 SRJ(server rrendered javascript--服務器返回的javascript)
以上方法就叫做SRJ,服務器返回的javascript,即不是在前臺寫的javascript
這是在ajax出現之前,后端程序員想出來的前后臺無刷新,局部更新頁面內容的交互方法
script請求的內容不受域名限制.當前網站的可以請求其他網站的js代碼,例如jquery
可以引入jquery
所以剛才的SRJ很危險,假如只用了get方法,那么像付錢這種操作很容易被偽裝成功,導致別人的惡意攻擊
例如我進入一個釣魚網站
那個網站直接請求
script.src = "http://alipay.com/pay?usname=mataotao&amount=100000"
這樣只要進入網站執行這個請求,豈不是可以隨意給自己的賬戶打錢?
所以只用get不安全.
所以很多危險操作都得用POST請求,例如打錢必須驗證用戶,做各種支付相關的安全防范,手機驗證碼之類的.
下面我們模仿一下不同的網站請求對方的后臺的過程
首先修改一下本地的hosts文件,將兩個域名都指向127.0.0.1,假裝成兩個不同的網站
hosts文件相當于一個個人的DNS,當你訪問某個域名時,是先通過hosts進行解析的,沒有找到才進一步通過外網DNS服務器進行解析。
如果是windows系統:
windows系統hosts文件位置及操作
如果是linux或者Unix
地址為/etc/hosts
添加代碼,那么如果我在本地訪問mataotao.com和jack.com,他首先會進127.0.0.1,而不是去外網解析dns
這樣我們就假裝成為了兩個網站,實際上兩個網站的內容是一樣的,知識用來做實驗
然后開兩個服務器,使用兩個不同的端口來監聽(模擬兩個不同的網站都開啟了服務器,都在監聽)
監聽8001
監聽8002
這樣我們在地址欄輸入mataotao.com:8001就進入了第一個網站,在地址欄輸入jack.com:8002
就進入了第二個網站.
這樣兩個個網站都可以接受請求,都可以訪問,只不過內容一樣而已
那么接下來我們來模仿不同網站之間的請求互動
修改前端SRJ請求代碼的請求路徑,改為script.src = "http://jack.com:8002/pay";
那么我們打開mataotao.com:8001就進入了第一個網站,點擊按鈕后,意思就是請求http://jack.com:8002/pay這個第二個網站的地址.我們來試一試
結果如圖所示
這是成功的時候
可以看到,兩個完全不同的網站,他們兩個是可以互相調script的
這時候的意思是:我是用第一個網站(mataotao.com)打開了第二個網站(jack.com)的付錢功能!
(由于我為了演示,這兩個網站用了同樣的代碼,同一個數據庫,我們假設第一個網站he第二個網站代碼不同)
這時候出現一個問題:
jack.com的后端程序員需要對mataotao.com的頁面細節了解的很清楚.
這樣的缺點是后臺對前臺需要了解得太深入了.比如說你是node或者php騰訊后端程序員,可是我竟然還要對阿里巴巴網站的一個按鈕的一個細節非常了解.耦合度太高,前后端彼此之間關系太緊密了.如果后端程序員對頁面細節不了解,代碼就寫不下去了.這樣對前后端分離不友好
解耦(解決耦合問題)
解決方法:
后臺程序員:我只把我后臺應該改的數據庫,然后我寫一個callback函數,將成功和失敗的狀態傳遞給前臺.這個函數就是我后端程序員做完這些事情成功之后要前端程序員去做的事情.然后執行這個函數,這個函數的內容交給前端去寫.
前端程序員:我不管后端如何修改數據庫,我事先寫好成功和失敗的函數,成功了就返回給我一個成功的提示,然后我根據提示執行成功的函數,如果給我的提示是失敗,就執行失敗的函數.
接下來寫代碼理解:
修改服務器端的代碼:
response.write(`${query.callbackName}.call(undefined,"success")`)//先獲取查詢字符串里的callbackName即從前臺傳過來的回調函數的函數名,然后再執行他,并把函數的參數定為success
添加前臺index.html的代碼
添加的這幾句代碼的解釋:
首先
script.src = "http://jack.com:8002/pay?callbackName=xxx";
在請求時帶上查詢參數,指定callbackName為xxx,以便給后臺程序員調用
接著
response.write(`${query.callbackName}.call(undefined,"success")`)
后臺程序員獲得前太程序員的查詢參數,并執行名為查詢參數的函數xxx
執行xxx,并且將相應的內容作為回調函數的第一個參數傳回去,這個參數為字符串"success"
window.xxx = function(result){ alert("我是前端程序員寫的函數,經過后端程序員調用才執行") alert(`后端返回回來的函數參數是:${result}`) }
執行結果:
也可以根據后臺傳過來的參數,修改前臺的邏輯
window.xxx = function(result){ if(result === "success"){ amount.innerText = amount.innerText - 1; } }
那么如果我們返回的參數不是字符串,而是對象呢?
這也是可以的,例如我們修改一下代碼
else if(path === "/pay"){//如果請求的路徑是pay,接受script的請求 var amount = fs.readFileSync("./db","utf-8") var newAmount = amount-1; if(Math.random()>0.5){//模擬成功或失敗 fs.writeFileSync("./db",newAmount);//成功了就把數據寫入數據庫 response.setHeader("Content-Type","applacation/javascript")//設置返回文件類型為javascript response.statusCode = 200;//返回碼為200,說明成功 response.write(`${query.callbackName}.call(undefined,{ "success":true, "left":${newAmount} })`)//把函數的參數定為一個對象 }else{ response.statusCode = 400;//否則返回碼為400,說明失敗 response.write("fail") } response.end() }
前端:
window.xxx = function(result){ if(result.success === true){ amount.innerText = amount.innerText - 1; } }5.2jsonp的含義和理解
我請求一個script,script調用一個函數的同時,把第一個參數設置為要返回的數據,不管這個數據是對象還是字符串.然后人們把這種調用script并返回字符串或者對象的使用方法叫做是jsonp,這就是jsonp!.只是名字比較奇怪,其實真名的名字應該是:動態標簽跨域請求!即利用動態標簽進行跨域請求的技術
這種方法的好處是可以解決跨域的問題.例如剛才的例子,兩個網站如果域名不一樣,那么第一個網址的前臺和另一個網址后臺的服務器無法進行數據交換(由于瀏覽器的同源策略).但是使用script標簽就可以避免這個問題.利用script請求,可以請求道第二個網站的json或字符串數據,這種方法就叫做jsonp
JSONP(JSON with Padding)是JSON的一種“使用模式”,可用于解決主流瀏覽器的跨域數據訪問的問題。由于同源策略,一般來說位于 server1.example.com 的網頁無法與不是 server1.example.com的服務器溝通,而 HTML 的
response.write(`${query.callback}.call(undefined,{ "success":true, "left":${newAmount} })`)
1隨機生成的查詢字符串
2返回的根據隨機生成的函數名的數據
jQuery使用jsonp非常簡單
只要這樣修改前臺的代碼.后臺不用改 url不需要寫callback查詢參數,因為jQuery會自動給你生成
btn.onclick = function () { $.ajax({ url: "http://jack.com:8002/pay", jsonp: "callback", dataType: "jsonp", success: function (response) { if (response.success) { amount.innerText = amount.innerText - 1; } } }); }
需要注意的一點是,jsonp不是ajax中的一種.不要背jquery誤導
7 面試題:jsonp為什么不能用post請求答:
jsonp是通過動態創建script實現的
動態創建script的時候只能用get,沒有辦法用post
6 什么jsonJSON 語法
6.1JSON 語法規則在 JS 語言中,一切都是對象。因此,任何支持的類型都可以通過 JSON 來表示,例如字符串、數字、對象、數組等。但是對象和數組是比較特殊且常用的兩種類型:
對象表示為鍵值對
數據由逗號分隔
花括號保存對象
方括號保存數組
6.2JSON 鍵/值對JSON 鍵值對是用來保存 JS 對象的一種方式,和 JS 對象的寫法也大同小異,鍵/值對組合中的鍵名寫在前面并用雙引號 "" 包裹,使用冒號 : 分隔,然后緊接著值:
{"firstName": "Json"}
這很容易理解,等價于這條 JavaScript 語句:
{firstName : "Json"}6.3 JSON 與 JS 對象的關系
很多人搞不清楚 JSON 和 Js 對象的關系,甚至連誰是誰都不清楚。其實,可以這么理解:
JSON 是 JS 對象的字符串表示法,它使用文本表示一個 JS 對象的信息,本質是一個字符串。
如
var obj = {a: "Hello", b: "World"}; //這是一個對象,注意鍵名也是可以使用引號包裹的
var json = "{"a": "Hello", "b": "World"}"; //這是一個 JSON 字符串,本質是一個字符串6.4JSON 和 JS 對象互轉
要實現從對象轉換為 JSON 字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: "Hello", b: "World"}); //結果是 "{"a": "Hello", "b": "World"}"
要實現從 JSON 轉換為對象,使用 JSON.parse() 方法:
var obj = JSON.parse("{"a": "Hello", "b": "World"}"); //結果是 {a: "Hello", b: "World"}
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/96367.html
摘要:同源策略限制了我們無法通過原生的對象獲取到數據。的原理其實不復雜瀏覽器的同源策略把跨域請求都禁止了的標簽是例外,可以突破同源策略從其他來源獲取數據由上可得,我們可以通過標簽引入文件,然后通過一系列操作獲取數據。上面三點便是實現跨域的原理。 今天做頁面時,后臺給了個接口:https://a.a.com/a/a.json,我頁面的上線地址是:http://b.b.com。顯而易見,因為瀏覽...
摘要:同源策略在中有一個很重要的安全性限制,被稱為同源策略。然而,當進行一些比較深入的前端編程的時候,不可避免地需要進行跨域操作,這時候同源策略就顯得過于苛刻。 JSONP原理 JSON和JSONP JSON(JavaScript Object Notation)是一種輕量級的數據交換格式。對于JSON大家應該是很了解了吧,不是很清楚的朋友可以去json.org上了解下,簡單易懂。 ...
摘要:概述是一種跨域通信的手段,它的原理其實很簡單首先是利用標簽的屬性來實現跨域。可靠的實現添加回調函數拼接傳遞的是一個匿名的回調函數,要執行的話,暴露為一個全局方法出錯處理使用示例來源個人博客 1. 概述 jsonp是一種跨域通信的手段,它的原理其實很簡單: 首先是利用script標簽的src屬性來實現跨域。 通過將前端方法作為參數傳遞到服務器端,然后由服務器端注入參數之后再返回,實現服...
摘要:什么叫是填充式或參數式的簡寫,是通過請求跨域接口,獲取數據的新實現方式的實現原理動態創建標簽,因為標簽是沒有同源策略限制,可以跨域的。具體看接下來的實現這個是庫的具體實現,建議下載來研究一下,最好自己動手寫一遍。 什么叫jsonp? jsonp是json with padding(填充式json或參數式json)的簡寫,是通過ajax請求跨域接口,獲取數據的新實現方式 jsonp的實現...
摘要:因為同源策略的限制,我們不能在與外部服務器進行通信的時候使用。這個是跨域服務器取數據的接口,參數為回調函數的名字,返回的格式為原理首先在客戶端注冊一個然后把的名字傳給服務器。 一、同源策略 同源策略,它是由Netscape提出的一個著名的安全策略,現在所有的可支持javascript的瀏覽器都會使用這個策略。 為什么需要同源策略,這里舉個例子: 假設現在沒有同源策略,會發生什么事...
閱讀 2293·2021-11-15 11:37
閱讀 2962·2021-09-01 10:41
閱讀 797·2019-12-27 11:58
閱讀 753·2019-08-30 15:54
閱讀 719·2019-08-30 13:52
閱讀 2936·2019-08-29 12:22
閱讀 1080·2019-08-28 18:27
閱讀 1458·2019-08-26 18:42