跨域

一、产生原因

  • 浏览器的同源策略会出于安全考虑,限制JavaScript跨域请求资源;
  • 同源:浏览器的协议、域名、端口号都相同就叫同源;
  • 客户端请求服务器文件时,协议、域名、端口号只要有一个不同就会产生跨域问题

二、几种主流跨域解决方案

这里补充一个内容

  • json:轻量级数据格式
  • 优点:
    • 轻量级,体积小,节省流量,提高加载速度
    • 解析成原生js对象,解析速度比XML快
    • 查找数据无需查找标签,效率更高
  • 注意:js不能直接引入json文件,可以通过ajax请求获得
  • 在数据传输流程中,json是以文本即字符串的形式进行传递的,而js操作的是json对象,所以json对象和json字符串之间的转换是关键
    • JSON字符串转JSON对象 JSON.parse ( JSON字符串 )
    • JSON对象转JSON字符串 JSON.stringify(JSON对象 )
      json文件如:
   {
   "name": "haha",
   "age": 18,
   "brother": [{
       "name": "哥哥",
       "age": 19
   }, {
       "name": "哥哥",
       "age": 19
   }]
}

1、通过服务端代理请求——要服务器

比如说:服务端php,php是没有跨域限制的,我们可以让服务端去别的网站获取内容然后返回页面,按照我的理解可以理解为在html页面通过ajax向php发出请求,让php代替去别的网站获取内容返回给我们(返回的为json:轻量级数据格式)

如:通过服务端代理请求百度的数据
html页面的代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
  margin: 0;
  padding: 0;
  list-style: none;
}
.wrap{
  width: 500px;
  margin: 100px auto 0;
}
.ipt{
  padding: 6px 8px;
  width: 480px;
}
.list{
  background-color: #ccc;
}
.list li{
  line-height: 30px;
  padding: 4px 10px;
}
.list li:hover{
  background-color: #eee;
}
</style>
</head>
<body>
  
<div class="wrap">
  <input type="text" class="ipt">
  <ul class="list">
    <!-- <li>12306</li>
    <li>12315</li>
    <li>12345</li> -->
  </ul>
</div>
<script src="./utils.js"></script>
<script>
var ipt = $1('.ipt')
var list = $1('.list')
// oninput 输入一次改变一次
// onchange  失去焦点时改变
//  onkeyup   键盘按下时改变
ipt.onkeyup = function (){
  var keyword = ipt.value
  // 空值判断
  if (!keyword) {
    return
  }
  ajax({
    url: './data/baidu.php',
    type: 'get',
    data: 'wd='+keyword,
    dataType: 'json',
    success: function (json){
      // console.log(json)
      var domStr = ''
      json.s.forEach(function (item){
        domStr += '<li>'+item+'</li>'
      })
      list.innerHTML = domStr
    },
    error: function (){
    }
  })
}
</script>
</body>
</html>

baidu.php

<?php

/*
****************************************************

	请求方式: get

	url:	./data/baidu.php
	参数:   wd = 搜索关键字
		
	return:	'{s:['数据1','数据2'.。。。]}'
			 json字符串

****************************************************
*/

// header("Access-Control-Allow-Origin:*");
 
// 服务端代理请求
$url = 'http://suggestion.baidu.com/su?wd=';

function getJSONStr($str){
	return substr($str,17);
}

function crul($key){
	global $url;
	$data = file_get_contents($url.$key);
	$data = getJSONStr($data);
	$data = str_replace("{q:\"","",$data);
	$data = str_replace("\",p:","{%aaa%}",$data);
	$data = str_replace(",s:[","{%aaa%}",$data);
	$data = str_replace("]});","",$data);
	$arr = explode("{%aaa%}",$data);
	$res = array();
	$res['q'] = iconv("GB2312","UTF-8",$arr[0]);

	if ($arr[1] == 'true'){
		$arr[1] = true;
	}else{
		$arr[1] = false;
	}

	$res['p'] = $arr[1];

	if (strlen($arr[2])>0){
		$arr[2] = substr($arr[2],1,-1);
		$arr[2] = str_replace("\",\"",",",$arr[2]);
		$arr[2] = iconv("GB2312","UTF-8",$arr[2]);
	}
	
	$res['s'] = explode(',',$arr[2]);
	echo json_encode($res);//json_encode()转换成json字符串
}

