故事
離四點(diǎn)還有一個(gè)小時(shí),還有個(gè)批量回復(fù)的功能沒(méi)有做完,自從上次被夸了之后,在項(xiàng)目的估時(shí)上面盡可能地壓縮時(shí)間,希望能夠更快完成任務(wù),然后提交上線,但是,估計(jì)這一次,還是不行,周會(huì)不去開(kāi)了。
后臺(tái)寫(xiě)完,跑了測(cè)試用例,功能基本上就是我要的了,現(xiàn)在就差Web層的邏輯和前臺(tái)的頁(yè)面以及js。其實(shí)最討厭寫(xiě)的還是前臺(tái)的頁(yè)面,邏輯,莫名其妙會(huì)花很長(zhǎng)的時(shí)間,即使只是調(diào)整一些樣式,拼裝一些數(shù)據(jù),在大前端,前后端分離的背景下,這已經(jīng)是最低級(jí)的活兒。但是還是要得把整個(gè)功能給做完,至少自己還是能夠控制住的。
把之前單個(gè)提交的html代碼拿過(guò)來(lái),建了一個(gè)新的html文件,在后臺(tái)web層寫(xiě)了一個(gè)方法跳轉(zhuǎn)到這個(gè)html頁(yè)面。html頁(yè)面的樣式也很簡(jiǎn)單,就是一個(gè)簡(jiǎn)單的form表單。
<form class="form" action="" method="post" id="human">
id:<input name="id" id="id"/><br/>
名字:<input name="name" id="name"/><br/>
年齡<input name="age" id="age"/><br/>
<button type="submit" id="subBtn">提交</button>
</form>
既然貼了html,接著就是用js組裝數(shù)據(jù),然后使用ajax提交到后臺(tái)。繼續(xù)貼代碼唄。
$(function(){
var postNotSupport = {
init: function() {
this.initEvent();
},
initEvent: function() {
var _this = this;
// post not support
// _this.postNotSupport;
},
postNotSupport: function() {
var _this = this;
_this.saveAjax();
},
saveAjax: function() {
var human = {
id:$("#id").val(),
name:$("#name").val(),
age:$("#age").val()
}
$("#subBtn").on("click", function() {
$.ajax({
url:"/postNotSupport/save",
type:"post",
contentType:"application/json",
data:JSON.stringify(human),
sync:true
});
})
}
}
postNotSupport.init();
})
后臺(tái)是使用SpringMVC去接的,
@RequestMapping(value = "/postNotSupport/save", method = RequestMethod.POST)
@ResponseBody
public Response save(@RequestBody Human human) {
System.out.println(human);
return Response.success();
}
完事具備,就等調(diào)試了??墒牵屛蚁氩坏降囊荒痪谷怀霈F(xiàn)了,當(dāng)我點(diǎn)擊提交的時(shí)候,出現(xiàn)了‘POST’ not support的錯(cuò)誤,經(jīng)驗(yàn)告訴我,這個(gè)錯(cuò)誤和POST的關(guān)系幾乎為0,因?yàn)闆](méi)有一個(gè)人會(huì)犯一個(gè)前臺(tái)是POST提交的方式,而后臺(tái)確實(shí)用GET去接受的錯(cuò)誤,而且這個(gè)錯(cuò)誤實(shí)在是太容易發(fā)現(xiàn)了。(后面就是我的打臉時(shí)刻),再按照以往的經(jīng)驗(yàn),如果出現(xiàn)這個(gè)錯(cuò)誤,那么一定就是后臺(tái)報(bào)的錯(cuò),我只需要看后臺(tái)報(bào)錯(cuò)的異常堆棧信息,就可以解決了。
以往的經(jīng)驗(yàn),在一定的程度上,是可以幫助人的,但是在絕大多數(shù)的情況下,都是坑人的。經(jīng)驗(yàn)真的就像一個(gè)圈圈一樣,把你畫(huà)到圈圈里面,然后就只能夠看到圈圈里面的東西,外面的東西就看不到,頗有一葉障目不見(jiàn)泰山的感覺(jué)。
我繼續(xù)往下拉異常的信息,可以,讓我發(fā)慌的事情出現(xiàn)了,后臺(tái)就只拋了POST not support的異常,往下的就是一些sql語(yǔ)句信息,很郁悶,心里想著,不科學(xué)吧??戳讼聲r(shí)間,已經(jīng)是四點(diǎn)半了,周會(huì)已經(jīng)開(kāi)了半個(gè)小時(shí),不知道他們的色眼看人看得怎么樣。不去想了,五點(diǎn)前解決問(wèn)題,然后上線吧。
在web層打了一個(gè)斷點(diǎn),跑去頁(yè)面重新又試了一下,idea的黃色閃動(dòng)亮起,我知道,這時(shí)候的程序在這個(gè)斷點(diǎn)下停下來(lái)。切換到idea,紅色的高亮如期而至,看了下接受的數(shù)據(jù)信息,從前臺(tái)發(fā)過(guò)來(lái)的json,在后臺(tái)都轉(zhuǎn)換成的對(duì)象,這時(shí)候還沒(méi)有報(bào)錯(cuò)。繼續(xù)向下執(zhí)行,程序進(jìn)入到了Service層的方法,同時(shí),那個(gè)可怕的錯(cuò)誤也開(kāi)始顯現(xiàn),繼續(xù)運(yùn)行,DAO層插入數(shù)據(jù),返回前臺(tái),一切都很順利,除了那500的錯(cuò)誤,那個(gè)post not support。
既然后臺(tái)沒(méi)錯(cuò),那就去前臺(tái)的js排查吧,在ajax前面和后面的位置上打上debugger的標(biāo)準(zhǔn),讓程序在相關(guān)的地方停下來(lái),當(dāng)js運(yùn)行到第一個(gè)debugger的時(shí)候,一切都相安無(wú)事,當(dāng)運(yùn)行到第二個(gè)debugger的時(shí)候,數(shù)據(jù)還沒(méi)有返回到前臺(tái),異常就出現(xiàn)了。嘆了口氣,看了下時(shí)間,已經(jīng)是5點(diǎn),估計(jì)是發(fā)不了版了,如果失敗了,還得麻煩運(yùn)維他們,而且周末也來(lái)了,在這個(gè)節(jié)骨眼上,還是不發(fā)布吧,線上還運(yùn)行著一個(gè)正常的版本,而且這個(gè)版本還運(yùn)行得挺好的。于是,自己放松下了神經(jīng),也許卸下壓力,可以讓自己能夠排查出問(wèn)題來(lái)。
打開(kāi)百度,罵了一聲,其實(shí)百度的搜索質(zhì)量一般般,奈何用不了翻墻的軟件,就暫時(shí)用百度吧。百度的搜索結(jié)果,基本上都千篇一律,某一篇博文出來(lái),復(fù)制了一次又一次,所以即使點(diǎn)擊去十幾個(gè)鏈接,內(nèi)容基本都一樣。上面講的如果是json返回,需要加@ResponseBody,method的get方法和post方法要寫(xiě)清楚,諸如此類(lèi)的,我都一遍又一遍地檢查,什么都是按照流程來(lái)的,應(yīng)該不會(huì)有錯(cuò)。
靈光一閃,想想百度坑人的事情,想想網(wǎng)絡(luò)上99%的都是錯(cuò)誤的,只有1%的是不一定是對(duì)的原則。我默默打開(kāi)官網(wǎng),spring的官網(wǎng),真TM慢。等了半天,加載出來(lái)了,找到SpringMVC的內(nèi)容,看了下的例子,啊,為什么一毛一樣,都能夠出錯(cuò)???心里一萬(wàn)個(gè)問(wèn)號(hào),同時(shí)有一萬(wàn)個(gè)草泥馬在奔跑。時(shí)間來(lái)到五點(diǎn)半,他們開(kāi)會(huì)也回來(lái)了。胖子回來(lái),喝了口水,我心里嘀咕著,“那個(gè)上不了線了,我還在處理,還沒(méi)開(kāi)發(fā)完,周一上吧”,胖子點(diǎn)了點(diǎn)頭表示同意。
繼續(xù)debug,怎么還繼續(xù)下去?。?!只能求救呀?。?!
拍了拍胖子的桌面,說(shuō)我遇到了一個(gè)post不支持的錯(cuò)誤,問(wèn)怎么解決。呃,好吧,受到了無(wú)情的嘲諷,post不支持,你是用post去提交get請(qǐng)求了吧。我據(jù)理力爭(zhēng),ajax和后臺(tái)都是post的。不可能出現(xiàn)不對(duì)應(yīng)的情況。這時(shí)候,由于微信的上線,boss去忙其他的
嘿,大芳,你解決過(guò)post不支持的問(wèn)題嗎?大芳輕蔑一下,你是不是用post去提交get請(qǐng)求了,來(lái)來(lái)來(lái),打開(kāi)代碼,讓我看看,作為部門(mén)后臺(tái)開(kāi)發(fā)的唯一一名女將,多少有點(diǎn)王者風(fēng)范。你把這個(gè){}去掉,嗯,去掉,跑一遍,錯(cuò)誤還是如期而至,你這個(gè)用PostRequest吧,改掉,跑一遍,呵呵。然后我按照我一開(kāi)始的debug流程,又跑了一遍。
解決不了,就只能等待。
時(shí)間到了8點(diǎn)半,我已經(jīng)很絕望地坐了4個(gè)小時(shí)了,這4個(gè)小時(shí),我都在處理這個(gè)虛無(wú)縹緲的bug,仿佛一切都不順利,我到底做錯(cuò)了什么。胖子忙完其他的事情,招呼過(guò)來(lái),坐下,開(kāi)始debug,同時(shí),也圍過(guò)來(lái)一群同事,在討論這個(gè)問(wèn)題。
事情并沒(méi)有預(yù)想的一樣,胖子坐下,掃了一下代碼,說(shuō)你這個(gè)少了個(gè)分號(hào),然后拂袖而去,不留姓與名。他還是按照異常的堆棧信息,一個(gè)斷點(diǎn)一個(gè)斷點(diǎn)打進(jìn)去,打到最后一個(gè)類(lèi)是DispatcherServlet,這是SpringMVC的內(nèi)容分發(fā)器,前臺(tái)所有的請(qǐng)求都會(huì)經(jīng)過(guò)這個(gè)類(lèi)進(jìn)行分發(fā)。ajax請(qǐng)求了一次,往后臺(tái)提交了一份數(shù)據(jù),理論上,后面如果沒(méi)有其他的操作,這個(gè)請(qǐng)求就停止了,也就是沒(méi)有其他的請(qǐng)求會(huì)進(jìn)來(lái)。但是,卻進(jìn)來(lái)了一個(gè)頁(yè)面的POST請(qǐng)求。
原因已經(jīng)知道了,就是這個(gè)頁(yè)面的POST請(qǐng)求,但是,他是怎么觸發(fā)的,找到這一步,又一次陷入了迷茫,圍過(guò)來(lái)的一群人都不知道怎么回事。繼續(xù)處理,在相關(guān)的POST請(qǐng)求上打上斷點(diǎn),但是這個(gè)斷點(diǎn)并沒(méi)有進(jìn)來(lái),說(shuō)明ajax請(qǐng)求之后的轉(zhuǎn)發(fā)并不是后臺(tái)處理的,原因就只有在前臺(tái)上。
看了一遍又一遍的js文件,看不出有什么問(wèn)題,大家都陷入了絕望的時(shí)候,大宏將了句,會(huì)不會(huì)是html的問(wèn)題,胖子急忙去找html文件,看到那個(gè)form表單,似乎明白了點(diǎn)什么,“你這個(gè)form表單,也提交了一次吧?”,他把form相關(guān)的內(nèi)容注釋掉,只留下input,再請(qǐng)求一次,這個(gè)bug就沒(méi)有出現(xiàn)了。
時(shí)間已經(jīng)到了9點(diǎn)10分,經(jīng)過(guò)了5個(gè)多小時(shí),這個(gè)未知bug終于是定位到了。
可是為什么在原來(lái)的頁(yè)面上,html文件也是這么寫(xiě)的,卻沒(méi)有出現(xiàn)這個(gè)異常的信息?
$(function(){
var postNotSupport = {
init: function() {
this.initEvent();
},
initEvent: function() {
var _this = this;
// 提交成功
_this.validPost();
},
validPost: function() {
var _this = this;
$("#human").validate({
submitHandler:function(form){
_this.saveAjax();
return false;
}
});
},
saveAjax: function() {
var human = {
id:$("#id").val(),
name:$("#name").val(),
age:$("#age").val()
}
$("#subBtn").on("click", function() {
$.ajax({
url:"/postNotSupport/save",
type:"post",
contentType:"application/json",
data:JSON.stringify(human),
sync:true
});
})
}
}
postNotSupport.init();
})
答案就在js文件上面,原來(lái)的js是有做字段規(guī)則校驗(yàn)的,使用jquery validate的submitHandler方法提交表單,而且還寫(xiě)了個(gè)return false的語(yǔ)句,這個(gè)return語(yǔ)句,正是jquery阻止瀏覽器默認(rèn)行為的方法,也就是說(shuō),阻止了form表單的submit方法。呀,坑死了。以前的項(xiàng)目寫(xiě)法真多樣,一個(gè)項(xiàng)目里面表單提交,ajax提交的基本都混雜著,真是坑人的混雜。
不管,吃飯去。幾個(gè)小時(shí)的debug,加上花椒油,真的身心疲憊,真不想講話。
代碼
感悟
- 項(xiàng)目規(guī)范很重要?。?!html的寫(xiě)法,js的寫(xiě)法,java的寫(xiě)法都要按規(guī)范了,提交表單要不用submit,要不用ajax,混合雙打反而是坑了自己坑了后面的人;
- 寫(xiě)后臺(tái)的太注重邏輯了,基本上debug都是集中在java和js文件,而html文件只會(huì)認(rèn)為是一個(gè)頁(yè)面樣式,不會(huì)出岔子,而這次卻出了岔子;
- debug要整個(gè)流程跟下來(lái),從前臺(tái)的頁(yè)面,js,到后臺(tái)的controller層,service層,dao層,都要跟;
- 如果解決不了,就不要鉆牛角尖,問(wèn)下別人,或者喝個(gè)茶放松下;
- 框架的原理需要清楚,至少也要了解,這樣更容易排錯(cuò)。