jsonp解决跨域

JSONP的由来

1、Ajax直接请求普通文件存在跨域无权限访问的问题,不管是静态页面、动态页面、web服务,只要是跨域请求,一律不准。

2、不过我们发现,web页面调用js文件则不受跨域的影响(不仅如此,我们还发现凡是拥有“src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>、<\iframe>)。

3、于是可以判断,当前阶段如果想通过纯web端跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理。

4、恰巧我们知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据。

5、这样,解决方案就呼之欲出了,web服务端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件,显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装进去。

6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来很像ajax,但其实并不一样。

7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,简称JSONP。该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名包裹在JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

利用jquery实现jsonp

js代码

<script type="application/javascript">
    $("#jsonp").on('click',function () {
        $.ajax({
            type: "GET",
            url: "/jsonp/jsonpTest",
            dataType: 'jsonp',
            jsonp: "callback",
            success: function (response, status, xhr) {
                console.log(response,status,xhr);
            },
            error: function () {
                console.log("请求失败");
            }
        });
    });
</script>

java后端代码

import cn.hutool.json.JSONUtil;
import com.compass.account.plugin.RedisDelayQueue;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @ignore
 * @author HuYu
 * @date 2023-06-10
 * @since 1.0
 **/
@Controller
@RequestMapping("/jsonp")
public class JSONPController {

    @Resource
    private RedisDelayQueue redisDelayQueue ;
    /**
     * jsonp测试
     * @return java.lang.String
     * @author compass
     * @date 2023/6/10 1:57
     * @since 1.0.0
     **/
    @ResponseBody
    @GetMapping(value = "/jsonpTest" , produces = {"application/javascript"})
    public String getUserInfo(@RequestParam("callback") String callback, HttpServletResponse response)   {
        response.setContentType("text/html;charset=utf-8");
        Map<String,String> resultMap = new HashMap<>();
        resultMap.put("msg","请求成功");
        resultMap.put("code","200");
        resultMap.put("data","admin");
        String jsonStr = JSONUtil.toJsonStr(resultMap);
        return  callback+"(" + jsonStr + ")";
    }

}

使用原生js调用jsonp

js代码:后端代码不变

    /**
     * 使用原生js发送jsonp请求,当请求结束后返回一个Promise回调
     * @description 使用方式:getRequest('/jsonp/jsonpTest').then(result=>{ console.log(result) })
     * @param url
     * @returns {Promise}
     */
    function getRequest(url) {
     return  new Promise(((resolve) => {
           // 创建一个全局函数回调
         if (!window.jsonResultHandler){
             window.jsonResultHandler = function (data) {
                 resolve(data);
             };
         }
            
            url = url.indexOf('?')===-1?(url+'?callback=jsonResultHandler'):(url+'&callback=jsonResultHandler');
            let callbackId = 'callback'+new Date().getTime();
            let script = document.createElement('script');
            script.setAttribute('src', url);
            script.setAttribute('id', callbackId);
            document.getElementsByTagName('head')[0].appendChild(script);
            // script加载完成后执行回调,然后删除掉scrip标签
            if(script.readyState){
                script.onreadystatechange=function(){
                    if(script.readyState==='complete'||script.readyState==='loaded'){
                        script.onreadystatechange=null;
                        window.jsonResultHandler();
                        let callbackDom =   document.getElementById(callbackId);
                        if (callbackDom){
                            callbackDom.remove();
                        }
                    }
                }
            }else{
                script.onload=function(){
                    window.jsonResultHandler();
                    let callbackDom =   document.getElementById(callbackId);
                    if (callbackDom){
                        callbackDom.remove();
                    }
                }

            }
        }))


    }
    getRequest('/jsonp/jsonpTest').then(result=>{ 
        console.log(result) 
    })

核心思路:就是利用script标签不会产生跨域,然后使用script标签去发送请求,然后后端返回一个script类型回调函数,然后这个回调函数在前端是一个全局变量的函数,随后被后端回调,这样就解决了跨域的问题,但是这种方式只支持get请求。

你可能感兴趣的:(前端,okhttp)