$key = $_REQUEST['wd'];

crul($key);

?>

utils.js内用到的代码

function $1(selector){
  return document.querySelector(selector)
}
function ajax(options){
  // data -> 'key=value&key=value'
  // 1.创建数据交互对象
  if (window.XMLHttpRequest) {
    var xhr = new XMLHttpRequest() // 非IE5 6
  } else {
    var xhr = new ActiveXObject('Microsoft.XMLHTTP') // IE5 6
  }

  // 判断并格式化参数data
  var data = ''
  // if (typeof options.data === 'object' && options.data !== null && options.data.constructor === 'Object') {
  if (isObject(options.data)) {
    // 把对象格式化成 -> 'k1=v1&k2=v2&k3=v3'
    for (var key in options.data) {
      data += key+'='+options.data[key]+'&'
    }
    // data = 'k1=v1&k2=v2&k3=v3&'
    data = data.substring(0,data.length-1)
  }

  if (typeof options.data === 'string') {
    data = options.data
  }

  // 判断请求方式
  if (options.type.toLowerCase() === 'get') {
    var time = ''
    time = options.cache ? '' : Date.now()
    // 2.打开连接
    xhr.open(options.type,options.url+'?'+data+'&_='+time,true) // 默认true,异步
    // 3.发送请求
    xhr.send(null) // get请求传null
  }
  if (options.type.toLowerCase() === 'post') {
    // 2.打开连接
    xhr.open(options.type,options.url,true) // 默认true,异步
    // post 请不会有缓存问题

    // 设置请求头,作用 模拟表单 post 请求提交数据,在send方法之前设置
    xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")

    // 3.发送请求
    xhr.send(data) // post请求 要传递的参数在此传
  }
  
  // 4.等待请求/响应状态
  // xhr.readyState  请求状态,0-4状态改变会触发一个readystatechange事件
  xhr.onreadystatechange = function (){
    // console.log( xhr.readyState );// 2 3 4
    if (xhr.readyState === 4) {// 请求完成
    // xhr.status 响应状态
      if (xhr.status === 200) {// OK 响应就绪
        // xhr.responseText 响应的数据
        // options.success(xhr.responseText)
        // 支持dataType配置
        if (options.dataType === 'json') {
          var json = JSON.parse(xhr.responseText)
          options.success(json)
        } else if (options.dataType === 'xml') {
          options.success(xhr.responseXML)
        } else {
          options.success(xhr.responseText)
        }
      } else {
        // console.log(xhr.status)
        options.error(xhr.status)
      }
    }
  }
}

页面效果
注意:我这里开了Nginx 且端口号为85 要在本地服务器打开才行即在浏览器查看需要打开本地进行查看 http://localhost:85
在这里插入图片描述
对了,我这里百度的api是

百度关键词:
url地址:http://suggestion.baidu.com/su

-----请求参数-----
   cb      回调函数
   wd      关键词

-----返回数据-----
JSON返回示例:
{
   q: "123",
   p: false,
   s: [
      0: "12306"
      1: "12306铁路客户服务中心"
      2: "12306火车票网上订票官网"
      3: "12333"
      4: "12333社保查询网"
      5: "12306验证码识别"
      6: "123网址之家"
      7: "12345"
      8: "123456hd"
      9: "12308"
   ]
}

2、jsonp跨域(针对get请求)——无需服务器

原理:jsonp跨域就是利用script标签的跨域能力请求资源
  • 浏览器出于安全考虑限制了JavaScript的跨域能力,但是没有限制标签的跨域
注意:
  • jsonp拿到的数据是json对象,而不是json字符串,不需要解析可以直接用
  • script标签可以跨域,类似的还有link、img、iframe
<img src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3807427310,283445384&fm=26&gp=0.jpg" alt="">

<!-- 页面引入子窗口 子窗口可以引入外部的一些资源 -->
<iframe width="600" height="400" src="https://www.baidu.com/" frameborder="1"></iframe>

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  • jsonp跨域就是利用script标签的特征:请求回来的数据当成js文件执行
    既然叫jsonp,显然目的还是json,而且是跨域获取
    即利用js创建一个script标签,把json的url赋给script的src属性,把这个script插入页面,让浏览器去跨域获取资源
  • callback是页面存在的回调方法,参数就是想得到的json
  • 回调方法要遵从服务端的约定一般是用 callback 或者 cb
  • jsonp针对get形式的请求
