網頁功能最常用的就是資料間的傳遞,不管事表單資料傳送到後台,或者是輸入查詢資料後,由後端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>
- HTML裡面的input都要給他一個formControlName跟TS的FormGroup做對應,不然到時候Angular建置時會報
TS:
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();
}
}
- params可以直接寫成let params = this.testForm.value;,這樣就不用一個一個塞值到object的property裡了。
- 如果post的參數給的是object物件,header的Content-Type再Angular會預設為'application/json',會自動把表單內容序列化後post給後端,Action的參數要加上[FromBody]的標籤,才能成功接到參數。
- 如果是formData的話,header的Content-Type會由Angular預設的application/json變更為'multipart/form-data',Action的參數要加上[FromBody]的標籤,才能成功接到參數。雖然參考2.的內容是說 Form Post預設是使用application/x-www-form-urlencoded,但我測試後參數如果為FormData,Angular好像預設會使用multipart/form-data。如果傳送的是multipart/form-data但Action參數加的是[FromBody]是會報錯的(下圖3.)。
- d
Controller:
[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}" });
}
}
- Controller的參數部分要加上[FromBody]的標籤,並且參數的型別要用一個Model裝起來。無法像在MVC時直接宣告一個與input name一樣的參數(ex: string name),他就會自己做model binding。[FromBody]跟[FromForm]的差別可以參考,個人是習慣用[FromBody]接整理好的Json隔是參數。
- .
Ref:
1.What the difference between [FromForm] and [FromBody] in Asp.Net Core
2.[筆記] Postman 常見的 Content-type
3.Angular預設Content-type
4.[鐵人賽 Day09] ASP.NET Core 2 系列 - Model Binding
5.ASP.NET Core Model Binding 死活綁不上 - 1