jsonp跨域请求详解——从繁至简

 

什么是jsonp?为什么要用jsonp?

JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外。利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。以上是百度百科对于jsonp的解释,所谓”同源”指的是”相同协议相同端口”。

例如:h ttp://abc.com/test1 与以下url做同源检测

h ttps://abc.com/test2不同源(协议不同 http / https)

h ttp://abc.com:81/test3 不同源(端口不同 80 / 81)

h ttp://abc.com/test4 同源

注:ie”同源策略”不包括端口,即与 h ttp://abc.com:81/test3 也为同源

跨域请求的原理

我们做一个简单的测试!

我们在 h ttp://169.254.200.238:8020/jsonp/index.html下向

h ttp://169.254.200.238:8080/jsonp.do发起请求,

 $.get("http://169.254.200.238:8080/jsonp.do", function (data) {
     console.log(data);
 });

此时浏览器抛出异常


因为两者的端口号分别为8080、8020 并不同源,从报错中也可以看出。

但是,我们换一种方式请求:

 <script type="text/javascript" src="http://169.254.200.238:8080/jsonp.do">
  </script>


可以看到,此时同样的请求确成功了!由此,我们可以得出<scrpit>可以进行跨域请求,这是jsonp的基础,但是浏览器同样抛出了语句不合法的异常,


那是因为我们请求的数据会立马被浏览器当作javascript语句去执行(谁让我们用<script>去请求数据呢),但是请求到的数据格式并不符合其语法规范。

那么,如何解决这一问题呢?

如果我们返回的内容符合javascript的语法规范呢?

所以,我们把请求的数据当作一个函数的参数,并且这个函数在客户端存在的话,那么这就行得通。

例如:请求返回的数据为

 callback( {"result":"success"} )

其中{“result”:”success”} 是我们想要获取的数据,浏览器会立即执行callback这个函数,此时,我们已经定义好了函数名为callback这个函数:

function callback(data){
    // data为返回数据 
    // TODO 解析数据
}

这样是不是一切都说的通了!所以jsonp跨域请求的关键就在于:

服务端要在返回的数据外层包裹一个客户端已经定义好的函数

ajax跨域请求实例

理解了上述内容,你就已经掌握了跨域请求的原理,那么下面我们将利用ajax发起跨域请求,服务端通过spring MVC处理jsonp请求。

//通过JQuery Ajax 发起jsonp请求
(注:不是必须通过jq发起请求 , 
     例如:Vue Resource中提供 $.http.jsonp(url, [options]))
$.ajax({
    // 请求方式
    type: "get", 
    // 请求地址
    url: "http://169.254.200.238:8080/jsonp.do", 
    // 标志跨域请求
    dataType: "jsonp",				
    // 跨域函数名的键值,即服务端提取函数名的钥匙(默认为callback)
    jsonp: "callbackparam",   
    // 客户端与服务端约定的函数名称
    jsonpCallback: "jsonpCallback",
    // 请求成功的回调函数,json既为我们想要获得的数据
    success: function(json) {
        console.log(json);
    },
    // 请求失败的回调函数
    error: function(e) {
	alert("error");
    }
});
@RequestMapping({"/jsonp.do"})
public String jsonp(@RequestParam("callbackparam") String callback){
    // callback === "jsonpCallback"
return callback + "({\"result\":\"success\"})";
}

此时,客户端接收到的返回值为:


这就是一个完整的跨域请求,但是这样就结束了吗?

细心的同学可能会发觉,现在如果不通过跨域去请求jsonp.do这个接口,不是就报错了吗?

是的,现在这个接口仅仅只能被携带callbackparam这个参数的请求。

为了解决这个问题,我们要判断请求的来源,

return  (request.from === jsonp) ? callback(data) : data ;

这才是我们想要的结果,解决这个问题需要我们对每个接口都做判断,或者通过AOP等等方式实现统一处理,这样做好像并不优雅。

在spring4.2以上的版本,支持了CORS(跨域资源共享)

CORS

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

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。为什么说它优雅呢?

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。 只要服务器实现了CORS接口 ,就可以跨源通信。

所以我们客户端可以像什么都没发生一样,依旧漠不关心的发送我们的请求:

// 不用担心跨域问题
$.ajax({
    // 请求方式
    type: "get", 
    // 请求地址
    url: "http://169.254.200.238:8080/jsonp.do", 
    // 此时依然请求json格式数据 而非jsonp
    dataType: "json",				
    // 请求成功的回调函数
    success: function(json) {
        console.log(json);
    },
    // 请求失败的回调函数
    error: function(e) {
	alert("error");
    }
});

而我们用spring MVC实现的服务端也出乎意料的简洁(基于xml):

// spring配置文件 spring必须为4.2以上版本
<mvc:cors>   
   <mvc:mapping path="/**" />
</mvc:cors>

注意:通过以上两步我们已经完成了跨域请求操作。

这里,我对整个项目添加了跨域支持 , path 为支持跨域的路径 , 同样你可以在这里做详细配置:

<mvc:cors>

    <mvc:mapping path="/api/**"
        allowed-origins="http://domain1.com, http://domain2.com"
        allowed-methods="GET, PUT"
        allowed-headers="header1, header2, header3"
        exposed-headers="header1, header2" allow-credentials="false"
        max-age="123" />

    <mvc:mapping path="/resources/**"
        allowed-origins="http://domain1.com" />

</mvc:cors>

当然,spring也支持通过java的方式进行配置:

// 此种方式等同于xml全局配置
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }
}

// 此种方式为详细配置
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("http://domain2.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(false).maxAge(3600);
    }
}

spring 也允许针对某个controller类 或者 方法进行 跨域 ,通过@Configuration注解完成。

这里就不做详细解释,详情可以参考

SpringMvc解决跨域问题 – 王念博客 – 开源中国社区

未经允许不得转载:绿岛小站 » jsonp跨域请求详解——从繁至简

赞 (1)

评论 1

评论前必须登录!

登陆 注册
  1. 网格布不错的文章,内容文章雅致.