springboot实现jsonp

87 篇文章 3 订阅

    jsonp是ajax跨域解决方案的一种办法,就是借助标签<script></script>可以实现不同域之间数据请求的一种方式,类似iframe,不受跨域限制,它请求返回之后,会以一种回调的形式调起挂在window对象上的全局方法callback,这里的callback就是我们在url请求中指定的回调函数,参数就是我们请求服务端包装在callback之内的数据。

    jsonp的实现,需要前后端配合,不是说前端可以实现,后端也可以实现,他们需要配合才能实现一个完整的jsonp请求。springboot1.x对jsonp还有一个AbstractJsonpResponseBodyAdvice的抽象类可以去实现jsonp,而无需改动普通的GET请求,但是在springboot2.x版本里面已经没有这个类了,所以又回到了springmvc阶段对jsonp的实现。

    springboot对jsonp的实现,最简单的就是需要获取前端请求的callback参数,然后拼接返回结果组成字符串的形式返回。

    springboot项目依赖:

<parent>
	  	<groupId>org.springframework.boot</groupId>
	  	<artifactId>spring-boot-starter-parent</artifactId>
	  	<version>2.1.4.RELEASE</version>
  </parent>
  <dependencies>
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
	    <groupId>com.alibaba</groupId>
	    <artifactId>fastjson</artifactId>
	    <version>1.2.58</version>
	</dependency>
  </dependencies>

   Java代码部分对jsonp的配合处理:

package com.xxx.springboot.web;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
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.RestController;
import com.alibaba.fastjson.JSON;
@RestController
@RequestMapping("/jsonp")
public class JsonpController {
	@GetMapping("/test")
	public String test(@RequestParam("callback")String callback) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("data", Arrays.asList(new String[]{"1","2","3"}));
		String result = JSON.toJSONString(map);
		return callback+"("+result+")";
	}
}

    这段代码有三个需要注意的地方:

    1、必须要获取callback参数,

    2、返回结果要通过callback参数拼接,不是原来的任意对象结构了,变成了字符串形式,

    3、如果类上注解是@Controller,那么方法名上需要注解@ResponseBody,或者直接在类上注解@RestController 。

    这个实现方式是请求方法带返回参数的形式,还有一种是无返回值(void)的方法形式,这种方式,就需要通过response.getWriter().write()来向请求写出结果。

package com.xxx.springboot.web;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
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 com.alibaba.fastjson.JSON;
@RequestMapping("/jsonp2")
@Controller
public class JsonpWriteController {
	@GetMapping("/test")
	public void test(@RequestParam("callback")String callback,HttpServletResponse response) throws IOException {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("code", 200);
		map.put("data", Arrays.asList(new String[]{"1","2","3","4"}));
		String result = JSON.toJSONString(map);
		response.getWriter().write(callback+"("+result+")");
	}
}

    以上部分只是讲了在服务端端如何通过springboot代码来配合实现jsonp,并不是就解决了问题,它只完成了一半。

   下面介绍web端如何实现jsonp的,jsonp原理是利用script脚本没有跨域限制,请求完成之后,会调起本地挂在在window对象上的回调函数,从而实现回显数据或者其他业务。

    我这里通过nginx启动一个http服务,然后将一个index.html指定到nginx配置的root路径下,这样nginx下请求地址是http://localhost,而springboot启动请求地址是http://localhost:8080,这样就构成了不同域,ajax请求因为跨域而受限制,可以用来验证jsonp。

    原生的jsonp在web页面上的实现:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp</title>
    <style>
        #root input{padding:10px;border:1px solid #ddd;color:#fff;border-radius: 3px;background: lightgreen;font-size: 16px;}
        #root .box{padding:5px;}
        #root textarea{width:790px;height:100px;resize: none;}
    </style>
