複習電商筆記-38-JS跨越和JSONP跨域
*JS跨域
首頁載入完後滑鼠移動到一級欄目上,觸發js呼叫,如下圖所示可以看到返回的json串。但有個奇怪的地方,怎麼串被category.getDataService()括起來和一般的json串不同呢?
首頁選單json串格式
什麼是跨域?
我們經常會在頁面上使用ajax請求訪問其他伺服器上的資料,此時,客戶端會出現跨域問題。它是由於javascript語言安全限制中同源策略造成的。簡單的說,同源策略是指一段指令碼只能讀取來自同一來源的視窗和文件的屬性。這裡同一來源是指主機名、協議和埠號的組合。
例如:
實現跨域方案:
方案一:拼接這樣一個串返回。
方案二:採用jsonp方式。
方案一簡單易實現,但通用性不強。所以我們採用方案二。
Jsonp解決跨域訪問原理
jQuery不支援跨域訪問
在後臺jt-manage-web專案中建立test.json。
{
"key":"jt good!"
}
建立一個test.htm檔案
<!doctype html> <html> <head> </head> <body> <script type="text/javascript" src="http://manage.jt.com/js/jquery-easyui-1.4.1/jquery.min.js"></script> <script type="text/javascript"> $(function(){ $.get("http://manage.jt.com/test.json",function(data){ alert(data.key); }); }); </script> </body> </html>
將test.htm檔案拷貝到jt-web專案中
訪問:http://www.jt.com/test.htm
XMLHttpRequest cannot load http://manage.jt.com/test.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.jt.com' is therefore not allowed access.
從www.jt.com去訪問了manage.jt.com網站,所以跨域請求資源,因為在js設計時為了安全性設計為同源策略,跨域不能被訪問,jquery也是js,遵循同源策略,所以jquery不允許跨域訪問導致報錯。
如何解決這個問題呢?
js雖然被設計為同源,但有個標籤<script>標籤例外,它可以跨域請求資源。請求資源中必須包含回撥函式,相當於自定義了一個js函式,只不過寫法特殊。
修改test.json
test({
"key":"jt good!"
})
修改test.htm
<!doctype html>
<html>
<head>
</head>
<body>
<script type="text/javascript">
//在test.json中回撥
function test(data){
alert(data.key);
}
</script>
<script type="text/javascript" src="http://manage.jt.com/test.json"></script>
</body>
</html>
達到跨域請求的效果;
為什麼<script>中的內容就可以跨域呢?
<script type="text/javascript">
function test(data){
alert(data.key);
}
var data = {key:"jt good!"}
test(data);
test({key:"jt good!"});
</script>
從上面例子就可以看出<script src=“”></script>標籤實現的就是將src請求返回的內容,變成js的語句。然後這個語句就是呼叫test函式,而引數就是json字串,被js轉成js的物件。
<script type="text/javascript" src="http://manage.jt.com/test.json"></script>獲取到的是字串,怎麼能直接解析字串呢?
var obj = eval("({'key':'ok'})");
fn(obj);
<script>返回的字串被eval轉換為js物件
SpringMVC支援Jsonp方式
整體配置方式
springmvc是通過<mvc:annotation-driven/>中注入了一個json的轉換,然後只需要在類上加@ResponseBody註解,就可以自動將物件轉換為json串。我們來擴充套件它。
第一步:引入擴充套件類,繼承MappingJackson2HttpMessageConverter:
package com.jt.common.spring.exetend.jackson;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonProcessingException;
public class CallbackMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
// 做jsonp的支援的標識,在請求引數中加該引數
private String callbackName;
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
// 從threadLocal中獲取當前的Request物件
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes()).getRequest();
String callbackParam = request.getParameter(callbackName);
if (StringUtils.isEmpty(callbackParam)) {
// 沒有找到callback引數,直接返回json資料
super.writeInternal(object, outputMessage);
} else {
JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
try {
String result = callbackParam + "(" + super.getObjectMapper().writeValueAsString(object)
+ ");";
IOUtils.write(result, outputMessage.getBody(), encoding.getJavaName());
} catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
}
public String getCallbackName() {
return callbackName;
}
public void setCallbackName(String callbackName) {
this.callbackName = callbackName;
}
}
第二步:加入依賴jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
第三步:修改jt-manage-web的springmvc-config.xml
<!-- MVC註解驅動 -->
<mvc:annotation-driven>
<!-- 採用自定義方案 -->
<mvc:message-converters>
<!-- 定義json轉化器,支援json跨域 -->
<bean
class="com.jt.common.spring.exetend.jackson.CallbackMappingJackson2HttpMessageConverter">
<!-- 跨域請求中的請求引數名 -->
<property name="callbackName" value="callback"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
這樣擴充套件後,之前的程式碼絲毫不用改變。而且方法可以通用。
單獨配置方式
import org.springframework.http.converter.json.MappingJacksonValue;
//檢查 http://sso.jt.com/user/check/{param}/{type}
@RequestMapping("/check/{param}/{type}")
@ResponseBody
public Object check(String callback, @PathVariable String param,@PathVariable Integer type){
try{
Boolean b = userService.check(type, param);
MappingJacksonValue mappingVal = new MappingJacksonValue(SysResult.oK(b));
mappingVal.setJsonpFunction(callback);
return mappingVal;
}catch(Exception e){
return SysResult.build(201, "檢查使用者、手機、郵箱出錯!");
}
}
問題:messageConverter擴充套件後引起的問題
上傳檔案時,會出現下面錯誤,如下圖所示:
格式錯誤,是什麼原因造成這個問題呢?
是由於我們修改了<mvc:annotation-driven>,它就將所有的結果轉換為json格式。那之前只<mvc:annotation-driven>怎麼就可以?它預設會加入什麼?檢視spring-webmvc-4.1.3.RELEASE.jar下的AnnotationDrivenBeanDefinitionParser類的getMessageConverters方法,在521行它做了判斷:
if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-
defaults"))) {
如果使用者指定了messageConverter,它不會再將給我們註冊預設的converter。由於我們加了資訊轉換後,那就只能轉換成json了。那它預設加了什麼?檢視525行可以看到:
RootBeanDefinition stringConverterDef =
createConverterDefinition(StringHttpMessageConverter.class, source);
預設它加入了一個字串格式轉換。返回一個純字串。
將它新增到jt-manage-web的springmvc-config.xml檔案中:
<!-- MVC註解驅動 -->
<mvc:annotation-driven>
<!-- 採用自定義方案 -->
<mvc:message-converters>
<!-- 定義文字轉化器 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg index="0" value="UTF-8" />
</bean>
<!-- 定義json轉化器,支援json跨域 -->
<bean
class="com.jt.common.spring.exetend.jackson.CallbackMappingJackson2HttpMessageConverter">
<!-- 跨域請求中的請求引數名 -->
<property name="callbackName" value="callback"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
重啟,測試檔案上傳,可以正常使用了。