沃梦达 / IT编程 / 前端开发 / 正文

AJAX跨域问题解决方案详解

从刚接触前端开发起,跨域这个词就一直以很高的频率在身边重复出现,一直到现在,已经调试过N个跨域相关的问题了,这篇文章主要介绍了AJAX跨域问题的解决方案

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="aHR0cDovL2xvY2FsaG9zdDo4MDgxL2IvanNvbnAyP2Z1bj1zYXlIZWxsbw=="></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 = "aHR0cDovL2xvY2FsaG9zdDo4MDgxL2IvanNvbnAyP2Z1bj1zYXlIZWxsbw=="
      // 将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 图示

IiBzcmM9

2.5 nginx反向代理

nginx反向代理中也是使用了这种代理机制来完成AJAX的跨域,实现起来非常简单,只要修改一个nginx的配置即可。这个再说。

到此这篇关于AJAX跨域问题解决方案详解的文章就介绍到这了,更多相关AJAX跨域内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!

本文标题为:AJAX跨域问题解决方案详解