</head>
<body>
    <div id="root">
        <div class="box">
            <input type="button" value="jsonp" onclick="handleclick()"/>
        </div>
        <div class="box">
            <textarea id="output"></textarea>
        </div>
        
    </div>
    <script>
        function showData(data){
            var res = JSON.stringify(data)
            document.querySelector("#output").textContent = res
        }
        function handleclick(){
            var url = "http://127.0.0.1:8080/jsonp/test?callback=showData&_t=123"
            var script = document.createElement("script")
            script.setAttribute("src",url)
            script.onerror = null
            document.getElementsByTagName("head")[0].appendChild(script)
        }
    </script>
</body>
</html>

  现在代码准备好了,启动springboot服务和nginx服务,打开浏览器输入地址:http://localhost,点击jsonp按钮,请求结果反应在下方输入框中。

   

    前面说springboot的两种返回形式有差别,可以改动代码,做验证:

<script>
        function showData(data){
            var res = JSON.stringify(data)
            document.querySelector("#output").textContent = res
        }
        function handleclick(){
            var url = "http://127.0.0.1:8080/jsonp2/test?callback=showData&_t=123"
            var script = document.createElement("script")
            script.setAttribute("src",url)
            script.onerror = null
            document.getElementsByTagName("head")[0].appendChild(script)
        }
</script>

    这次请求,数据体中是["1","2","3","4"],是对应JsonpWriteController类中void方法体通过write方式写出来的。 

     

    当然了,前端页面这里的jsonp,是原生的,而jquery对jsonp的实现做了封装,它将其封装到ajax上,只不过需要指定dataType参数为jsonp。

    index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp</title>
    <style>
        #root input{padding:10px;border:1px solid #ddd;color:#fff;border-radius: 3px;background: lightgreen;font-size: 16px;}
        #root .box{padding:5px;}
        #root textarea{width:790px;height:100px;resize: none;}
    </style>
</head>
<body>
    <div id="root">
        <div class="box">
            <input type="button" value="jsonp" onclick="handleclick()"/>
        </div>
		<div class="box">
            <input type="button" value="jsonp by jquery" onclick="handleclick2()" />
        </div>
        <div class="box">
            <textarea id="output"></textarea>
        </div>
        
    </div>
	<script src="https://code.jquery.com/jquery-1.12.4.js" ></script>
	<script>
		function showData(data){
            var res = JSON.stringify(data)
            document.querySelector("#output").textContent = res
        }
        function handleclick(){
            var url = "http://127.0.0.1:8080/jsonp/test?callback=showData&_t=123"
            var script = document.createElement("script")
            script.setAttribute("src",url)
            script.onerror = null
            document.getElementsByTagName("head")[0].appendChild(script)
        }
	    var base_url = "http://localhost:8080/"
        function handleclick2(){
            $.ajax({
                url:base_url+"jsonp2/test",
                type:"get",
                dataType:"jsonp",
                //jsonp:"callback",
                success:function(res){
                    showData(res)
                },
                error:function(xhr,textStatus,errorThrown){
                    console.log("error")
                }
            })
        }
	</script>
</body>
</html>

    实现效果:

    

    这里两者请求不一样,一个带了callback=showData,那个是原生实现的,还有一个是jQuery封装了jsonp的实现,它没有设置callback参数,默认回调函数就是success,我们在success里面调用了showData。

    还有一种方式,就是指定jsonp参数,就是callback,同时,还需要指定jsonpCallback就是回调的真实函数,在这里是showData,而不用指定success函数。

function handleclick2(){
	$.ajax({
		url:base_url+"jsonp2/test",
		type:"get",
		dataType:"jsonp",
		jsonp:"callback",
		jsonpCallback:"showData",
		//success:function(res){
		//    showData(res)
		//},
		error:function(xhr,textStatus,errorThrown){
			console.log("error")
		}
	})
}

    jsonp实现跨域的两个缺点:

    1、需要服务端配合修改代码,

    2、只支持get请求。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luffy5459

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值