zoukankan      html  css  js  c++  java
  • Ajax,JSONP以及跨域问题

    没用过裸的Ajax 也没听过jsonp,也不了解跨域问题,emmm…

    参考:

    http://www.runoob.com/ajax/ajax-tutorial.html

    https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434499861493e7c35be5e0864769a2c06afb4754acc6000

    https://segmentfault.com/a/1190000015597029

    http://ghmagical.com/article/page/id/AASiankfBJWp

    Ajax,Asynchronous JavaScript and XML,直译就是异步的JavaScript和XML,其实就是使用JavaScript执行异步网络请求,不局限XML,可以有多种格式。

    好处就是使用js获取数据,再用js更新页面,就不需要刷新,重新加载整个页面了。

    Ajax使用XMLHttpRequest与后台进行交互

    其实Ajax的实现代码真的很简单… (刚好在开一个9002端口的后台服务……

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <div id="info"></div>
        <script>
            function success(text) {
                const infoDiv = document.getElementById('info')
                infoDiv.innerHTML = text
                console.log('success:' + text)
            }
    
            function fail(code) {
                const infoDiv = document.getElementById('info')
                infoDiv.innerHTML = code
                console.log('fail:' + code)
            }
    
            var request;
            /**
             * 对于低版本的IE,需要换一个 ActiveXObject 对象
             * 通过检测 window 对象是否有 XMLHttpRequest 属性来确定浏览器是否支持标准的 XMLHttpRequest
             */
            if (window.XMLHttpRequest) {
                request = new XMLHttpRequest();
            } else {
                request = new ActiveXObject('Microsoft.XMLHTTP');
            }
    
            request.onreadystatechange = function () { // 状态发生变化时,函数被回调
                if (request.readyState === 4) { // 成功完成
                    // 判断响应结果:
                    if (request.status === 200) {
                        // 成功,通过responseText拿到响应的文本:
                        return success(request.responseText);
                    } else {
                        // 失败,根据响应码判断失败原因:
                        return fail(request.status);
                    }
                } else {
                    // HTTP请求还在继续...
                }
            }
    
            // 发送请求:
            request.open('GET', 'http://127.0.0.1:9002/user/info');
            request.send();
        </script>
    </body>
    </html>

    其中的函数

    open(method, url, async)
    method:请求的类型;GET 或 POST
    url:文件在服务器上的位置
    async:true(异步)或 false(同步)
    
    send(string)
    string:仅用于 POST 请求
    
    setRequestHeader(header,value)
    向请求添加 HTTP 头。
    header: 规定头的名称
    value: 规定头的值 
    
    例
    xmlhttp.open("POST","/try/ajax/demo_post2.php",true);
    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttp.send("fname=Henry&lname=Ford");

    至于 onreadystatechange 肯定就是一个回调函数啦,每当 readyState 属性改变时,就会调用该函数。那个…异步才需要回调函数嘛…

    readyState 含义

    0: 请求未初始化
    1: 服务器连接已建立
    2: 请求已接收
    3: 请求处理中
    4: 请求已完成,且响应已就绪

    status 含义(就是HTTP状态码

    200:服务器响应正常。
    304:该资源在上次请求之后没有任何修改(这通常用于浏览器的缓存机制,使用GET请求时尤其需要注意)。
    400:无法找到请求的资源。
    401:访问资源的权限不够。
    403:没有权限访问资源。
    404:需要访问的资源不存在。
    405:需要访问的资源被禁止。
    407:访问的资源需要代理身份验证。
    414:请求的URL太长。
    500:服务器内部错误。

    responseText 和 responseXML

    responseText: 获得字符串形式的响应数据。
    responseXML: 获得 XML 形式的响应数据。

    然后就是跨域问题。

    Failed to load http://b: ... has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a' is therefore not allowed access.

    首先要知道跨域问题是因为浏览器的安全策略:同源策略。

    默认情况下,JavaScript在发送AJAX请求时,URL的域名必须和当前页面完全一致。域名,协议,端口号都要相同。

    也就是说小一去访问小夏,然而小夏家有防盗门,进不去。你总不能怪别人有防盗门吧。这时小一一个人,无论做什么都是没办法进去的,想要进去,只有小夏帮你。你们对个暗号,小夏帮你开门,或者小夏给你钥匙,录个指纹之类的。

    同理,跨域问题,光靠前端是没办法解决的,有了后端帮助才可以解决,比如把门打开……嗯……

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

    我的理解就是在后台配置一下…………比如我用Spring Boot写的后台代码……

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**");
            }
        };
    }

    正常点……

    根据报错就可以看出 a 网站想访问 b, 但是 b 的响应中 Access-Control-Allow-Origin 字段并不包含 a 的域名,访问失败。

    对于简单请求,定义如下:

    请求方法是以下三种方法之一:HEAD、GET、POST

    HTTP 的头信息不超出以下几种字段:Accept、Accept-Language、Content-Language、Last-Event-ID

    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

    只需要合理设置 Access-Control-Allow-Origin 即可。

    对于非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)这种情况下除了设置origin,还需要设置Access-Control-Request-Method以及Access-Control-Request-Headers

    。。。。xx

    2. 还有一种解决方式是代理,前端把请求发给同域的代理服务器,代理服务器再转发给真正的服务器。

    3. 就是我要学习的JSONP了,它有个限制,只能用GET请求,并且要求返回JavaScript。这种方式跨域实际上是利用了浏览器允许跨域引用JavaScript资源。

    通过js创建一个script标签然后添加src,这样就会调用这个src的请求,所以只能是get请求。其实真的也是很简单的~前端页面:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>jsonp</title>
    </head>
    <body>
        <span>从后台获取数据:</span>
        <div id="data"></div>
        <script>
            function foo(data) {
                console.log('通过jsonp获取后台数据:', data)
                document.getElementById('data').innerHTML = data
            }
            (function jsonp() {
                let head = document.getElementsByTagName('head')[0] // 获取head元素 把js放里面
                let js = document.createElement('script')
                js.src = 'http://127.0.0.1:9002/testJSONP?a=1&b=2&callback=foo'
                head.appendChild(js)
            })()
        </script>
    </body>
    </html>

    后端返回数据用 callback() 包裹起来,这样就相当于返回的那个js文件在调用 callback 函数,需要的逻辑直接写到指定的 callback 函数就可以了,很机智的方法。

    @RequestMapping(value="/testJSONP", method = RequestMethod.GET)
    public String testJSONP(String callback, Integer a, Integer b) {
        return callback + '(' + (a + b) + ')';
    }

    请求信息

    返回一个字符串

    抄一遍大神把ajax和jsonp合一起的代码,不到100行,很简单。

    function ajax(params) {
    
        params = params || {}
        params.data = params.data || {}
    
        params.jsonp ? jsonp(params) : json(params) // 根据params中是否有jsonp参数判断是不是jsonp请求
    
        function json(params) {
            params.type = (params.type || 'GET').toUpperCase() // 默认使用get方法
            params.data = formatParams(params.data) // 把参数对象改为参数字符串
            let xhr = new XMLHttpRequest()
            xhr.onreadystatechange = function() {
                // readyState 属性表示请求/响应过程的当前活动阶段 4为完成 已经接收到全部响应数据
                if (xhr.readyState === 4) {
                    let status = xhr.status
                    if (status >= 200 && status < 300) {
                        let response = ''
                        // 判断接受数据的内容类型
                        let type = xhr.getResponseHeader('Content-Type')
                        if (type.indexOf('xml') !== -1 && xhr.responseXML) {
                            response = xhr.responseXML
                        } else if (type === 'application/json') {
                            response = JSON.parse(xhr.responseText)
                        } else {
                            response = xhr.responseText
                        }
                        params.success && params.success(response)
                    } else {
                        params.error && params.error(status)
                    }
                }
            }
            // 如果是get就把参数放在url 否则放在body
            if (params.type === 'GET') {
                xhr.open(params.type, params.url + '?' + params.data, true)
                xhr.send()
            } else {
                xhr.open(params.type, params.url, true)
                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
                xhr.send(params.data)
            }
        }
    
        function jsonp(params) {
            let callbackName = params.jsonp
            let head = document.getElementsByTagName('head')[0]
            params.data['callback'] = callbackName
            let data = formatParams(params.data)
            let script = document.createElement('script')
            head.appendChild(script)
    
            window[callbackName] = function(json) {
                head.removeChild(script)
                clearTimeout(script.timer)
                window[callbackName] = null
                params.success && params.success(json)
            }
    
            script.src = params.url + '?' + data
    
            if (params.time) {
                script.timer = setTimeout(() => {
                    head.removeChild(script)
                    window[callbackName] = null
                    params.error && params.error({ message: '超时' })
                }, params.time)
            }
        }
    
        function formatParams(data) {
            let arr = []
            for (let name in data) {
                arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]))
            }
            // 添加一个随机数 防止缓存
            arr.push('v=' + random())
            return arr.join('&')
        }
    
        function random() {
            return Math.floor(Math.random() * 10000 + 500)
        }
    
    }
  • 相关阅读:
    关于2021-01-13爆发的incaseformat病毒相关信息收集1.2(持续更新和关注中...)
    (转)服务器UDIMM、LRDIMM、RDIMM三种内存的区别
    Shred命令 学习
    关于图片文件的元数据信息清理和时间戳修改
    关于Chrome 浏览器 “小尾巴”问题的解决办法
    CF471D MUH and Cube Walls -kmp
    UVA12467 Secret Word -kmp
    UVA12604 Caesar Cipher -kmp
    【JLOI2007】周期串
    Http协议
  • 原文地址:https://www.cnblogs.com/wenruo/p/9440251.html
Copyright © 2011-2022 走看看