今天看开源项目的时候发现一个对象不太理解
Spring中MappingJacksonValue
这个对象,于是抱着学习的心态去查了一下资料。
发现涉及浏览器跨域,jsonp
的方面于是好好的填上这个坑。
下面我们来看看一个例子:
前端的端口号是8081
这是我用vue+axios写的测试
methods: {
getInfo(){
this.axios.get('http://localhost:8080/api/genres').then(result=>{
console.log(result)
},error=>{
console.log(error)
})
}
后端服务器地址是:http://localhost:8080/api/genres
以上就服务器都是本地,就端口号不同
现在我们来看看前端向后端请求数据时会发生什么
Access to XMLHttpRequest at 'http://localhost:8080/api/genres' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
报错为什么??不应该得到数据的吗?以上就是一个简单的跨域了。
那为什么script
、img
等标签不会有跨域的问题?
因为这些标签本身都是可以允许跨域的。
**跨域的产生来源于现代浏览器所通用的‘同源策略’,所谓同源策略,是指只有在地址的: **
上面的这个demo就违反了同源策略中的端口名不一样
其实浏览器已经把请求发送出去了,只不过得到数据的时候被浏览器给拦截了而已
跨域的访问会带来许多安全性的问题,比如,cookie一般用于状态控制,常用于存储登录的信息,如果允许跨域访问,那么别的网站只需要一段脚本就可以获取你的cookie,从而冒充你的身份去登录网站,造成非常大的安全问题,因此,现代浏览器均推行同源策略。
其实对于浏览器跨域的解决有很多种方法,这里我就不一一详细接受了,其他方式我也不太了解。
JSONP:jsonp是解决浏览器跨域的一个方案。
通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
原生javascript
实现:
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://localhost:8080/login?user=admin&callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
jquery:
$.ajax({
url: 'http://www.localhost.com:8080/login',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "handleCallback", // 自定义回调函数名
data: {}
});
vue.js:
this.$http.jsonp('http://www.localhost.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
只能GET,不能POST方式
由于是从其它域中加载代码执行,因此如果其他域不安全,很可能会在响应中夹带一些恶意代码。
要确定 JSONP 请求是否失败并不容易。虽然 HTML5 给 script 标签新增了一个 onerror 事件处理程序,但是存在兼容性问题。
后台同样需要处理返回到前台的数据
package controller.admin.api;
import model.entity.Genre;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.bind.annotation.*;
import service.GenreService;
import java.util.List;
@RestController
@RequestMapping("/api/genres")
public class GenreController {
private GenreService genreService;
@Autowired
public GenreController(GenreService genreService) {
this.genreService = genreService;
}
@GetMapping("{genreId:\\d+}")
public Object getById(@PathVariable("genreId") int genreId, String callback) {
//需要返回的数据
Genre genre = genreService.fetchById(genreId);
//callback 这个是js调用时传过来的参数,内容就是回调的方法名
//第一种方式 ,把我们返回的数据转JSON后,然后拼接我们在js中定义的方法名,把json数据作为参数传递进去
//返回 String
/*String jsonStr = JsonUtils.objectToJson(itemCat);
return callback + "("+jsonStr+");";*/
//第二种方式,使用Spring自带对象,前提是需要在Srping4.0的版本才有的哟。
//返回 Object
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(genre);
mappingJacksonValue.setJsonpFunction(callback);
return mappingJacksonValue;
}
}
最后来看看java
后台返回的jsonp
的数据,跟原本的json数据是不一样的。
/**/handleCallbak({"id":1,"name":"流行音乐","description":"流行音乐(Popular music)是属于一种有着广泛听众极具吸引力音乐,相较于艺术音乐站和传统音乐。流行音乐是一个不分年龄人人共享音乐以“雅俗共赏”通称。"});
现在是可以访问的了。
但是好像springmvc
已经不推荐mappingJacksonValue.setJsonpFunction(callback);
这种方式了。
这种方式前端不需要做任何处理,后端处理就ok
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 跨域请求适配
*
* @author HarryZhang
*/
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")//允许域名访问,如果*,代表所有域名
.allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")
;
}
};
}
}
这种方式简单方便(推荐)。