Web API 2 筆記 - HttpGet 資料繫結與驗證
2 |
前篇文章已介紹過 ASP.NET Web API 2 資料驗證做法,這篇會談談我在處理 TaskLogController 時的衍生需求 - 支援 GET 方式呼叫 Web API,用一行 URL 傳參數完成寫入 Log 動作。
首先聲明,使用 GET 方式更新有違一般資安原則(延伸閱讀:隱含殺機的GET式AJAX資料更新),但考量這個 Web API 會鎖定呼叫來源 IP 限定特定伺服器呼叫,而改為單一 URL 完成動作可降低呼叫端的程式複雜度(有些古老排程可用程式庫很貧乏,且不一定能引用 curl 等第三方軟體),故我還是花了點時間研究納入選項。
直覺做法是將 TaskLogEntity 屬性拆成輸入參數,方法內部以輸入值建構 TaskLogEntity 資料物件,再呼叫 Validate<T> 觸發驗證,剩下的程序與前篇文章範例相同,若 ModelState.IsValid == false 則用 Request.CreateErrorResponse() 傳回錯誤訊息。
/// <summary>
/// 新增作業執行記錄
/// </summary>
/// <param name="taskName">作業名稱</param>
/// <param name="action">動作</param>
/// <param name="eventType">事件別</param>
/// <param name="message">訊息</param>
/// <returns></returns>
[HttpGet]
public HttpResponseMessage Add(string taskName, string action, string eventType, string message = null)
{
var entity = new TaskLogEntity()
{
LogTime = DateTime.Now,
TaskName = taskName,
Action = action,
Message = message
};
//呼叫Validate<T>觸發驗證
Validate<TaskLogEntity>(entity);
//補上列舉轉換
EventTypes et;
if (Enum.TryParse<EventTypes>(eventType, out et))
{
entity.EventType = et;
}
else
{
ModelState.AddModelError("EventType", $"無效的 EventType 列舉值 - {eventType}");
}
//若資料驗證未過,傳回錯誤訊息
if (!ModelState.IsValid)
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
entity.LogTime = DateTime.Now;
//TODO: 將 Entity 寫入資料庫
//TaskLogDataHelper.Insert(entity);
//成功時傳回OK
return Request.CreateResponse(HttpStatusCode.OK);
}
列舉處理部分要花點工夫。POST JSON 時 EventTypes 轉換是由 Json.NET 處理,改由 URL 參數由字串轉列舉有兩種做法,第一種 eventType 參數直接宣告為 EventTypes 型別並自訂 ModelBinder 轉換(參考:Enumeration Model Binder For Asp.Net Mv),缺點是當轉換失敗即抛出例外不執行方法,因此錯誤訊息不會透過 ModelState 反映,回傳行為不一致。因此我採用第二種做法,接入字串型別 eventType 參數再自行轉換,轉換失敗時用 ModelState.AddModelError() 加入錯誤訊息。
執行結果如下:
與前篇文章的測試結果比較,錯誤回傳訊息的屬性名稱為 "taskName"、"eventType",而非 "entity.TaskName", "entity.EventType",若要求一致有兩處要稍加修改:Validate<TaskLogEntity>(entity, "entity");
,ModelState.AddModelError("entity.EventType", $"無效的 EventType 列舉值 - {eventType}");
。
除了將屬性轉為輸入參數,我後來又找到更簡便的做法 - 使用 [FromUri] Attribute 搭配輸入參數型別 TaskLogEntity,交由 Web API 2 將 URL 參數映對到 TaskLogEntity 屬性,如此寫法跟 POST Json 相同:
/// <summary>
/// 新增作業執行記錄
/// </summary>
/// <param name="entity"></param>
[HttpGet]
public HttpResponseMessage UriAdd([FromUri]TaskLogEntity entity)
{
//若資料驗證未過,傳回錯誤訊息
if (!ModelState.IsValid)
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
entity.LogTime = DateTime.Now;
//TODO: 將 Entity 寫入資料庫
//TaskLogDataHelper.Insert(entity);
//成功時傳回OK
return Request.CreateResponse(HttpStatusCode.OK);
}
Swagger UI 會解析 [FromUri] 依各屬性必填與否及最大長度加入客戶端檢查,網頁介面在輸入無效內容時會如下圖欄位變紅無法送出。
為測試效果改用 curl 測試,如下圖,輸入的無效值被成功阻攔。其中列舉型別 eventType,ASP.NET Web API 2 會自動將文字轉換成列舉,轉換失敗訊息跟 Json.NET 有所不同,但一樣會合併於 ModelState 輸出。
二者相比,[FromUri] 方法簡便許多,確定勝出。只有一個小問題,若 [HttpPost] Add([FromBody]TaskLogEntity entity)、[HttpGet] Add([FromUri] entity) 並存會有 Overloading 重複問題,需修改方法名稱避開,在本例是將 [HttpGet] 改名為 UriAdd() 以與 [HttpPost] Add() 區隔。
Tips of how to implement data validation when pass entity parameter via HttpGet.
Comments
# by supershowwei
單就同時支援 HttpGet、HttpPost 複雜型別參數這點,ASP.NET MVC 比 ASP.NET Web API 友善許多。
# by nick
等待複核中,留言將在稍後顯示 / The comment is awaiting review.