今天在上班的時(shí)候抵拘,因?yàn)橐婕暗絪pringmvc寫(xiě)json接口哎榴,通過(guò)ajax寫(xiě)數(shù)據(jù)到前端,然后就發(fā)現(xiàn)一個(gè)很神奇的事情。
當(dāng)我使用@RestController或者@ResponseBody的時(shí)候叹话,前端拿到的數(shù)據(jù)如下圖:
很顯然這是一個(gè)json對(duì)象偷遗,而不是一個(gè)json字符串。
后臺(tái)代碼如下:
@RestController
public class LoginRestController {
@Autowired
private LoginAction loginAction;
@RequestMapping(value = DuMockUrlConstants.LOGIN_JSON,method = {RequestMethod.POST})
public RequestResult uploadJar(UserVO userVO){
loginAction.handle(new LoginRequest(userVO.getLoginName(),userVO.getPassword()),new ActionResponse());
return RequestResult.success();
}
}
@AllArgsConstructor
@Data
@ToString(callSuper = true)
@RequiredArgsConstructor
public class RequestResult<T> extends AbstractResquestResult{
private T data;
public static RequestResult success(){
return new RequestResult(RespEnum.SUCCESS.getCode(),RespEnum.SUCCESS.getMemo(),null);
}
public static RequestResult fail(){
return new RequestResult(RespEnum.ERROR.getCode(),RespEnum.ERROR.getMemo(),null);
}
public RequestResult(String code, String message, T data) {
super(code, message);
this.data = data;
}
public RequestResult(String code, String message) {
super(code, message);
}
}
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Data
public class AbstractResquestResult implements Serializable{
@NonNull
protected String code;
@NonNull
protected String message;
}
@AllArgsConstructor
public enum RespEnum {
@Getter
private String code;
@Getter
private String memo;
SUCCESS("000000","成功"),
}
簡(jiǎn)單來(lái)講驼壶,代碼實(shí)質(zhì)是返回了一個(gè)RequestResult對(duì)象氏豌,該對(duì)象的code為000000,message為成功热凹,很顯然是一個(gè)登陸的接口泵喘。
但當(dāng)我把代碼改成如下:
@RestController
public class LoginRestController {
@Autowired
private LoginAction loginAction;
@RequestMapping(value = DuMockUrlConstants.LOGIN_JSON,method = {RequestMethod.POST})
public RequestResult uploadJar(UserVO userVO,HttpServletResponse response) {
try {
response.getWriter().write(JSON.toJSONString(RequestResult.success()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
結(jié)果成了如下圖所示,返回的是json格式的字符串般妙,和上文通過(guò)@RestController,@ResponseBody不一樣的數(shù)據(jù)纪铺。這里的亂碼先忽略不用管。
為什么會(huì)出現(xiàn)這樣的問(wèn)題碟渺,按照我的理解鲜锚,springmvc肯定底層會(huì)做json格式數(shù)據(jù)轉(zhuǎn)換,轉(zhuǎn)換成json苫拍,然后寫(xiě)入到流中芜繁,難道是我寫(xiě)json格式的姿勢(shì)不正確?
帶著這個(gè)疑問(wèn),動(dòng)手調(diào)試springMvc源代碼绒极,從DispatcherServlet開(kāi)始骏令,一路找相應(yīng)的返回值,最終在HandlerAdapter中找到調(diào)用反射獲取返回對(duì)象RequestResult該對(duì)象垄提。
找到該對(duì)象榔袋,差不多往輸出流中寫(xiě)數(shù)據(jù)也就快到了,然后繼續(xù)往下看铡俐。
調(diào)試到RequestResponseBodyMethodProcessor對(duì)象時(shí)凰兑,發(fā)現(xiàn)了寫(xiě)流的地方,就在上文單步調(diào)試的位置审丘,終于要找到了聪黎。。备恤。然而下班了。
然后下班途中就順手在我們精盡XXX小分隊(duì)的微信群中問(wèn)了一句锦秒,如下圖:
在各位大佬的集思廣益之下露泊,終于答案浮出水面:
一個(gè)關(guān)鍵字,HttpMessageConverter浮出水面旅择,終于在剛才那個(gè)方法的實(shí)現(xiàn)類中找到了write方法惭笑。如下:
MappingJackson2CborHttpMessageConverter是HttpMessageConvert的實(shí)例,通過(guò)write方法寫(xiě)數(shù)據(jù),最終返回的是Json對(duì)象沉噩,而不是json數(shù)據(jù)串捺宗。
然后通過(guò)下面代碼,證明了這一點(diǎn):
@RequestMapping(value = DuMockUrlConstants.LOGIN_JSON,method = {RequestMethod.POST})
public RequestResult uploadJar(UserVO userVO,HttpServletResponse response){
RequestResult requestResult= RequestResult.success();
MappingJackson2HttpMessageConverter converter=new MappingJackson2HttpMessageConverter();
try{
converter.write(requestResult, requestResult.getClass(), MediaType.APPLICATION_JSON_UTF8, new ServletServerHttpResponse(response));
}catch (Exception e){
e.printStackTrace();
}
return null;
}
終于川蒙,通過(guò)@ResponseBody和@RestController和通過(guò)流寫(xiě)入json的格式實(shí)現(xiàn)一樣了蚜厉。
不得不感嘆人外有人,天外有天畜眨,謙虛使人進(jìn)步昼牛,查了一下午不敵別人一句話來(lái)得快,要虛心學(xué)習(xí)了康聂。
局勢(shì)有了新突破贰健,深夜總會(huì)給人很多思考,大半夜群里一聲吼恬汁,嚇得我趕緊起來(lái)看看伶椿。
代碼做了如下改動(dòng):
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(JSON.toJSONString(requestResult));
結(jié)果如第一個(gè)圖一樣,還以為是spring本身做了轉(zhuǎn)換氓侧,原來(lái)是我自己的response的ContentType未設(shè)置導(dǎo)致的問(wèn)題脊另。
好吧,mark一下這個(gè)問(wèn)題甘苍,希望后面不要有人踩這個(gè)坑尝蠕。