jsonp实现跨域具体怎么做
  • 注意:在html页面请求了php文件,必须打开服务器才不会报错,因为php是基于服务端的脚本语言,必须借助一些服务器才能打开
    • 如果后端返回的是函数调用,那么,前端必须有这个全局函数即 js function test(json){ console.log(json.wd) }不 然在在php直接 echo ‘test()’会报错,告诉我们test未定义
      • 原因:请求回来的数据当成js文件执行 在php调用了函数 但是在js文件未定义函数
      • 可以在函数接收一个形参(json),这个形参就是从php返回的我们需要的数据
      • 需要注意的是,我们不能一进入页面就调用函数,因为调用函数就直接执行了,我们并不知道需要执行的内容 一般会在地址栏传一个数据给后端,告诉后端我们需要拿什么数据 后端需要接收一下前端传的数据 如 :
      function test(json){
       	console.log(json.wd)
       }
       <script src="./data/jsonp.php?cb=test"></script>  
      
       //后端 jsonp.php文件中
       $wd = $_GET['wd'];
       echo 'test({a:123,b:456,wd:'.$wd.'})';
      

jsonp实现思路: 这样依旧会一进入页面就直接执行了,无法根据用户需要进行获取内容,比如说搜索,用户输入内容后我们再去向后端请求数据,因此我们需要动态创建script标签,即在需要的时候才创建script标签请求资源

jsonp的实现
动态创建script标签步骤如下:
  • 创建script标签
    var oScript = document.createElement(‘script’)
  • 给创建的script标签的src属性赋值(数据地址)
    oScript.src = ‘./data/jsonp.php?wd=888’
  • 将创建的script标签插入body末尾
    document.body.appendChild(oScript)
  • 这时点击btn会一直创建script 因此我们要在script标签加载完成后,可以删除掉
    oScript.onload = function (){
    document.body.removeChild(oScript)
    }
<button class="btn">请求数据</button>
<script>
var btn = document.querySelector('.btn')

btn.onclick = function (){
  // 需要是动态创建script标签
  var oScript = document.createElement('script')
  // 给创建的script标签的src属性赋值(数据地址)
  oScript.src = './data/jsonp.php?wd=888'
  // 将创建的script标签插入body末尾
  document.body.appendChild(oScript)
  // 这时点击btn会一直创建script
  // 因此我们要在script标签加载完成后,可以删除掉
  oScript.onload = function (){
    document.body.removeChild(oScript)
  }
}

// 全局函数
function test(json){
  console.log(json.wd)
}
</script>

