什么是跨域
浏览器对js的安全限制(即同源策略)不允许跨域访问,即当前浏览器不能访问满足以下条件的资源
- IP不同(和当前浏览器,以下同)
- 协议不同(如http和https)
- 域名不同
- 域名相同,但端口不同
为什么要跨域
同一产品的不同前端系统(如PC端,安卓端,ios等)需要的业务逻辑相似
为了提高代码复用率,避免为每一种前端系统项目都开发一套相似的业务逻辑代码
专门开发一个后端系统项目来实现业务逻辑,让多个前端系统共同调用即可
优点 | 缺点 |
---|---|
提高代码复用率 | 需要发布Webservice |
提高可扩展性 | 系统之间需要远程调用 |
降低耦合度 |
跨域的实现
一、项目准备
准备两类项目,两者间通过Webservice进行通信
项目类别 | 项目个数 | 项目特点 | 项目组成 |
---|---|---|---|
服务层 | 一个 | 没有表现层,只有业务逻辑,需要连接数据库,需要发布服务 | controller,service,pojo,dao |
表现层 | 一个或多个 | 有表现层,没有业务逻辑,不需连接数据库 | controller,service,pojo,jsp |
二、解决跨域
方法 | 请求数据时间 | 实现方式 |
---|---|---|
jsonp | 页面加载完毕之后(js代码在页面加载完毕后执行) | 前端ajax跨域访问js片段 (动态加载数据,网页更新少,活跃度低,不利于搜索引擎优化) |
HttpClient | 页面初始化时 | 在service层用CloseableHttpClient类发送跨域请求,在controller层用addAttribute为对应的视图中的参数赋值 |
jsonp:
jsonp跨域的原理:js不能跨域访问数据,但可以跨域访问js片段
使用<script>
标签,标签中指定src=需要跨域访问的资源地址
或者将数据用js方法包装后在url中传递callback参数回调该方法
-
客户端在请求资源的url后传递一个callback参数,参数值为回调方法的名称
url = "资源路径?callback=回调方法名称";
-
在客户端用jsonp发送请求
$.getJSONP(url);
-
在服务端用回调方法将数据包装成js片段,以
getData(data)
的形式返回数据//方法一 @RequestMapping(value = "资源路径", produces = MediaType.APPLICATION_JSON_VALUE+";charset=utf-8") @ResponseBody private String getItemCatList(String callback) { //取数据 ItemCatResult itemCatResult = itemCatService.getItemCatList(); //支持jsonp调用 String json = JsonUtils.objectToJson(itemCatResult); return "callback("+json+")"; } //方法二 @RequestMapping("资源路径") @ResponseBody private Object getItemCatList(String callback) { //取数据 ItemCatResult itemCatResult = itemCatService.getItemCatList(); //支持jsonp调用 MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(itemCatResult); mappingJacksonValue.setJsonpFunction(callback); return mappingJacksonValue; }
HttpClient:
在service层用java代码模拟浏览器跨域请求数据
- 准备:在项目中添加依赖的jar包:httpclient、httpcore
- 模拟get请求数据:
public void testHttpGet() throws Exception { //创建HttpClient对象来准备发送请求 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建httpGet设置访问的url地址 HttpGet httpGet = new HttpGet("http://www.baidu.com"); //执行请求,创建httpRespond CloseableHttpResponse httpResponse = httpClient.execute(httpGet); //创建httpentity接收返回数据 HttpEntity entity = httpResponse.getEntity(); //转化接收数据格式 String html = EntityUtils.toString(entity, "UTF-8"); System.out.println(html); //关闭请求、关闭响应 httpResponse.close(); httpClient.close(); }
- 模拟post请求数据:
public void testHttpPost() throws Exception { //创建HttpClient对象来准备发送请求 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建httpPost设置访问的url地址 HttpPost httpPost = new HttpPost("http://localhost:8082/posthtml.html"); //准备需要发送的数据 List<NameValuePair> list = new ArrayList<>(); list.add(new BasicNameValuePair("name","张三")); list.add(new BasicNameValuePair("pass","123")); //创建StringEntity将需要发送的数据包装 StringEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); //将包装好的数据添加到请求中 httpPost.setEntity(entity); //发送请求,创建httpResponse接收请求响应 CloseableHttpResponse httpResponse = httpClient.execute(httpPost); //创建httpEntity从响应中取出返回数据 HttpEntity httpEntity = httpResponse.getEntity(); //用EntityUtils转化接收数据格式 String res = EntityUtils.toString(httpEntity,"UTF-8"); System.out.println(res); //关闭响应、请求 httpResponse.close(); httpClient.close(); }
- 给前端页面赋值:
@RequestMapping("/index") public String showIndex(Model model) { //获取跨域请求返回的数据 String adData = contentService.getAd1List(); //传递给前端页面 model.addAttribute("adData",adData); //返回指定的前端页面视图 return "index"; }