擴充套件springMVC訊息轉換器來支援jsonp資料格式
阿新 • • 發佈:2019-02-18
1.JSONP是用來解決json跨域問題的技術。即將傳輸的json轉化為js指令碼。
Callback是JSONP的實現的一種方式,例:getData([{"id":1,"title":"XXXX"},{"id":2,"title":"YYYYY"}])。當瀏覽器讀取到funName(json文字)就會用js的語法解析,然後呼叫定義好的js getData函式,以回撥函式的形式獲取json資料。
2.為什麼要在spring mvc中對於jsonp要自己處理?
因為spring mvc預設是不支援jsonp的,所以需要我們自己處理
3.如何在伺服器端實現對JSONP支援
根據前臺請求url是否攜帶callback,來判斷是否需要jsonp。
a)如果攜帶,說明出現跨域問題,伺服器就需要對跨域問題進行處理,處理方式這裡採用callback,方法執行完後伺服器會返回指定callback名的js script。
b)如果沒攜帶,說明沒有跨域問題,伺服器返回普通json
之前的解決方式:
在controller中自行處理,判斷請求中是否攜帶callback回撥函式名。如果沒有,則不對json進行處理。這種情況多適用於同一個系統下的ajax請求,不出現ajax跨域問題情況下。如果有,就對json進行處理,使其符合jsonp規範。這種情況適用於不同系統的ajax請求,出現跨域問題的情況下。package com.taotao.manager.controller.api; import javax.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import com.fasterxml.jackson.databind.ObjectMapper; import com.taotao.common.ItemCatResult; import com.taotao.manager.service.ItemCatService; @Controller @RequestMapping("/api/item/cat") public class ApiItemCatController { @Resource private ItemCatService itemCatService; private ObjectMapper objectMapper=new ObjectMapper(); @RequestMapping(method=RequestMethod.GET) /** * required=false 當訪問此方法沒有攜帶對應callback的引數值,則屬性值預設為null。如果不設定並且沒有攜帶資料,會丟擲異常 * @param callback 回撥函式名 * @return 符合jsonp規範的json格式資料 */ public ResponseEntity<String> all(@RequestParam(value="callback",required=false) String callback){ try { ItemCatResult result = itemCatService.queryAllToTree();//從後臺查詢處資料 if(result!=null){ String resultJson = objectMapper.writeValueAsString(result);//jackson元件把後臺資料轉化為json if(StringUtils.isEmpty(callback)){//前臺是否傳callback回撥函式名 return ResponseEntity.ok(resultJson);//沒有的話,返回普通的json }else{ return ResponseEntity.ok(callback+"("+resultJson+")");//有,返回封裝後的json } } return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } }
因為返回型別是ResponseEntity<String>,後臺string物件轉換為前臺資料是在StringHttpMessageConverter訊息轉換器中轉換的,它預設的編碼是iso-8859-1,前臺接收的資料如果產生亂碼,我們需要在spring-mvc配置檔案中寫如下配置
<mvc:annotation-driven> <!-- 自定義訊息轉換器 --> <mvc:message-converters register-defaults="true"> <!-- 自定義訊息轉換器,設定編碼為utf-8,防止responseEntity<String>轉換成json物件輸出亂碼 --> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg index="0" value="utf-8"></constructor-arg> </bean> </mvc:message-converters> </mvc:annotation-driven>
這種方式實現比較麻煩。因為在每個需要json跨域問題的controller中都要自己手動寫程式碼判斷處理
通過擴充套件springMVC訊息轉換器使得springMVC預設支援jsonp
首先寫一個類繼承org.springframework.http.converter.json.MappingJackson2HttpMessageConverter類,並重寫writeInternal()方法package com.taotao.common.spring.exetend.converter.json;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
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;
}
}
然後在springMVC的配置檔案中進行配置
<mvc:annotation-driven>
<!-- 自定義訊息轉換器 -->
<mvc:message-converters>
<!-- 自定義支援jsonp的MappingJackson2HttpMessageConverter訊息轉發器 -->
<bean class="com.taotao.common.spring.exetend.converter.json.CallbackMappingJackson2HttpMessageConverter">
<!-- 做jsonp的支援的標識,回撥函式名,在請求引數中加該引數 -->
<property name="callbackName" value="callback"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
然後controller還是可以按照原來的寫法,但此時已經預設支援jsonp
package com.taotao.manager.controller.api;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taotao.common.ItemCatResult;
import com.taotao.manager.service.ItemCatService;
@Controller
@RequestMapping("/api/item/cat")
public class ApiItemCatController {
@Resource
private ItemCatService itemCatService;
private ObjectMapper objectMapper=new ObjectMapper();
@RequestMapping(method=RequestMethod.GET)
/**
* required=false 當訪問此方法沒有攜帶對應callback的引數值,則屬性值預設為null。如果不設定並且沒有攜帶資料,會丟擲異常
* @param callback 回撥函式名
* @return 符合jsonp規範的json格式資料
*/
public ResponseEntity<ItemCatResult> all(@RequestParam(value="callback",required=false) String callback){
try {
ItemCatResult result = itemCatService.queryAllToTree();
if(result!=null){
return ResponseEntity.ok(result);
}
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
測試: 普通的獲取json方式:http://manage.taotao.com/rest/api/item/cat
解決跨域請求問題下,請求json資料的url:http://manage.taotao.com/rest/api/item/cat?callback=category.getDataService