.Net Core WebAPI 傳值到後端Controller的方法

網頁功能最常用的就是資料間的傳遞,不管事表單資料傳送到後台,或者是輸入查詢資料後,由後端DB回傳資料。這之間都少不了傳輸資料到後台。
在以前MVC的時候,可以透過Razor語法的HTML Helper的BeginForm,在Submit之後,會透過ModelBinding把資料傳到後端Controller。
或是在View直接把整個Form序列化(serilizeArray)後再傳給後端。
現再寫前後分離(Angular + .Net 6 WebAPI)採用WebAPI來開發,剛完成第一個前後分離專案,順便把資料傳送到Controller的方式記錄下來。


常見的POST表單資料到後端

先來看看類似以前MVC最常用到的POST表單資料到後端Controller,在WebAPI裡要如何處理:

HTML
<form method="post" #frm="ngForm" [formGroup]="testForm" (ngSubmit)="onSubmit()">
  <div class="form-row">
    <label><span class="label-font">姓名:</span></label><input formControlName="name" type="text">
  </div>
  <div class="form-row">
    <label><span class="label-font">年齡:</span></label><input formControlName="age" type="text">
  </div>
  <input type="submit" value="SUBMIT">
</form>

 

Andular元件程式碼

HTML裡面的input都要給他一個formControlName跟TS的FormGroup做對應,不然到時候Angular建置時會報

export class TestComponent implements OnInit {

  public testForm: FormGroup = new FormGroup({
    name: new FormControl(),
    age: new FormControl()
  })

  constructor(
    private _httpClient: HttpClient
  ) { }

  ngOnInit(): void {
  }

  onSubmit(frm: any) {
    const url = "/Test/Method_1";
    let params = {
      name: this.testForm.controls["name"].value.toString(),
      age: this.testForm.controls["age"].value.toString(),
      ids: [1,2,3,4,5]
    };
    
    /*也可以這樣寫*/
    //let params = this.testForm.value;
    /*也可以這樣寫 ref:https://www.simplilearn.com/tutorials/angular-tutorial/angular-service*/
    //let params = frm.value;
    
    let options =
    {
      //headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
      withCredentials: true
    };

    this._httpClient.post(url, params, options).subscribe();
  }
}

參數型別為Object
        [HttpPost]
        public async Task<IActionResult> Method_1([FromBody] Object_Test data)
        {            
            try
            {
                return Ok(new { name = $"processed_{data.Name}", age = $"processed_{data.Age}", ids = data.Ids });
            }
            catch (Exception Ex)
            {
                return BadRequest(new {errMsg = Ex.Message,  name = $"processed_{data.Name}", age = $"processed_{data.Age}" });
            }
        }
  • params可以直接寫成let params = this.testForm.value;,這樣就不用一個一個塞值到object的property裡了。
  • 如果post的參數給的是object物件,header的Content-Type再Angular會預設為'application/json',會自動把表單內容序列化後post給後端,Action的參數要加上[FromBody]的標籤,才能成功接到參數。
參數使用object
參數使用object,Content-Type為application/json

Controller的參數部分要加上[FromBody]的標籤,並且參數的型別要用一個Model裝起來。無法像在MVC時直接宣告一個與input name一樣的參數(ex: string name),他就會自己做model binding。[FromBody]跟[FromForm]的差別可以參考,個人是習慣用[FromBody]接整理好的Json隔是參數。


參數型別為FormData
        [HttpPost]
        public async Task<IActionResult> Method_1([FromBody] Object_Test data)
        {            
            try
            {
                return Ok(new { name = $"processed_{data.Name}", age = $"processed_{data.Age}", ids = data.Ids });
            }
            catch (Exception Ex)
            {
                return BadRequest(new {errMsg = Ex.Message,  name = $"processed_{data.Name}", age = $"processed_{data.Age}" });
            }
        }

如果參數傳的是FormData的話,header的content-type會由Angular預設的application/json變更為'multipart/form-data'。雖然參考2.的內容是說 Form Post預設使用的content-type為application/x-www-form-urlencoded,但我測試後參數如果為FormData,Angular好像預設會使用multipart/form-data。如果傳送的是multipart/form-data但Action參數加的是[FromBody]是會報錯的

參數使用FormData
參數使用FormData,但Action的參數加上[FromBody]標籤會報錯誤訊息
20250506補充:
看到了自己上面寫的[如果傳送的是multipart/form-data但Action參數加的是[FromBody]是會報錯的]這段話。

讓20250506的我回來替當初寫這篇文章時的我來解答這個問題!
當content-type為multipart/form-data時。後端Controller Action要使用的參數來源屬性應該要用[FromForm]。[FromBody]是用來解析JSON用的。multipart/form-data的格式不為JSON,用[FromBody]會無法進行model binding,要改用[FromForm]。以上來源可以參考這篇文章:呼叫API時,content-type的設定

把[FromBody]改成[FromForm],讓.Net知道header body的資料格式為application/form-data。就可以Model Binding了!

        [HttpPost]
        public async Task<IActionResult> Method_1([FromForm] Object_Test data)
        {            
            try
            {
                return Ok(new { name = $"processed_{data.Name}", age = $"processed_{data.Age}", ids = data.Ids });
            }
            catch (Exception Ex)
            {
                return BadRequest(new {errMsg = Ex.Message,  name = $"processed_{data.Name}", age = $"processed_{data.Age}" });
            }
        }

 

Ref: