ASP.NET Web API 2:安全的PATCH方法的三種實作

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屬性的更新。

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。