事發緣由
在 .Net Framework 4.6.2 MVC 的 ApiController 中,某個查詢資料列表的方法除了提供查詢條件的參數以外,還有提供選擇性的分頁參數。也就是像這樣子:
[HttpGet]
public IEnumerable<Boo> GetBoos(
[FromUri] SearchBooParameter parameter,
[FromUri] PagingParameter paging = null)
{
// 呼叫 Service 查資料...
}
由於需要調整該功能的預設排序,改為由大到小,又不想背負更改大量共用的 PagingParameter
去影響到其他使用到的地方,決定在 Controller 這裡簡單用預測值加上判斷處理一下就好
相信著「若使用者沒有傳遞 paging 相關的參數,應該就會是給定的預設值 null
吧!」的我,用了 if (paging is null)
進行判斷:若是 null
的情況就將其中用來標示排序方向的成員 isDesc
設定為 true,開開心心交差。
[HttpGet]
public IEnumerable<Boo> GetBoos(
[FromUri] SearchBooParameter parameter,
[FromUri] PagingParameter paging = null)
{
if (paging is null)
{
paging = new PagingParameter();
paging.isDesc = true; // 預設由大到小
}
// 呼叫 Service 查資料...
}
但實際使用之後發現:即使只有傳入查詢條件參數、未傳遞 paging 時,資料仍然由小到大顯示,且 paging.isDesc
竟然是 false,並未被更改到。也就是說,即使未傳遞 paging,它也並不是 null!
實測之後發現:若在呼叫該 API 的時候,給定一個完全無關的參數,例如 ?a=1
,則 paging 還是會被建立一個實體出來,並無視 = null
這個預設值。因此就導致了非預期(=跟我想的不一樣啊!)的行為。
這邊直接先講結論:如果有傳遞 QueryString 的任何參數時,不管這些參數跟指定的類別有沒有關係,放在 [FromUri]
的複雜型別都會先建立出實體,再嘗試和 QueryString 的內容進行比對與設值。
設定在 [FromUri]
的複雜型別身上的預設值,像是 [FromUri] PagingParameter paging = null
只有完全沒給任何 QueryString 的時候才會吃到。(不過因為預設值只能是常數的關係,基本上就是指 defualt 的 null)
因此如果遇到要給定預設值的場合,還是得乖乖地針對型別中的成員做設定比較保險,例如 bool isDesc { get; set; } = true
。另外,因為完全沒給 QueryString 的時候還是會是 null,故該有的參數檢查仍然不能漏了。
……
閱讀全文