jsonp请求百度的数据例子:(不需要打开服务器,因为请求的不是php文件)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
  *{
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .wrap{
    width: 500px;
    margin: 100px auto 0;
  }
  .ipt{
    padding: 6px 8px;
    width: 480px;
  }
  .list{
    background-color: #ccc;
  }
  .list li{
    line-height: 30px;
    padding: 4px 10px;
  }
  .list li:hover{
    background-color: #eee;
  }
  </style>
</head>
<body>
  <div class="wrap">
    <input type="text" class="ipt">
    <ul class="list">
      <!-- <li>12306</li>
      <li>12315</li>
      <li>12345</li> -->
    </ul>
  </div>
<script>
var ipt = document.querySelector('.ipt')
var list = document.querySelector('.list')
  // 不需要在服务器端打开
ipt.onkeyup = function (){
  var oScript = document.createElement('script')
  oScript.src = 'http://suggestion.baidu.com/su?cb=mycallback&wd='+ipt.value
  document.body.appendChild(oScript)
  oScript.onload = function (){
    document.body.removeChild(oScript)
  }

}

// 请求成功执行的函数
function mycallback(json){
  var domStr = ''
  json.s.forEach(function (item){
    domStr += '<li>'+item+'</li>'
  })
  list.innerHTML = domStr
}
//mycallback在后端请求到的数据:     mycallback({q:"1",p:false,s:["1688黄页网","192.168.1.1 登陆入口","1公顷等于多少平方米","1升等于多少毫升","192.168.0.1 登陆页面","163","163 邮箱","123","11pro max","192.168.1.1手机登录"]});
</script>
</body>
</html>
jsonp的封装及使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
  *{
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .wrap{
    width: 500px;
    margin: 100px auto 0;
  }
  .ipt{
    padding: 6px 8px;
    width: 480px;
  }
  .list{
    background-color: #ccc;
  }
  .list li{
    line-height: 30px;
    padding: 4px 10px;
  }
  .list li:hover{
    background-color: #eee;
  }
  </style>
</head>
<body>
  <div class="wrap">
    <input type="text" class="ipt">
    <ul class="list">
      <!-- <li>12306</li>
      <li>12315</li>
      <li>12345</li> -->
    </ul>
  </div>
<script>
var ipt = document.querySelector('.ipt')
var list = document.querySelector('.list')
ipt.onkeyup = function (){
  jsonp({
    url: 'http://suggestion.baidu.com/su',
    data: 'wd='+ipt.value,
    jsonp: 'cb',
    jsonpCallback: 'hehe',//自定义函数名
    success: function mycb(json){
      var domStr = ''
      json.s.forEach(function (item){
        domStr += '<li>'+item+'</li>'
      })
      list.innerHTML = domStr
    }
  })

}
// mycallback({q:"1",p:false,s:["1688黄页网","192.168.1.1 登陆入口","1公顷等于多少平方米","1升等于多少毫升","192.168.0.1 登陆页面","163","163 邮箱","123","11pro max","192.168.1.1手机登录"]});

function isObject(obj){
  if (Object.prototype.toString.call(obj) === '[object Object]') {
    return true
  }
  return false
}

function jsonp(options){
  // options.success 变成全局函数
  // options.jsonpCallback = 'hehe'
  window[options.jsonpCallback] = options.success

  // 判断 options.data的数据类型
  // 如果字符串,直接赋值data变量
  // 如果是对象,转成参数序列的字符串
  var data = ''
  if (typeof options.data === 'string') {
    data = options.data
  }
  if (isObject(options.data)) {
    for (var key in options.data){
      data += key+'='+options.data[key]+'&'
    }
    data = data.substring(0,data.length-1)
  }

  // 创建 script标签
  var oScript = document.createElement('script')
  // 给src属性赋值(url+接口参数)
  oScript.src = options.url+'?'+options.jsonp+'='+options.jsonpCallback+'&'+data
  // 把script插入文档中
  document.body.appendChild(oScript)
  // script标签加载完成时,删除此标签
  oScript.onload = function (){
    document.body.removeChild(oScript)
  }
}
// window.mycallback()
</script>
</body>
</html>

例题:查询手机号归属地及运营商

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
<input type="text" class="ipt">
<button class="btn">查询</button>
<div class="show"></div>

<script src="./utils.js"></script>
<script>
var ipt = $1('.ipt')
var btn = $1('.btn')
var show = $1('.show')

btn.onclick = function (){

  jsonp({
    url: 'http://tcc.taobao.com/cc/json/mobile_tel_segment.htm',
    data: 'tel='+ipt.value,
    jsonp: 'callback',
    jsonpCallback: 'hehe',
    success: function (json){
      show.innerText = '归属地:'+json.province+',运营商:'+json.catName
    }
  })
}
</script>
</body>
</html>

在这里插入图片描述

3、CORS 跨域资源共享(xhr2)——要打开服务器

  • CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
  • 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制
  • 整个CORS通信过程,都是浏览器自动完成,不需要用户参与
  • 对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样
  • 实现CORS通信的关键是服务端,只要服务端实现了CORS接口,就可以跨源通信

实现CORS并不难,只需服务端即后端做一些设置即可:如在php文件中设置

<?php
header("Access-Control-Allow-Origin:*"); // 允许任何来源

?>

注意:IE10以下不支持CORS

4、Nginx代理跨域——要打开服务器

找到以下文件如下图进行配置
D:\phpstudy_pro\Extensions\Nginx1.15.11\conf\vhosts\localhost_80.conf
在这里插入图片描述
配置以上图片内容后重启Nginx
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值