ASP.NET Web API 2:安全的PATCH方法的三種實作
之前「ASP.NET WEB API的HTTP PATCH動詞與PATCH方法實作」被網友反應有問題,經驗證後證實,ASP.NET MVC的Bind屬性對於ASP.NET Web API無效。真的是犯了程式碼會動的大錯,非常抱歉,以下重新整理三種Patch方法的實作方向。
Model - TodoItem
以下是範例用的Model:
public class TodoItem { public int Id { get; set; } public string Name { get; set; } public bool IsDone { get; set; } }
請先利用基架(Web API 2 - Entity Framework)產生TodoItemsConteoller。建置之後,先利用POSTMAN / Fiddler等工具新增幾筆資料。
Patch方法的安全性問題
PATCH動詞在HTTP規範中代表著「部分更新」,而Patch方法即是要拿來實作對應PATCH動詞的方法。但預設的Model Binding機制是將所有傳入的資料盡量(盡其所能)進行Binding動作。
public IHttpActionResult Patch(int id, TodoItem todoItem) {}
這在我們進行部分更新的可能會有一些安全性問題,例如,可疑用戶端傳入{"Id":1,"Name":"Is Patch?","IsDone":false}
,Name與IsDone都會被修改。所以我們要保護好資料,不應該被更新的部分就要剔除。
Patch方法 - 指定更新屬性
第一種方法就是之前那一篇的方法,透過指定更新屬性名稱來進行部分更新的動作:
TodoItem item = db.TodoItems.Find(id); // 一一指定 item.IsDone = todoItem.IsDone; db.Entry(item).State = EntityState.Modified; try { db.SaveChanges(); } // 省略
我們的範例比較簡單,工作項目只許修改是否完成(IsDone)的狀態。此種方式在有大量屬性(欄位)時比較麻煩。記得,如果你不是一一指定的方式,那麼此PATCH方法等於是PUT方法。
Patch方法 - DTO
第二種方式可以使用DTO技巧:
public class TodoItemDTO { public int Id { get; set; } public bool IsDone { get; set; } }
和TodoItem類別一樣,去建立一個Patch方法使用TodoItemDTO類別,然後在Patch方法中改用DTO去進行Binding的動作,那麼不必要的資料就會自然被剔除。
public IHttpActionResult Patch(int id, TodoItemDTO todoItem) {}
讓ASP.NET Web API去Binding TodoItemDTO物件,而不是原始TodoItem物件。也就是說,只保留我們想要的屬性。
Patch方法 - Entity Framework IsModified屬性
如果不想在專案內產生大量的DTO,那麼還可以考慮使用Entity Frameowrk的IsModified屬性。
var excluded = new[] { "Name" }; var entry = db.Entry(todoItem); entry.State = EntityState.Modified; foreach (var name in excluded) { entry.Property(name).IsModified = false; } //TodoItem item = db.TodoItems.Find(id); //item.IsDone = todoItem.IsDone; //db.Entry(item).State = EntityState.Modified; try { db.SaveChanges(); } // 省略
以上利用entry.Property(name).IsModified屬性將我們要排除的name屬性名稱的Modified狀態取消,也就是說,不管Name屬性名稱是否有傳入值,在Entity Framework看來都是未異動,在SaveChanges()被呼叫時不會進行已排除Name屬性的更新。
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。