首頁 > 軟體

AJAX跨域問題解決方案詳解

2022-08-22 14:02:10

1.前言

跨域簡單的說,就是從一個域名的網頁去存取另一個域名網頁的資源。

通過超連結或者form表單提交或者window.location.href的方式進行跨域是不存在問題的。但在一個域名的網頁中的一段js程式碼傳送ajax請求去存取另一個域名中的資源,由於同源策略的存在導致無法跨域存取,那麼ajax就存在這種跨域問題。

關於同源問題,我們判斷同源從三個要素著手:協定、域名、埠號。

如果協定一致,域名一致,埠號一致,三個要素都一致,才是同源,其它一律都是不同源

接下來我們來談談ajax中存在的跨域問題如何解決。

2.解決方案

下面例子都是部署在兩個伺服器上,html程式碼是a伺服器上的內容,servlet是b伺服器上的內容。

2.1 設定響應頭

這個比較簡單,只需要在跨域存取資源的Servlet中新增程式碼:

response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080"); // 允許某個
response.setHeader("Access-Control-Allow-Origin", "*"); // 允許所有

2.2 jsonp

jsonp是一種類AJAX的請求機制,同樣可以完成區域性重新整理的效果。但是jsonp只支援GET請求方式。

2.2.1 前端程式碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp跨域</title>
</head>
<body>
<!-- 下面一行的程式碼效果是和下面22-28行的程式碼一樣的 -->
<!--<script type="text/javascript" src="http://localhost:8081/b/jsonp2?fun=sayHello"></script>-->
<script type="text/javascript">
  // data是一個json:{"username" : "lucy"}
  function sayHello(data){ 
    document.getElementById("mydiv").innerHTML = data.username
  }
  window.onload = () => {
    document.getElementById("btn").onclick = () => {
      // 載入script元素
      // 建立script元素物件
      const htmlScriptElement = document.createElement("script");
      // 設定script的type屬性
      htmlScriptElement.type = "text/javascript"
      // 設定script的src屬性
      htmlScriptElement.src = "http://localhost:8081/b/jsonp2?fun=sayHello"
      // 將script物件新增到body標籤中(這一步就是載入script)
      document.getElementsByTagName("body")[0].appendChild(htmlScriptElement)
    }
  }
</script>
<button id="btn">jsonp解決跨域問題,達到ajax區域性重新整理的效果</button>
<div id="mydiv"></div>
</body>
</html>

2.2.2 後端程式碼

package com.bjpowernode.b.web.servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/jsonp2")
public class JSONPServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 獲取函數名
        String fun = request.getParameter("fun");
        // 響應一段js程式碼
        response.getWriter().print(fun + "({"username" : "lucy"})");
    }
}

2.3 使用jQuery封裝的jsonp

jQuery中的jsonp其實就是我們上面程式碼的高度封裝,底層原理完全相同。

核心程式碼:

$.ajax({
    type : "GET",
    url : "跨域的url",
    dataType : "jsonp", // 指定資料型別
    jsonp : "fun", // 指定引數名(不設定的時候,預設是:"callback")
    jsonpCallback : "sayHello" // 指定回撥函數的名字
							   // (不設定的時候,jQuery會自動生成一個隨機的回撥函數,
    						   //並且這個回撥函數還會自動呼叫success的回撥函數。)
})

後端程式碼同上。

2.4 代理機制(httpclient)

使用Java程式傳送get/post請求這裡有兩種方案:

  • 第一種方案:使用JDK內建的API(java.net.URL…),這些API是可以傳送HTTP請求的。
  • 第二種方案:使用第三方的開源元件,比如:apache的httpclient元件。(httpclient元件是開源免費的,可以直接用)

這裡我們說第二種方案。

2.4.1 前端程式碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用代理機制完成ajax跨域存取</title>
</head>
<body>
<script type="text/javascript">
    // ES6當中的有一個新語法:箭頭函數。
    window.onload = () => {
        document.getElementById("btn").onclick = () => {
            // 傳送ajax請求
            // 1.建立核心物件
            const xmlHttpRequest = new XMLHttpRequest(); // const可以宣告變數。(可以自己研究一下:var let const宣告變數時有什麼區別)
            // 2.註冊回撥函數
            xmlHttpRequest.onreadystatechange = () => {
                if (xmlHttpRequest.readyState == 4) {
                    // 這裡也可以使用區間的方式,因為狀態碼是200~299都是正常響應結束。
                    if (xmlHttpRequest.status >= 200 && xmlHttpRequest.status < 300) {
                        document.getElementById("mydiv").innerHTML = xmlHttpRequest.responseText
                    }
                }
            }
            // 3.開啟通道
            xmlHttpRequest.open("GET", "/a/proxy", true)
            // 4.傳送請求
            xmlHttpRequest.send()
        }
    }
</script>
<button id="btn">使用代理機制解決ajax跨域存取</button>
<div id="mydiv"></div>
</body>
</html>

2.4.2 代理Servlet程式碼

這一部分的程式碼基本上都是模板套用,改改具體引數就好了。

package com.bjpowernode.javaweb.servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@WebServlet("/proxy")
public class ProxyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 通過httpclient元件,傳送HTTP GET請求,存取 TargetServlet
        HttpGet httpGet = new HttpGet("http://localhost:8081/b/target");
        httpGet.setHeader("Content-Type", "application/x-www-form-urlencoded");
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpResponse resp = httpClient.execute(httpGet);
        HttpEntity entity = resp.getEntity();
        BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
        String line = null;
        StringBuffer responseSB = new StringBuffer();
        while ((line = reader.readLine()) != null) {
            responseSB.append(line);
        }
        reader.close();
        httpClient.close();
        // b站點響應回來的資料
        response.getWriter().print(responseSB);
    }
}

2.4.3 目標Servlet程式碼

package com.bjpowernode.b.web.servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/target")
public class TargetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 響應一個json字串。
        response.getWriter().print("{"username":"jackson"}");
    }
}

2.4.4 圖示

2.5 nginx反向代理

nginx反向代理中也是使用了這種代理機制來完成AJAX的跨域,實現起來非常簡單,只要修改一個nginx的設定即可。這個再說。

到此這篇關於AJAX跨域問題解決方案詳解的文章就介紹到這了,更多相關AJAX跨域內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com