<div class="iteye-blog-content-contain" style="font-size: 14px"><p><div id="cnblogs_post_body"></p>
<p> <p>浏览器具有同源策略,会禁止向与当前页面不同的域发送请求,只要是协议,域名,端口中有任何一个不同,都被当作是不同的域,这虽然是一种保护数据的机制,但是对我们开发来说确是个麻烦,解决办法有很多,这里介绍一下JSONP和CORS</p> </p>
<p> <p>先来理解一下浏览器这个同源策略是什么,就是浏览器禁止掉<strong>向其他域名</strong>发送的请求,其实是在<strong>请求回来</strong>的时候被禁掉的</p> </p>
<p> <p><img src="http://images2017.cnblogs.com/blog/1164899/201711/1164899-20171120163200633-931000397.png" alt="" /></p> </p>
<p> <p>哪些操作会受同源策略,哪些不会呢?</p> </p>
<p> <p> <strong>requests模块</strong>不受影响,因为没有经过浏览器</p> </p>
<p> <p> <strong>ajax发请求时,浏览器会限制</strong>(我们就是要解决这个问题)</p> </p>
<p> <p> 有<strong>src属性</strong>的都不受同源策略限制 &nbsp;-img,script,iframe</p> </p>
<p> <p> 但是注意,script中的src,拿到的数据会当作<strong>js代码</strong>执行,所以不能直接把数据放到src中</p> </p>
<p> <p>&nbsp;</p> </p>
<p> <p><span style="font-size: 18px"><strong>JSONP</strong></span></p> </p>
<p> <p><strong> </strong>jsonp是一种机智的方式,本质就是在远程发送数据的时候,在数据外层套一个函数名,<strong>把数据以函数的形式返回</strong>(<strong>这样才能被script识别</strong>)</p> </p>
<p> <p> 在<strong>本地先定义一个函数</strong>,当远程发送过来数据,本地就<strong>当成一个函数执行</strong>,<strong>真实数据就相当于参数</strong></p> </p>
<p> <p>&nbsp;</p> </p>
<p> <p><strong> </strong>jsonp本质就是创建一个script标签,把url放到src属性中,就是相当于发送了一个get请求,事实上jsonp只能发送get请求</p> </p>
<p> <p>&nbsp;</p> </p>
<p> <p><strong>用一个简单的示例来理解一下jsonp的本质</strong></p> </p>
<p> <p>客户端的地址是:localhost:8000,要向服务端&nbsp;localhost:8888&nbsp;发送请求,获取数据,</p> </p>
<p> <p>利用jsonp来避开同源策略:</p> </p>
<p> <p><strong>客户端:</strong></p> </p>
<p> <div class="cnblogs_code"> </p>
<p> <pre><span style="color: #0000ff">&lt;!</span><span style="color: #ff00ff">DOCTYPE html</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;</span><span style="color: #800000">html </span><span style="color: #ff0000">lang</span><span style="color: #0000ff">=&quot;en&quot;</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;</span><span style="color: #800000">head</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="color: #0000ff">&lt;</span><span style="color: #800000">meta </span><span style="color: #ff0000">charset</span><span style="color: #0000ff">=&quot;UTF-8&quot;</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="color: #0000ff">&lt;</span><span style="color: #800000">meta </span><span style="color: #ff0000">http-equiv</span><span style="color: #0000ff">=&quot;x-ua-compatible&quot;</span><span style="color: #ff0000"> content</span><span style="color: #0000ff">=&quot;IE=edge&quot;</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="color: #0000ff">&lt;</span><span style="color: #800000">meta </span><span style="color: #ff0000">name</span><span style="color: #0000ff">=&quot;viewport&quot;</span><span style="color: #ff0000"> content</span><span style="color: #0000ff">=&quot;width=device-width, initial-scale=1&quot;</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="color: #0000ff">&lt;</span><span style="color: #800000">title</span><span style="color: #0000ff">&gt;</span>Title<span style="color: #0000ff">&lt;/</span><span style="color: #800000">title</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;/</span><span style="color: #800000">head</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;</span><span style="color: #800000">body</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;</span><span style="color: #800000">button </span><span style="color: #ff0000">onclick</span><span style="color: #0000ff">=&quot;jsonp('http://127.0.0.1:8888/get_data?callback=func')&quot;</span><span style="color: #0000ff">&gt;</span>获取数据<span style="color: #0000ff">&lt;/</span><span style="color: #800000">button</span><span style="color: #0000ff">&gt;</span>{#通过参数向服务端发送函数名,这样就能实现动态生成函数名#}</pre> </p>
<p> <pre><span style="color: #0000ff">&lt;</span><span style="color: #800000">script</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="background-color: #f5f5f5; color: #0000ff">function</span><span style="background-color: #f5f5f5; color: #000000"> func(arg) {</p>
<p> console.log(arg);</p>
<p> document.head.removeChild(tag);</p>
<p> }</p>
<p> </p>
<p> </span><span style="background-color: #f5f5f5; color: #0000ff">function</span><span style="background-color: #f5f5f5; color: #000000"> jsonp(url) {</p>
<p> tag </span><span style="background-color: #f5f5f5; color: #000000">=</span><span style="background-color: #f5f5f5; color: #000000"> document.createElement(</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">script</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">);</span><span style="background-color: #f5f5f5; color: #008000">//</span><span style="background-color: #f5f5f5; color: #008000">创建一个script标签,用来发送请求,注意是一个全局的变量,以便在func函数中删除</span></p>
<p><span style="background-color: #f5f5f5; color: #000000"> tag.src </span><span style="background-color: #f5f5f5; color: #000000">=</span><span style="background-color: #f5f5f5; color: #000000"> url;</p>
<p> document.head.appendChild(tag); </span><span style="background-color: #f5f5f5; color: #008000">//</span><span style="background-color: #f5f5f5; color: #008000">在html的头文件中添加这个script标签</span></p>
<p><span style="background-color: #f5f5f5; color: #000000"> }</p>
<p></span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">script</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;/</span><span style="color: #800000">body</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;/</span><span style="color: #800000">html</span><span style="color: #0000ff">&gt;</span></pre> </p>
<p> </div> </p>
<p> <p>&nbsp;</p> </p>
<p> <p>服务端:</p> </p>
<p> <div class="cnblogs_code"> </p>
<p> <pre>from djan<a href="http://www.cppentry.com/list.php?fid=78">GO</a>.shortcuts import HttpResponse<br /><br />def get_data(request):<br /> func_name = request.GET.get('callback')#从请求头中获取函数名<br /> return HttpResponse('%s(数据)'%func_name)#返回一个函数,把数据当作参数</pre> </p>
<p> </div> </p>
<p> <p>1.jsonp就是利用script的src发送请求不会被受同源策略限制,然后通过src属性向服务端发送请求</p> </p>
<p> <p>2.script会把请求来的数据当成是js代码,所以服务端返回的数据不能直接是数据,会报错</p> </p>
<p> <p>3.把返回值封装成一个函数的形式,真实数据放在函数的参数中,客户端等收到后,从函数中拿到数据就行了</p> </p>
<p> <p>4.这个函数名最好是动态生成的,不然每次发送一个请求就要前后端商量好函数名,太麻烦</p> </p>
<p> <p>&nbsp;</p> </p>
<p> <p>以上就是jsonp的原理</p> </p>
<p> <p>所以使用jsonp,本地需要定义一个函数,而远程需要把数据封装成一个函数</p> </p>
<p> <p>再来看看ajax如何实现跨域请求</p> </p>
<p> <div class="cnblogs_code"> </p>
<p> <pre><span style="color: #0000ff">&lt;!</span><span style="color: #ff00ff">DOCTYPE html</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;</span><span style="color: #800000">html </span><span style="color: #ff0000">lang</span><span style="color: #0000ff">=&quot;en&quot;</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;</span><span style="color: #800000">head</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="color: #0000ff">&lt;</span><span style="color: #800000">meta </span><span style="color: #ff0000">charset</span><span style="color: #0000ff">=&quot;UTF-8&quot;</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="color: #0000ff">&lt;</span><span style="color: #800000">meta </span><span style="color: #ff0000">http-equiv</span><span style="color: #0000ff">=&quot;x-ua-compatible&quot;</span><span style="color: #ff0000"> content</span><span style="color: #0000ff">=&quot;IE=edge&quot;</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="color: #0000ff">&lt;</span><span style="color: #800000">meta </span><span style="color: #ff0000">name</span><span style="color: #0000ff">=&quot;viewport&quot;</span><span style="color: #ff0000"> content</span><span style="color: #0000ff">=&quot;width=device-width, initial-scale=1&quot;</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="color: #0000ff">&lt;</span><span style="color: #800000">title</span><span style="color: #0000ff">&gt;</span>Title<span style="color: #0000ff">&lt;/</span><span style="color: #800000">title</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;/</span><span style="color: #800000">head</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;</span><span style="color: #800000">body</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;</span><span style="color: #800000">button </span><span style="color: #ff0000">onclick</span><span style="color: #0000ff">=&quot;jsonp()&quot;</span><span style="color: #0000ff">&gt;</span>获取数据<span style="color: #0000ff">&lt;/</span><span style="color: #800000">button</span><span style="color: #0000ff">&gt;</span></p>
<p><span style="color: #0000ff">&lt;</span><span style="color: #800000">script</span><span style="color: #0000ff">&gt;</span></p>
<p> <span style="background-color: #f5f5f5; color: #0000ff">function</span><span style="background-color: #f5f5f5; color: #000000"> func(arg) {</p>
<p> console.log(arg);</p>
<p> document.head.removeChild(tag);</p>
<p> }</p>
<p> </p>
<p> {# <strong>这种实际上就是内部帮我们实现创建一个script标签</strong>#}</p>
<p> </span><span style="background-color: #f5f5f5; color: #0000ff">function</span><span style="background-color: #f5f5f5; color: #000000"> jsonp() {</p>
<p> $.ajax({</p>
<p> url:</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">http://127.0.0.1/get_data</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">,</p>
<p> type:</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">GET</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">,</p>
<p> dataType:</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">JSONP</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">,</p>
<p> jsonp:</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">callback</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">,</p>
<p> jsonCallback:</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">func</span><span style="background-color: #f5f5f5; color: #000000">'</span><span style="background-color: #f5f5f5; color: #000000">{# <strong>这两行就是自动在url后面添加</strong></span><strong><span style="background-color: #f5f5f5; color: #000000">?</span><span style="background-color: #f5f5f5; color: #000000">callback</span><span style="background-color: #f5f5f5; color: #000000">=</span></strong><span style="background-color: #f5f5f5; color: #000000"><strong>func</strong> #}</p>
<p> </p>
<p> </p>
<p> })</p>
<p> }</p>
<p></span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">script</span><span style="color: #0000ff">&gt;</span></pre> </p>
<p> </div> </p>
<p> <p>&nbsp;</p> </p>
<p> <p>jsonp只能发送get请求,但是可不是所有的数据都能封装在参数中,想要发送post请求,还得用CORS</p> </p>
<p> <hr /> </p>
<p> <p>&nbsp;</p> </p>
<p> <p><span style="font-size: 18px"><strong>CORS跨站资源共享(Cross-Origin Resource Sharing)</strong></span></p> </p>
<p> <p>再来看一下跨域请求时,浏览器的提示信息:</p> </p>
<p> <p><img src="http://images2017.cnblogs.com/blog/1164899/201711/1164899-20171120163200633-931000397.png" alt="" /></p> </p>
<p> <p>之所以ajax发送请求,返回的数据拿不到,是因为少了一点东西。提示缺少一个响应头,所以CORS的原理就是添加上这个响应头</p> </p>
<p> <p>只需要修改视图函数&nbsp;</p> </p>
<p> <div class="cnblogs_code"> </p>
<p> <pre><span style="color: #0000ff">from</span> djan<a href="http://www.cppentry.com/list.php?fid=78">GO</a>.shortcuts <span style="color: #0000ff">import</span><span style="color: #000000"> HttpResponse</p>
<p> </p>
<p></span><span style="color: #0000ff">def</span><span style="color: #000000"> get_data(request):</p>
<p> response </span>= HttpResponse(<span style="color: #800000">'</span><span style="color: #800000">数据</span><span style="color: #800000">'</span><span style="color: #000000">)</p>
<p> response[</span><span style="color: #800000">'</span><span style="color: #800000">Access-Control-Allow-Origin</span><span style="color: #800000">'</span>] = <span style="color: #800000">'</span><span style="color: #800000">http://127.0.0.1:8888</span><span style="color: #800000">'</span><span style="color: #008000">#</span><span style="color: #008000">这样表示允许这个地址访问</span></p>
<p> #response[<span style="color: #800000">'</span><span style="color: #800000">Access-Control-Allow-Origin</span><span style="color: #800000">'</span>] = <span style="color: #800000">'</span><span style="color: #800000">*</span><span style="color: #800000">'</span><span style="color: #008000">#</span><span style="color: #008000">这样表示允许所有地址访问</span></p>
<p> <span style="color: #0000ff">return</span> response</pre> </p>
<p> </div> </p>
<p> <p>这样就可以了,这是最简单的情况,本地不用做任何事,远程设置一个响应头</p> </p>
<p> <p>&nbsp;</p> </p>
<p> <p>还有一种复杂点的情况:<strong>非简单请求</strong></p> </p>
<p> <p>简单请求&amp;非简单请求:</p> </p>
<p> <p> 简单请求的条件: </p> </p>
<p> <p> 1.请求方式为&nbsp;HEAD,GET,POST</p> </p>
<p> <p> 2.请求头中Content-Type的值是下面这三个中的一个</p> </p>
<p> <p> application/x-www-form-urlencoded</p> </p>
<p> <p> multipart/form-data</p> </p>
<p> <p> text/plain</p> </p>
<p> <p>&nbsp;</p> </p>
<p> <p> <strong>同时满足以上两个条件时,才是简单请求,有一个不满足就是复杂请求</strong></p> </p>
<p> <p>&nbsp;</p> </p>
<p> <p><strong><br /></strong>对于复杂请求,以PUT请求为例,会先发一个<strong>options请求</strong>,用来预检,对于预检请求,也要返回响应头</p> </p>
<p> <p>远程视图函数要这样处理:</p> </p>
<p> <div class="cnblogs_code"> </p>
<p> <pre><span style="color: #0000ff">def</span><span style="color: #000000"> data(request):</p>
<p> </span><span style="color: #0000ff">if</span> request.method == <span style="color: #800000">'</span><span style="color: #800000">OPTIONS</span><span style="color: #800000">'</span><span style="color: #000000">:</p>
<p> </span><span style="color: #008000">#</span><span style="color: #008000">预检</span></p>
<p> response =<span style="color: #000000"> HttpResponse()</p>
<p> response[</span><span style="color: #800000">'</span><span style="color: #800000">Access-Control-Allow-Origin</span><span style="color: #800000">'</span>] = <span style="color: #800000">'</span><span style="color: #800000">*</span><span style="color: #800000">'</span><span style="color: #000000"></p>
<p> response[</span><span style="color: #800000">'</span><span style="color: #800000">Access-Control-Allow-Methods</span><span style="color: #800000">'</span>] = <span style="color: #800000">'</span><span style="color: #800000">PUT</span><span style="color: #800000">'#允许这个请求头 </span></p>
<p> <span style="color: #0000ff">return</span><span style="color: #000000"> response</p>
<p> </span><span style="color: #0000ff">elif</span> request.method == <span style="color: #800000">'</span><span style="color: #800000">PUT</span><span style="color: #800000">'</span><span style="color: #000000">:</p>
<p> </span><span style="color: #008000">#</span><span style="color: #008000">预检通过后真正的请求</span></p>
<p> response = HttpResponse(<span style="color: #800000">'</span><span style="color: #800000">数据</span><span style="color: #800000">'</span><span style="color: #000000">)</p>
<p> response[</span><span style="color: #800000">'</span><span style="color: #800000">Access-Control-Allow-Origin</span><span style="color: #800000">'</span>] = <span style="color: #800000">'</span><span style="color: #800000">*</span><span style="color: #800000">'</span></p>
<p> </p>
<p> <span style="color: #0000ff">return</span> response</pre> </p>
<p> </div> </p>
<p> <p><strong>如果GET请求自定义请求头,也变成了复杂请求</strong>,也需要设置一下,允许这个请求头</p> </p>
<p> <p>比如自定义了一个请求头:xxx:yyyy</p> </p>
<p> <p>那远程代码中就要允许这个请求头</p> </p>
<p> <div class="cnblogs_code"> </p>
<p> <pre><span style="color: #0000ff">def</span><span style="color: #000000"> data(request):</p>
<p> </span><span style="color: #0000ff">if</span> request.method == <span style="color: #800000">'</span><span style="color: #800000">OPTIONS</span><span style="color: #800000">'</span><span style="color: #000000">:</p>
<p> </span><span style="color: #008000">#</span><span style="color: #008000">预检</span></p>
<p> response =<span style="color: #000000"> HttpResponse()</p>
<p> response[</span><span style="color: #800000">'</span><span style="color: #800000">Access-Control-Allow-Origin</span><span style="color: #800000">'</span>] = <span style="color: #800000">'</span><span style="color: #800000">*</span><span style="color: #800000">'</span></p>
<p> <span style="color: #008000">#</span><span style="color: #008000"> response['Access-Control-Allow-Methods'] = 'PUT'</span></p>
<p> response[<span style="color: #800000">'</span><span style="color: #800000">Access-Control-Allow-Headers</span><span style="color: #800000">'</span>] = <span style="color: #800000">'</span><span style="color: #800000">xxx</span><span style="color: #800000">'</span><span style="color: #008000">#</span><span style="color: #008000">允许这个请求头</span></p>
<p> </p>
<p> <span style="color: #0000ff">return</span><span style="color: #000000"> response</p>
<p> </span><span style="color: #0000ff">elif</span> request.method == <span style="color: #800000">'</span><span style="color: #800000">PUT</span><span style="color: #800000">'</span><span style="color: #000000">:</p>
<p> </span><span style="color: #008000">#</span><span style="color: #008000">预检通过后真正的请求</span></p>
<p> response = HttpResponse(<span style="color: #800000">'</span><span style="color: #800000">数据</span><span style="color: #800000">'</span><span style="color: #000000">)</p>
<p> response[</span><span style="color: #800000">'</span><span style="color: #800000">Access-Control-Allow-Origin</span><span style="color: #800000">'</span>] = <span style="color: #800000">'</span><span style="color: #800000">*</span><span style="color: #800000">'</span></p>
<p> </p>
<p> <span style="color: #0000ff">return</span> response</pre> </p>
<p> </div> </p>
<p> <div></p>
<p> 虽然复杂请求可以解决</p>
<p> </div> </p>
<p> <div></p>
<p> 但是,要尽量避免复杂请求,会增加服务器压力</p>
<p> </div> </p>
<p> <div></p>
<p> <hr />总结,解决跨域请求,常用的有三种方式:requests模块,cors,jsonp</p>
<p> </div> </p>
<p> <div></p>
<p> 后两种直接从前端拿数据,不需要经过服务器,requests模块是先把请求发送到服务器,服务器向目标发送请求,再返回数据到本地</p>
<p> </div> </p>
<p> <div></p>
<p> &nbsp;</p>
<p> </div> </p>
<p> <div></p>
<p> jsonp和cors相比,兼容性更好一点</p>
<p> </div> </p>
<p> <div></p>
<p> &nbsp;</p>
<p> </div> </p>
<p> <div></p>
<p> 参考博客:http://www.cnblogs.com/wupeiqi/articles/5703697.html</p>
<p> </div></p>
<p></div></p></div>
处理跨域请求
猜你喜欢
转载自ymssl.iteye.com/blog/2406733
今日推荐
周排行