Ajax请求跨域

什么是ajax跨域问题

ajax出现请求跨域错误问题 , 主要是因为浏览器们基于用户信息安全考虑 所使用 “同源政策”

那什么是“同源政策”呢 ?

就是限制 :1.协议相同 2.域名相同(一级域名相同也不行) 3.端口相同

如何解决ajax跨域问题呢?

  1. 古老的JSONP方式

  2. CORS方式

  3. 代理请求方式 (nginx 反向代理 )

JSONP方式

jsonp解决跨域问题是一个比较古老的方案(实际中不推荐使用), 实际项目中如果要使用JSONP,一般会使用JQuery对JSONP进行了封装的类库来进行ajax请求

jsonp、json,ajax的关系

肯定的说没什么关系 , 名字有点像而已 。打个比方,JSON是地下党们用来书写和交换情报的“暗号”,而JSONP则是把用暗号书写的情报传递给自己同志时使用的接头方式。看到没?一个是描述信息的格式,一个是信息传递双方约定的方法。(这段话来自网络);

  1. json 是一种非常友好的数据交换格式

  2. jsonp是一种投机的约定协议 (非官方)

  3. 另外我们现在JQuery ajax 中看到的 dataType: "jsonp " 也是JQuery为了方便Ajax跨域调用封装进去的

  4. jsonp和ajax

    • ajax和jsonp的调用方式很像,都是请求url,然后把返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装

    • ajax的核心是通过xmlHttpRequest获取非本页内容

    • jsonp的核心是动态添加script标签调用服务器生成的js脚本(用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript)

    • ajax通过服务端代理一样跨域,jsonp也不并不排斥同源的数据的获取

jsonp 原理和由来

  1. 由于 “同源政策” 限制

  2. 但是又有像img、script、iframe这类可以指定src属性的标签 且天生具有跨域获取别人网站上数据(图片,脚本,源文件其实都是数据)的能力

    eg.:

    <!--京东商品图片-->
    <img src="http://img30.360buyimg.com/uuid.jpg" />
    <!--百度CDN-->
    <script src="http://apps.bdimg.com/libs/jquery/jquery.min.js"></script>
    
  3. 有了 src 这条“潜规则” 跨域访问数据,只要在远程服务器上设法将json数据封装进js格式的文件中 ,供客户端调用和进一步处理,这就是jsonp协议的原理 。所谓js格式就是这样:

    callback({"name":"小贱西风","hobby":"bugs","sex":******});
    
  4. 虽然不能用 src 属性直接获取 json 数据,但是双方可以建立一个潜规则(也就是jsonp,算一种协议吧)

    JQuery中ajax(JSONP)跨域使用 eg.

    $.ajax({  
            url: "http://remotehost:port/ajax/jsonp/restful",   //后台给的接口
            type:"get",//请求方式 ajax跨域仅支持get,写上post也是走的get,而且如果后台限制了method还会报405(Method Not Allowed)
            dataType: "jsonp",//一定为jsonp哦,json不支持跨域
            jsonp: "callback",//传递给后台,用以获得jsonp回调函数名的参数名(一般默认为:callback)				
            jsonpCallback: "data", // 自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据     				
            data: {		//后台需要的数据
                username:"wesley",
                password:"test"
            },
            success: back,
        	error: errorback
        })
    
    function back(data) {
        console.log("jsonpCallback输出:"+ data)// 后台返回的数据
    }
    
    

    原生js方式

    <script>
             //回调函数
             function jsonpCallback (result) {
                 var data = JSON.stringify(result); //json对象转成字符串
                 $("#text").val(data);// 处理jsonp返回数据
             }
    		// script脚本
     		var script_element = "<script src='http://remotehost:port/ajax/jsonp/restful?callback=jsonpCallback'><\/script>";
             $(document).ready(function () {
                 $("#btn").click(function () {
                     //在头部动态创建一个脚本,该脚本发起一个跨域请求
                     $("head").append(script_element);
                 });
             });
     </script>
    

    后台代码

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws Exception {
         response.setCharacterEncoding("UTF-8");
         response.setContentType("text/html;charset=UTF-8");
         //获取数据
         List<Data> datas = getDatas();
         JSONArray jsonArray = JSONArray.fromObject(datas);
         String result = jsonArray.toString();
         //前端传过来的回调函数名称,如上面jquery方式中对应的 【jsonp: "callback"】
         String callback = request.getParameter("callback");
         //用回调函数名称包裹返回数据,这样,返回数据就作为回调函数的参数传回去了
         result = callback + "(" + result + ")";
         response.getWriter().write(result);
     }
    

淘汰原因

基于JSONP的实现原理,所以JSONP只能是“GET”请求,不能进行较为复杂的POST和其它请求,所以遇到那种情况,就得参考下面的CORS解决跨域了

CORS方式

主要是由后端来解决

cors原理

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能 同源 使用的限制

详细请参考 (阮一峰)跨域资源共享 CORS 详解

后台配置

  • Xml Filter
<!-- 1.在项目或模块pom中引入依赖 -->
<dependency>
    <groupId>com.thetransactioncompany</groupId>
    <artifactId>cors-filter</artifactId>
    <version>version</version>
</dependency>

<!-- 2.在项目或模块 WEB-INF/web.xml中加入以下配置 -->
<filter>
	<!-- The CORS filter with parameters -->
	<filter-name>CORS</filter-name>
	<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>

	<!-- Note: All parameters are options, if omitted the CORS 
             Filter will fall back to the respective default values.
          -->
	<init-param>
		<param-name>cors.allowGenericHttpRequests</param-name>
		<param-value>true</param-value>
	</init-param>

	<init-param>
		<param-name>cors.allowOrigin</param-name>
		<param-value>*</param-value>
	</init-param>

	<init-param>
		<param-name>cors.allowSubdomains</param-name>
		<param-value>false</param-value>
	</init-param>

	<init-param>
		<param-name>cors.supportedMethods</param-name>
		<param-value>GET, HEAD, POST, OPTIONS</param-value>
	</init-param>

	<init-param>
		<param-name>cors.supportedHeaders</param-name>
		<param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value>
	</init-param>

	<init-param>
		<param-name>cors.exposedHeaders</param-name>
		<!--这里可以添加一些自己的暴露Headers   -->
		<param-value>X-Test-1, X-Test-2</param-value>
	</init-param>

	<init-param>
		<param-name>cors.supportsCredentials</param-name>
		<param-value>true</param-value>
	</init-param>

	<init-param>
		<param-name>cors.maxAge</param-name>
		<param-value>3600</param-value>
	</init-param>

</filter>

<filter-mapping>
	<!-- CORS Filter mapping -->
	<filter-name>CORS</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

  • Spring Boot
@Configuration
public class CorsConfig {

    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 可以自行可选配置
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig());
        return new CorsFilter(source);  
    }
}

代理请求方式

与前面的方法不同,前面CORS是后端解决,而这个主要是前端对接口进行代理,也就是

  • 前端ajax请求的是本地接口
  • 本地接口接收到请求后向实际的接口请求数据,然后再将信息返回给前端
  • 下面说下用nginx代理

修改本地接口所在服务的nginx配置 , 所有/apis/打头的接口,全部去请求www.remotehost.com

## 匹配以/apis/开头的请求
location ^~/apis {
	rewrite  ^.+apis/?(.*)$ /$1 break;
	include  uwsgi_params;
	##后面加不加 / 是有区别的
	proxy_pass   http://www.remotehost.com/;
}
posted @ 2021-06-27 15:54  沉梦匠心  阅读(226)  评论(0)    收藏  举报