js跨域的幾種解決方案
一、什么是跨域?
指的是瀏覽器不能執(zhí)行其他網(wǎng)站的腳本,簡單來說是瀏覽器同源政策的限制,瀏覽器針對于ajax的限制。
同源政策
兩個頁面擁有相同的 協(xié)議,域名,端口 就是同源,如果有一個不相同就是不同源。
同源政策產(chǎn)生的目的
保護用戶信息安全,防止一些網(wǎng)站盜取用戶信息。
常見跨域場景
二、跨域解決方案
1、通過jsonp跨域
2、跨域資源共享(CORS)
3、document.domain + iframe跨域
4、location.hash + iframe
5、window.name + iframe跨域
6、postMessage跨域
7、nginx代理跨域
8、nodejs中間件代理跨域(非vue、vue(webpack--proxy代理))
9、WebSocket協(xié)議跨域
方案一:jsonp (json with padding)
原理:(回調函數(shù))
1.動態(tài)創(chuàng)建一個script標簽(無跨域限制)
2.通過srcipt標簽里的src屬性 進行 跨域訪問 屬性里面寫 跨域的地址+ ?callback屬性作為函數(shù)傳遞給后端
3.服務端通過接收客戶端傳過來的callback屬性值對應的函數(shù)
4.要發(fā)生過去的數(shù)據(jù) 通過函數(shù)的參數(shù)傳遞過去 (JSON格式)
5.將函數(shù)名對應調用的執(zhí)行代碼返回給客戶端(服務器端響應數(shù)據(jù)一定是函數(shù)代碼的調用,當瀏覽器對script將響應內容加載完成后,會自動調用函數(shù))
6.并且在服務器端的執(zhí)行函數(shù)是字符串,字符串包裹著函數(shù)調用的代碼,如果沒有包裹,則就在服務端立即執(zhí)行。
缺點:只能發(fā)送get請求,易受到 XSS攻擊
客戶端代碼
// 1.封裝一個jsonp函數(shù); jsonp({ // method: 'GET',// 所有的jsonp請求都是get請求,所以這個屬性可以不寫了 // data: , // 寫了以后太繁瑣,取消 url: 'http://www.localhost:3006/api/jsonp', success: function (res) { console.log(res) } }) // 封裝 function jsonp(obj) { // 1.創(chuàng)建一個script標簽; 2.改變src 3.給函數(shù)起名字,定義為全局函數(shù); var script = document.createElement("script"); // 不要用innerHTML, 他不會自動發(fā)送請求 // 3.給函數(shù)起名字,定義為全局函數(shù); var fnName = "haha_123123"; // window.aaa就是把aaa設置為全局變量! window[fnName] = obj.success; // 2.改變src,添加到head中 script.src = obj.url + "?callback=" + fnName; // 把script標簽添加到head標簽中,就會發(fā)送src的請求了 document.head.appendChild(script); // 代碼執(zhí)行完畢,把script標簽刪除 script.onload = function () { document.head.removeChild(script); window[fnName] = undefined; } }
服務端代碼
app.get("/api/jsonp", (req, res) => { // 獲取函數(shù)名,設置對象,發(fā)送給客戶端 const fnName = req.query.callback; // 定義發(fā)送給客戶端的對象轉換為json字符串 var objStr = JSON.stringify({ name: '張三', age: 18 }); // 字符串類型的執(zhí)行函數(shù) res.send(`${fnName}(${objStr})`); });
vue解決跨域問題- vue-jsonp
// 第一步:在vue項目中安裝上vue-jsonp npm i vue-jsonp --save // 第二步:在vue項目中main.js導入 import { VueJsonp } from 'vue-jsonp' Vue.use(VueJsonp) // 使用示例: this.$jsonp(url, { id: '1', output: "jsonp" }).then(res => { console.log('res', res) }).catch(err => { console.log(err) })
方案二:CORS (CROSS-Origin Resource Sharing 跨域資源共享)
原理:服務器設置
同源政策默認阻止跨域獲取資源,但是CORS給了web服務器權限,即服務器可以選擇,允許訪問他們的資源 。
CORS需要瀏覽器和服務器同時支持,才可以實現(xiàn)跨域請求,目前幾乎所有瀏覽器都支持CORS,IE則不能低于IE10。CORS的整個過程都由瀏覽器自動完成,前端無需做任何設置,跟平時發(fā)送ajax請求并無差異。so,實現(xiàn)CORS的關鍵在于服務器,只要服務器實現(xiàn)CORS接口,就可以實現(xiàn)跨域通信。
缺點:忽略 cookie,瀏覽器版本有一定要求
屬于跨源 AJAX 請求的根本解決方法。相比 JSONP 只能發(fā)GET
請求,CORS 允許任何類型的請求。
分類:簡單請求和復雜請求
條件:
1、請求方式:HEAD、GET、POST
2、請求頭信息:
Accept Accept-Language Content-Language Last-Event-ID Content-Type 對應的值是以下三個中的任意一個 application/x-www-form-urlencoded multipart/form-data text/plain
注意:同時滿足以上兩個條件時,則是簡單請求,否則為復雜請求
簡單請求和復雜請求的區(qū)別:
簡單請求:一次請求
復雜請求:兩次請求,在發(fā)送數(shù)據(jù)之前會先發(fā)第一次請求做‘預檢’,只有‘預檢’通過后才再發(fā)送一次請求用于數(shù)據(jù)傳輸。
關于預檢:
在發(fā)送真正的請求之前,會默認發(fā)送一個options請求,做預檢,預檢成功后才發(fā)送真正的請求。
- 請求方式:OPTIONS
- “預檢”其實做檢查,檢查如果通過則允許傳輸數(shù)據(jù),檢查不通過則不再發(fā)送真正想要發(fā)送的消息
- 如何“預檢”
=> 如果復雜請求是PUT等請求,則服務端需要設置允許某請求,否則“預檢”不通過
Access-Control-Request-Method
=> 如果復雜請求設置了請求頭,則服務端需要設置允許某請求頭,否則“預檢”不通過
Access-Control-Request-Headers
實現(xiàn):設置三個響應頭
1.Access-Control-Allow-Origin 字段 (指定了哪些域名或ip地址可以跨域 也可以寫* 代表允許所有地址;允許跨域訪問的域名:若有端口需寫全(協(xié)議+域名+端口),若沒有端口末尾不用加'/')
response.setHeater('Access-Control-Allow-Origin',' http://XXX.cn || * || ip地址 ');
2.CORS默認支持9個請求頭,如果在9個請求頭之外 要單獨設置,多個請求頭之間要用英文逗號隔開。
// 提示OPTIONS預檢時,后端需要設置的兩個常用自定義頭 response.setHeater('Access-Control-Allow-Headers','Content-Type,X-Requested-With');
3.默認情況下CORS僅支持客戶端發(fā)起的GET、POST、HEAD請求,如果希望客戶端發(fā)送PUT、DELETE請求需要在服務器端設置
response.setHeater('Access-Control-Allow-Method','PUT');
// 允許前端帶認證cookie:啟用此項后,上面的域名不能為'*',必須指定具體的域名,否則瀏覽器會提示 response.setHeader("Access-Control-Allow-Credentials", "true");
和跨域CORS有關的幾個請求頭和響應頭請求頭
請求頭 | 響應頭 |
Origin | Access-Control-Allow-Origin |
Access-Control-Request-Headers | Access-Control-Allow-Headers |
Access-Control-Request-Method | Access-Control-Allow-Methods |
Access-Control-Allow-Credentials | |
Access-Control-Expose-Headers | |
Access-Control-Max-Age |
請求頭:
- Origin:當前請求源,和響應頭里的Access-Control-Allow-Origin 對標, 是否允許當前源訪問,Origin是不可修改的
- Access-Control-Request-Headers:本次真實請求的額外請求頭,和響應頭里的Access-Control-Allow-Headers對標,是否允許真實請求的請求頭
- Access-Control-Request-Method:本次真實請求的額外方法,和響應頭里的Access-Control-Allow-Methods對標,是否允許真實請求使用的請求方法
響應頭:
1.Access-Control-Allow-Credentials:
這里的Credentials(憑證)其意包括:Cookie ,授權標頭或 TLS 客戶端證書,默認CORS請求是不帶Cookies的,這與JSONP不同,JSONP每次請求都攜帶Cookies的,當然跨域允許帶Cookies會導致CSRF漏洞。如果非要跨域傳遞Cookies,web端需要給ajax設置withCredentials為true,同時,服務器也必須使用Access-Control-Allow-Credentials頭響應。此響應頭true意味著服務器允許cookies(或其他用戶憑據(jù))包含在跨域請求中。另外,簡單的GET請求是不預檢的,即使請求的時候設置widthCrenditials為true,如果響應頭不帶Access-Control-Allow-Credentials,則會導致整個響應資源被瀏覽器忽略。
2.Access-Control-Allow-Headers
3.Access-Control-Allow-Methods
4.Access-Control-Allow-Origin
5.Access-Control-Expose-Headers:
在CORS中,默認的,只允許客戶端讀取下面六個響應頭(在axios響應對象的headers里能看到):
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
如果這六個以外的響應頭要是想讓客戶端讀取到,就需要設置Access-Control-Expose-Headers這個為響應頭名了,比如Access-Control-Expose-Headers: Token
6. Access-Control-Max-Age:設置預檢請求的有效時長,就是服務器允許的請求方法和請求頭做個緩存。
補充:
根據(jù)頭信息和請求方式,瀏覽器進行了優(yōu)化,對請求進行劃分,分為簡單請求和非簡單請求
簡單請求和非簡單請求不做概述
簡單請求 : 滿足瀏覽器請求會直接發(fā)送 并在請求頭中攜帶Origin 表示本次來自哪個源
如果不滿足簡單請求 , 會在發(fā)送請求之前發(fā)送一個預檢請求 大小為0kb 請求方式為options 收到服務器響應后,分析它是否支持跨域,如果支持跨域則直接發(fā)送,如果不支持 報錯!
JSONP和CORS的區(qū)別:?JSONP:服務端不用修改,需要改前端。發(fā)jsonp請求?
?JSONP:只能發(fā)GET請求?
?CORS:前端的代碼不用修改,服務端的代碼需要修改。如果是簡單請求的話在服務端加上一個響應頭。?
?CORS:可以發(fā)任意請求
方案三:document.domain + iframe跨域
此方案僅限主域相同,子域不同的跨域應用場景。
通過 iframe 是瀏覽器非同源標簽,加載內容中轉,傳到當前頁面的屬性中。
實現(xiàn)原理:兩個頁面都通過js強制設置document.domain為基礎主域,就實現(xiàn)了同域。
缺點:同一一級域名;相同協(xié)議;相同端口
1.)父窗口:(http://www.domain.com/a.html)
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe> <script> document.domain = 'domain.com'; var user = 'admin'; </script>
2.)子窗口:(http://child.domain.com/b.html)
<script> document.domain = 'domain.com'; // 獲取父窗口中變量 alert('get js data from parent ---> ' + window.parent.user); </script>
方案四: location.hash + iframe跨域
實現(xiàn)原理: a欲與b跨域相互通信,通過中間頁c來實現(xiàn)。 三個頁面,不同域之間利用iframe的location.hash傳值,相同域之間直接js訪問來通信。
具體實現(xiàn):A域:a.html -> B域:b.html -> A域:c.html,a與b不同域只能通過hash值單向通信,b與c也不同域也只能單向通信,但c與a同域,所以c可通過parent.parent訪問a頁面所有對象。
1.)a.html:(http://www.domain1.com/a.html)
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); // 向b.html傳hash值 setTimeout(function() { iframe.src = iframe.src + '#user=admin'; }, 1000); // 開放給同域c.html的回調方法 function onCallback(res) { alert('data from c.html ---> ' + res); } </script>
2.)b.html:(http://www.domain2.com/b.html)
<iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); // 監(jiān)聽a.html傳來的hash值,再傳給c.html window.onhashchange = function () { iframe.src = iframe.src + location.hash; }; </script>
3.)c.html:(http://www.domain1.com/c.html)
<script> // 監(jiān)聽b.html傳來的hash值 window.onhashchange = function () { // 再通過操作同域a.html的js回調,將結果傳回 window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', '')); }; </script>
方案五:window.name + iframe跨域
window.name屬性的獨特之處:name值在不同的頁面(甚至不同域名)加載后依舊存在,并且可以支持非常長的 name 值(2MB)。
缺點:頁面的屬性值有大小限制
1.)a.html:(http://www.domain1.com/a.html)
var proxy = function(url, callback) { var state = 0; var iframe = document.createElement('iframe'); // 加載跨域頁面 iframe.src = url; // onload事件會觸發(fā)2次,第1次加載跨域頁,并留存數(shù)據(jù)于window.name iframe.onload = function() { if (state === 1) { // 第2次onload(同域proxy頁)成功后,讀取同域window.name中數(shù)據(jù) callback(iframe.contentWindow.name); destoryFrame(); } else if (state === 0) { // 第1次onload(跨域頁)成功后,切換到同域代理頁面 iframe.contentWindow.location = 'http://www.domain1.com/proxy.html'; state = 1; } }; document.body.appendChild(iframe); // 獲取數(shù)據(jù)以后銷毀這個iframe,釋放內存;這也保證了安全(不被其他域frame js訪問) function destoryFrame() { iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } } // 請求跨域b頁面數(shù)據(jù) proxy('http://www.domain2.com/b.html', function(data){ alert(data); });
2.)proxy.html:(http://www.domain1.com/proxy....中間代理頁,與a.html同域,內容為空即可。
3.)b.html:(http://www.domain2.com/b.html)
<script> window.name = 'This is domain2 data!'; </script>
總結:通過iframe的src屬性由外域轉向本地域,跨域數(shù)據(jù)即由iframe的window.name從外域傳遞到本地域。這個就巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操作。
方案六:postMessage跨域
postMessage是HTML5 XMLHttpRequest Level 2中的 API,且是為數(shù)不多可以跨域操作的window屬性之一,它可用于解決以下方面的問題:
a.) 頁面和其打開的新窗口的數(shù)據(jù)傳遞
b.) 多窗口之間消息傳遞
c.) 頁面與嵌套的iframe消息傳遞
d.) 上面三個場景的跨域數(shù)據(jù)傳遞
用法:postMessage(data,origin)方法接受兩個參數(shù)
- data: html5規(guī)范支持任意基本類型或可復制的對象,但部分瀏覽器只支持字符串,所以傳參時最好用JSON.stringify()序列化。
- origin: 協(xié)議+主機+端口號,也可以設置為"*",表示可以傳遞給任意窗口,如果要指定和當前窗口同源的話設置為"/"。
缺點:瀏覽器版本要求,部分瀏覽器要配置放開跨域限制
1.)a.html:(http://www.domain1.com/a.html)
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { var data = { name: 'aym' }; // 向domain2傳送跨域數(shù)據(jù) iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com'); // 接受domain2返回數(shù)據(jù) window.addEventListener('message', function(e) { alert('data from domain2 ---> ' + e.data); }, false); </script>
2.)b.html:(http://www.domain2.com/b.html)
<script> // 接收domain1的數(shù)據(jù) window.addEventListener('message', function(e) { alert('data from domain1 ---> ' + e.data); var data = JSON.parse(e.data); if (data) { data.number = 16; // 處理后再發(fā)回domain1 window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com'); } }, false); </script>
方案七:nginx 代理跨域(線上環(huán)境)
前端向發(fā)送請求,經(jīng)過代理,請求需要的服務器資源
缺點:需要額外的代理服務器
1、nginx配置解決iconfont跨域
瀏覽器跨域訪問js、css、img等常規(guī)靜態(tài)資源被同源策略許可,但iconfont字體文件(eot|otf|ttf|woff|svg)例外,此時可在nginx的靜態(tài)資源服務器中加入以下配置。
location / { add_header Access-Control-Allow-Origin *; }
2、nginx反向代理接口跨域
跨域原理: 同源策略是瀏覽器的安全策略,不是HTTP協(xié)議的一部分。服務器端調用HTTP接口只是使用HTTP協(xié)議,不會執(zhí)行JS腳本,不需要同源策略,也就不存在跨越問題。
實現(xiàn)思路:通過nginx配置一個代理服務器(域名與domain1相同,端口不同)做跳板機,反向代理訪問domain2接口,并且可以順便修改cookie中domain信息,方便當前域cookie寫入,實現(xiàn)跨域登錄。
nginx具體配置:
#proxy服務器 server { listen 81; server_name www.domain1.com; location / { proxy_pass http://www.domain2.com:8080; #反向代理 proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名 index index.html index.htm; # 當用webpack-dev-server等中間件代理接口訪問nignx時,此時無瀏覽器參與,故沒有同源限制,下面的跨域配置可不啟用 add_header Access-Control-Allow-Origin http://www.domain1.com; #當前端只跨域不帶cookie時,可為* add_header Access-Control-Allow-Credentials true; } }
1.) 前端代碼示例:
var xhr = new XMLHttpRequest(); // 前端開關:瀏覽器是否讀寫cookie xhr.withCredentials = true; // 訪問nginx中的代理服務器 xhr.open('get', 'http://www.domain1.com:81/?user=admin', true); xhr.send();
2.) Nodejs后臺示例:
var http = require('http'); var server = http.createServer(); var qs = require('querystring'); server.on('request', function(req, res) { var params = qs.parse(req.url.substring(2)); // 向前臺寫cookie res.writeHead(200, { 'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:腳本無法讀取 }); res.write(JSON.stringify(params)); res.end(); }); server.listen('8080'); console.log('Server is running at port 8080...');
方案八:Nodejs中間件代理跨域
node中間件實現(xiàn)跨域代理,原理大致與nginx相同,都是通過啟一個代理服務器,實現(xiàn)數(shù)據(jù)的轉發(fā),也可以通過設置cookieDomainRewrite參數(shù)修改響應頭中cookie中域名,實現(xiàn)當前域的cookie寫入,方便接口登錄認證。
1、 非vue框架的跨域(2次跨域)
利用 node + express + http-proxy-middleware 搭建一個proxy服務器。
1.)前端代碼示例:
var xhr = new XMLHttpRequest(); // 前端開關:瀏覽器是否讀寫cookie xhr.withCredentials = true; // 訪問http-proxy-middleware代理服務器 xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true); xhr.send();
2.)中間件服務器:
var express = require('express'); var proxy = require('http-proxy-middleware'); var app = express(); app.use('/', proxy({ // 代理跨域目標接口 target: 'http://www.domain2.com:8080', changeOrigin: true, // 修改響應頭信息,實現(xiàn)跨域并允許帶cookie onProxyRes: function(proxyRes, req, res) { res.header('Access-Control-Allow-Origin', 'http://www.domain1.com'); res.header('Access-Control-Allow-Credentials', 'true'); }, // 修改響應信息中的cookie域名 cookieDomainRewrite: 'www.domain1.com' // 可以為false,表示不修改 })); app.listen(3000); console.log('Proxy server is listen at port 3000...');
3.)Nodejs后臺同(七:nginx)
2、 vue框架的跨域 -- Proxy代理(1次跨域,開發(fā)環(huán)境)
通過服務器代理去發(fā)ajax 這個代理是webpack提供給我們的 因為服務器只是對ajax的同源限制,并不會限制服務器之間的通信。
利用 node + webpack + webpack-dev-server 代理接口跨域。在開發(fā)環(huán)境下,由于vue渲染服務和接口代理服務都是webpack-dev-server同一個,所以頁面與代理接口之間不再跨域,無須設置headers 跨域信息了。
使用環(huán)境:開發(fā)環(huán)境
生產(chǎn)環(huán)境:nginx
webpack.config.js / vue.config.js 部分配置:
//vue.config.js module.exports = { entry: {}, module: {}, ... devServer: { proxy: { // 如果請求地址以/api打頭,就出觸發(fā)代理機制 // http://localhost:8080/api/login -> http://localhost:3000/api/login '/api': { // 攔截以 /api 開頭的url接口 target: 'http://localhost:3000', //目標接口域名 路徑:http://localhost:3000/api changeOrigin: true, //是否跨域 ws: true, //如果要代理 websockets,配置這個參數(shù) secure: false, // 如果是https接口,需要配置這個參數(shù) // // 標識替換 // // 原請求地址為 /api/getData 將'/api'替換''時, // // 代理后的請求地址為: http://xxx.xxx.xxx/getData // // 若替換為'/other',則代理后的請求地址為 http://xxx.xxx.xxx/other/getData pathRewrite: { // 標識替換 '^/api': '/' //重寫接口 后臺接口指向不統(tǒng)一 所以指向所有/ 路徑:http://localhost:3000/ // '^/api': '/api/mock' } } }, }
方案九:WebSocket 協(xié)議跨域
- WebSocket protocol是HTML5一種新的協(xié)議(WebSocket協(xié)議本質上是一個基于tcp的協(xié)議)。
- 它實現(xiàn)了瀏覽器與服務器全雙工通信,能更好的節(jié)省服務器資源和帶寬并達到實時通訊的目的,同時允許跨域通訊,是server push技術的一種很好的實現(xiàn)。
- Websocket是一個持久化的協(xié)議
WebSocket 的原理
- WebSocket約定了一個通信的規(guī)范,通過單個TCP一個握手的機制,客戶端和服務器之間能建立一個類似tcp的連接,從而方便它們之間的通信
- 在WebSocket出現(xiàn)之前,web交互一般是基于http協(xié)議的短連接或者長連接
- WebSocket是一種全新的協(xié)議,不屬于http無狀態(tài)協(xié)議,協(xié)議名為"ws"
有什么用: WebSocket用來解決http不能持久連接的問題,因為可以雙向通信所以可以用來實現(xiàn)聊天室,以及其他由服務端主動推送的功能例如 實時天氣、股票報價、余票顯示、消息通知等。
WebSocket與http的關系
相同點:
- 都是基于tcp的,都是可靠性傳輸協(xié)議
- 都是應用層協(xié)議
不同點:
- WebSocket是單個 TCP 連接上進行雙向通信協(xié)議,模擬Socket協(xié)議,可以雙向發(fā)送或接受信息
- HTTP是單向的
- WebSocket是需要瀏覽器和服務器一次握手進行建立連接的
- 而http是瀏覽器發(fā)起向服務器的連接,服務器預先并不知道這個連接
聯(lián)系:
WebSocket在建立握手時,數(shù)據(jù)是通過HTTP傳輸?shù)?。但是建立之后,在真正傳輸時候是不需要HTTP協(xié)議的
總結(總體過程):
首先,客戶端發(fā)起http請求,經(jīng)過3次握手后,建立起TCP連接;http請求里存放WebSocket支持的版本號等信息,如:Upgrade、Connection、WebSocket-Version等;
然后,服務器收到客戶端的握手請求后,同樣采用HTTP協(xié)議回饋數(shù)據(jù);
最后,客戶端收到連接成功的消息后,開始借助于TCP傳輸信道進行全雙工通信。
WebSocket有以下特點:
- 是真正的全雙工方式,建立連接后客戶端與服務器端是完全平等的,可以互相主動請求。而HTTP長連接基于HTTP,是傳統(tǒng)的客戶端對服務器發(fā)起請求的模式。
- HTTP長連接中,每次數(shù)據(jù)交換除了真正的數(shù)據(jù)部分外,服務器和客戶端還要大量交換HTTP header,信息交換效率很低。
Websocket協(xié)議通過第一個request建立了TCP連接之后,之后交換的數(shù)據(jù)都不需要發(fā)送 HTTP header就能交換數(shù)據(jù),這顯然和原有的HTTP協(xié)議有區(qū)別所以它需要對服務器和客戶端都進行升級才能實現(xiàn)(主流瀏覽器都已支持HTML5)
var socket = new WebSocket("ws://example.com/socketserver"); socket.onopen = function(event) { socket.send("Hello server!"); }; socket.onmessage = function(event) { console.log("Received message from server: " + event.data); }; socket.onerror = function(event) { console.log("WebSocket error: " + event.error); }; socket.onclose = function(event) { console.log("WebSocket connection closed with code " + event.code); };
在上面的代碼中,首先創(chuàng)建了一個 WebSocket 對象,指定了服務器的地址。然后在 onopen 回調函數(shù)中,發(fā)送了一個消息到服務器。當服務器發(fā)送消息到客戶端時,onmessage 回調函數(shù)會被觸發(fā),從而可以處理服務器發(fā)送的消息。如果出現(xiàn)錯誤或者連接被關閉,onerror 和 onclose 回調函數(shù)會被觸發(fā),從而可以處理這些事件。
需要注意的是,在使用 WebSocket 發(fā)送消息之前,必須先建立 WebSocket 連接。在上面的代碼中,通過創(chuàng)建一個 WebSocket 對象來建立連接,然后在 onopen 回調函數(shù)中發(fā)送消息到服務器。如果在連接建立之前就嘗試發(fā)送消息,那么這些消息將無法發(fā)送成功。
vue怎么用websocket發(fā)送請求
<template> <div> <input v-model="message" @keyup.enter="sendMessage"> <button @click="sendMessage">Send</button> <ul> <li v-for="msg in messages">{{ msg }}</li> </ul> </div> </template> <script> export default { data() { return { message: '', messages: [], socket: null } }, created() { this.socket = new WebSocket('ws://localhost:3000') // 監(jiān)聽服務器端響應處理函數(shù) this.socket.addEventListener('message', this.handleMessage) /* this.$once('hook:beforeDestroy',() => { // 關閉連接 this.socket.close() }) */ }, beforeDestroy() { // 關閉連接 this.socket.close() }, methods: { sendMessage() { // 向服務器端發(fā)送消息 this.socket.send(this.message) this.message = '' }, handleMessage(event) { this.messages.push(event.data) } } } </script>
在上面的代碼中,
- 首先在 created 鉤子函數(shù)中創(chuàng)建了一個 WebSocket 對象,并添加了一個消息處理函數(shù) handleMessage。
- 在 sendMessage 方法中,將用戶輸入的消息發(fā)送到服務器。
- 在 handleMessage 方法中,將從服務器接收到的消息添加到 messages 數(shù)組中,從而可以在界面上顯示出來。
- 在組件銷毀之前,需要調用 close() 方法關閉 WebSocket 連接。
需要注意的是,在使用 WebSocket 時,需要考慮跨域問題。如果 WebSocket 連接的 URL 和當前頁面的 URL 不同,那么需要在服務器端進行相應的跨域設置。同時,需要注意在發(fā)送和接收消息時的數(shù)據(jù)格式,通常需要將數(shù)據(jù)轉換為 JSON 字符串進行傳輸也支持二進制。
原生WebSocket API 使用起來不太方便,我們使用Socket.io,它很好地封裝了webSocket接口,提供了更簡單、靈活的接口,也對不支持webSocket的瀏覽器提供了向下兼容。
缺點:瀏覽器一定版本要求,服務器需要支持 websocket 協(xié)議
1.)前端代碼:
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script> <script> // var socket = io('http://www.domain2.com:8080'); // 創(chuàng)建Socket.IO實例,建立連接 var socket= new io.Socket('localhost',{ port: 8080 }); socket.connect(); // 連接成功處理 socket.on('connect', function() { console.log('Client has connected to the server!'); }); // 監(jiān)聽服務端消息 socket.on('message', function(msg) { console.log('data from server: ---> ' + msg); }); // 監(jiān)聽服務端關閉 socket.on('disconnect', function() { console.log('Server socket has closed.'); }); document.getElementsByTagName('input')[0].onblur = function() { // 通過Socket發(fā)送一條消息到服務器 socket.send(this.value); }; </script>
2.)Nodejs socket后臺:
var http = require('http'); var socket = require('socket.io'); // 啟http服務 var server = http.createServer(function(req, res) { res.writeHead(200, { 'Content-type': 'text/html' }); res.end(); }); server.listen('8080'); console.log('Server is running at port 8080...'); // 監(jiān)聽socket連接 socket.listen(server).on('connection', function(client) { // 接收信息 client.on('message', function(msg) { client.send('hello:' + msg); console.log('data from client: ---> ' + msg); }); // 斷開處理 client.on('disconnect', function() { console.log('Client socket has closed.'); });
到此這篇關于js跨域的幾種解決方案的文章就介紹到這了,更多相關js跨域內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
解決BootStrap Fileinput手機圖片上傳顯示旋轉問題
這篇文章主要介紹了 BootStrap Fileinput手機圖片上傳顯示旋轉問題,需要的朋友可以參考下2017-06-06新發(fā)現(xiàn)一個騙鏈接的方法(js讀取cookies)
新發(fā)現(xiàn)一個騙鏈接的方法(js讀取cookies),需要的朋友可以參考下。2012-01-01JS動態(tài)修改表格cellPadding和cellSpacing的方法
這篇文章主要介紹了JS動態(tài)修改表格cellPadding和cellSpacing的方法,涉及javascript操作cellPadding和cellSpacing屬性的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-03-03