今天一個正在進行中的案子,接到開發伙伴來的訊息,說他用POST來的資料,進去資料庫都是空白。看到這個訊息,我的心頭小鹿禁不住一陣亂撞,這下不得了,趕快測試看看:Model寫入資料庫沒問題,Controller丟資料給Model沒問題,難道是接收POST資料有問題?
於是趕緊打開心愛的Notepadd++看看程式,由於我是負責寫API接收Client丟資料來處理,然後回傳處理結果,而且Client端有可能用GET也可能用POST丟來,資料欄位多少不一定(這是我們訂好的規格),所以接收資料我是這樣寫的:
$data_array = $this->input->get_post(NULL,TRUE);
這樣我可以接收整個POST資料或GET資料,而且CI會幫我把整個$_POST或$_GET陣列過濾掉XSS然後傳回陣列給我。至於順序呢,依照CI的官方手冊說,它會先看POST再看GET。好了,這時候我們一般人的理解應該就是:如果有POST資料,它就用POST,然後傳回給我,如果沒有POST,它會再去看GET,然後一樣處理好傳回給我。
但,真的是這樣嗎? 當我單獨用$this->input->get(NULL,TRUE)或$this->input->post(NULL,TRUE)都沒問題,可以正常接收到整個GET或POST來的陣列,但是get_post這樣用就挫賽了。舉個例子,譬如我的某個API的URL點是這樣的:
http://api.example.com/v1/user/add
他可以接受POST或GET丟任意數量的資料來,我就接收起來,處理好存入資料庫。好了,如果Client端是這樣丟來的:
http://api.example.com/v1/user/add?token=9c6b99753ff8c41cda0331d634d2a4eac8e53a34
然後POST內容是這樣
name=David+Pai&mobile=0999123456
那這樣透過$this->input->get_post(NULL,TRUE)會得到什麼呢? 你以為$data_array會是這樣:
1 2 3 4 5 6 7 8 |
array(3) { ["token"]=> string(40) "9c6b99753ff8c41cda0331d634d2a4eac8e53a34" ["name"]=> string(9) "David Pai" ["mobile"]=> string(10) "0999123456" } |
結果不是,只剩下這樣:
1 2 3 4 |
array(1) { ["token"]=> string(40) "9c6b99753ff8c41cda0331d634d2a4eac8e53a34" } |
只剩下GET來的資料,POST全都不見了。
怎麼會這樣,不是說會先看POST再看GET,那至少給我POST資料吧,怎麼反而只給GET資料呢? 好的,這是我的膝蓋第一次被CI射了一箭,直到我去追了Input Class的原始碼…讓我們看看get_post這個方法:
1 2 3 4 5 6 7 8 9 10 11 |
function get_post($index = '', $xss_clean = FALSE) { if ( ! isset($_POST[$index]) ) { return $this->get($index, $xss_clean); } else { return $this->post($index, $xss_clean); } } |
看出來了嗎? 這下真相終於大白,如果你這樣用$this->input->get_post(NULL,TRUE),它會因為$_POST[NULL]判斷為false而直接跑去用$this->get(NULL,TRUE),所以得到的結果就只剩下GET來的東西了。
所以,對於CI的get_post,有幾個地方要注意:
- 它沒有像get或post那樣,第一個參數指定為NULL就可以處理整個$_POST或$_GET
- 如果第一個參數指定為NULL,只會得到跟get一樣的結果
- 如果要像手冊說的那樣,第一個參數一定要指定
那麼,如果要像上面那個例子那樣,我POST和GET都要接收,然後碰到相同的以POST為先,這樣怎麼做呢? 既然CI沒有這樣的方法,那我們只好自力救濟啦,分開來接,然後再自己merge囉:
1 2 3 4 5 6 7 |
$data_get = $this->input->get(NULL, TRUE); $data_post = $this->input->post(NULL, TRUE); $data_get = is_array($data_get) ? $data_get : array(); $data_post = is_array($data_post) ? $data_post : array(); $data_array = array_merge($data_get, $data_post); |
於是,這個Bug解決了,而我的膝蓋也復原了…
又学习了,不过我在项目中大多都是指定post或get的name然后接收的。
呵呵,其實我平常也都是指定name接收的,只是碰到這個案子特殊,name是不知道的情況下才會這樣用
套您的話,我也透過這次學習了^^
Hey! Do you use Twitter? I’d like to follow you if that would be okay. I’m undoubtedly enjoying your blog and look forward to new posts.
I am adding Twitter to my social link on top right block of this page. You can follow that.