header的content-type紀錄header body的資料格式,後端API可以依據content-type知道header body內的資料格式,再來解析header body資料,進行Model Binding.
呼叫POST API時,不管是透過下列何種方式
- HTML傳統表單
- JQuery
- Angular的HttpClientModule
- 後端程式直接呼叫(ex: .Net MVC)
遇到POST要傳遞參數時,後端程式會收到的header body資料是什麼格式,是取決於前端如何提交資料(content-type)的。
用戶端提交方式 | MVC 接收到的 Content-Type |
---|---|
一般 HTML 表單 | application/x-www-form-urlencoded |
表單含檔案 (enctype="multipart/form-data" ) | multipart/form-data |
AJAX | application/x-www-form-urlencoded |
用 HTML <form>提交資料(預設content-type:application/x-www-form-urlencoded)
模擬POST 表單資料到後端
<!--如果不透過api測試軟體,form表單大概長這樣-->
<form method="post" action="/Home/Submit">
<input type="text" name="id" />
<input type="text" name="username" />
<input type="submit" value="Submit">
</form>


content-type與後端資料來源的屬性的對應
上面結果我們可以發現,我們收到一個415 Unsupported Media Type的回應,來分析一下出了什麼問題。先來看一下我們後端的API。
public async Task<Result<string>> TestAPI(ApiData data)
{
var result = new Result<string>();
try
{
result.Content = $"{data.id}-{data.name}";
result.Success = true ;
}
catch (Exception ex)
{
result.Message = ex.Message;
}
return result;
}
知道問題的原因後,我們在參數前面加上[FromForm]的資料來源屬性,讓.Net知道header body的資料格式為x-www-form-urlencoded,就能正常取得前端POST到後端的參數資料了。
public async Task<Result<string>> TestAPI([FromForm]ApiData data)
{
var result = new Result<string>();
try
{
result.Content = $"{data.id}-{data.name}";
result.Success = true ;
}
catch (Exception ex)
{
result.Message = ex.Message;
}
return result;
}

表單如果包含上傳檔案
<form method="post" enctype="multipart/form-data">
<input name="Title" />
<input name="File" type="file" />
</form>

Angular的HttpClientModule(預設content-type:依據body內容自行決定)
下面是用ChatGPT查詢出來的資料(Prompt:angular httpclient 預設的content type是什麼):
在 Angular 的 HttpClient
中,預設的 Content-Type
會根據你送出的資料類型而自動決定。以下是常見情況:
傳送的資料類型 | 預設 Content-Type |
---|---|
JavaScript 物件 {} (例如 JSON 資料) | application/json |
FormData (如圖片上傳) | 無設定(瀏覽器自動設定為 multipart/form-data ,含 boundary) |
字串或 URLSearchParams | application/x-www-form-urlencoded |
Blob 、ArrayBuffer 等原始資料 | 無設定(需自行指定) |
Body內容為物件
這邊寫一個呼叫POST API的TypeScript Function。我們body內用是一個{ }物件
TestAPI(): Observable<IResultDto<string>>
{
const url = `${environment.apiBaseUrl}/DesignAuditRise/TestAPI`;
const options = this.generatePostOptions();
const data: any = {
id: 123,
username: 'jojo999'
}
return this.httpClient.post<IResultDto<string>>(url, data, options);
}
後端程式一樣用先前的TestAPI
public async Task<Result<string>> TestAPI(ApiData data)
{
var result = new Result<string>();
try
{
result.Content = $"{data.id}-{data.username}";
result.Success = true ;
}
catch (Exception ex)
{
result.Message = ex.Message;
}
return result;
}
Angular把body資料格式自動判斷為application/json

如先前所說,後端API預設會使用[FromBody]把header body資料格式判斷為JSON來解析。application/json與[FromBody]是可以匹配的,正常Model Binding並取得API回傳值

Body內容為FormData
TestAPI(): Observable<IResultDto<string>>
{
const url = `${environment.apiBaseUrl}/DesignAuditRise/TestAPI`;
const options = this.generatePostOptions();
const data = new FormData();
data.append('id', '123');
data.append('username', 'jojo9988');
return this.httpClient.post<IResultDto<string>>(url, data, options);
}
接著直接呼叫API,Angular把body資料格式自動判斷為application/json。但卻得到了一個415 Error。問題原因跟上面範例一樣,multipart/form-data與預設使用的[FromBody]不能匹配,所以回傳415 Error。


後端API在Model前加上[FromForm],讓.Net知道header body資料格式為multipart/form-data。接著在戳一次API,就能成功Model Binding取得API回傳值了。
public async Task<Result<string>> TestAPI([FromForm]ApiData data)
{
var result = new Result<string>();
try
{
result.Content = $"{data.id}-{data.username}";
result.Success = true ;
}
catch (Exception ex)
{
result.Message = ex.Message;
}
return result;
}

透過上面兩個測試範例,可以知道在Angular透過HttpClient呼叫POST API時,會自己依照body內容的型別來判斷要使用的content-type。
透過前端AJAX呼叫API(預設content-type:application/x-www-form-urlencoded)
後端API 如果是使用.NET(Core) WebAPI專案
後端API程式如下
public async Task<Result<string>> TestAPI(ApiData data)
{
var result = new Result<string>();
try
{
result.Content = $"{data.id}-{data.name}";
result.Success = true ;
}
catch (Exception ex)
{
result.Message = ex.Message;
}
return result;
}
我們這裡寫一個AJAX來呼叫後端的測試API,並且先不指定content-type
$.ajax({
type: "POST",
url: "https://localhost:44371/api/DesignAuditRise/TestAPI",
data: { id: 111, username: "jojoaaa" },
dataType: "json",
headers: {
'Access-Control-Allow-Origin': '*',
},
});


要得到正確的API回應的話,我們可以在後端參數API加上[FromForm]。讓.Net知道header body的資料格式為application/x-www-form-urlencoded,這樣就可以正常取得API回傳資料了。
public async Task<Result<string>> TestAPI([FromForm]ApiData data)//加上[FromForm]讓.Net知道header body的資料格式為application/x-www-form-urlencoded
{
var result = new Result<string>();
try
{
result.Content = $"{data.id}-{data.username}";
result.Success = true ;
}
catch (Exception ex)
{
result.Message = ex.Message;
}
return result;
}


我們也可以修改AJAX參數的格式為JSON,並加上contentType: "application/json"
。讓.Net知道header body的資料格式為application/json,這樣也是可以正常取得API回傳資料。
$.ajax({
type: "POST",
url: "https://localhost:44371/api/DesignAuditRise/TestAPI",
data: JSON.stringify({ id: 111, username: "jojoaaa" }),
contentType: "application/json"
dataType: "json",
headers: {
'Access-Control-Allow-Origin': '*',
},
});
public async Task<Result<string>> TestAPI(ApiData data)//[FromBody]可加可不加,.Net預設就是用FromBody來解析JSON做參數binding.
{
var result = new Result<string>();
try
{
result.Content = $"{data.id}-{data.username}";
result.Success = true ;
}
catch (Exception ex)
{
result.Message = ex.Message;
}
return result;
}


後端API 如果是使用.NET Framework MVC專案
後端API程式如下,這裡我們沒指定預設要用什麼格式來解析header body內容。來測試看看.Net Framework MVC預設會使用何種格式來解析。
[HttpPost]
public ActionResult TestAPI(ApiData data)
{
var res = new PCBRepairNavi.Models.ResultNew<string>()
{
Content = $"{data.id}-{data.username}",
Success = true
};
return Content(JsonConvert.SerializeObject(res), "application/json");
}
我們這裡寫一個AJAX來呼叫後端的測試API,並且先不指定content-type
function TestAPI() {
$.ajax({
type: "post",
url: testApiUrl,
data:{ id: 123 , username: 'jojo123'},
dataType: "json"
}).done(function(data) {
console.log(data);
})
}

也成功取得API的回應

接著修改一下AJAX程式,並指定content-type為application/json
function TestAPI() {
$.ajax({
type: "post",
url: testApiUrl,
data: JSON.stringfy({ id: 123 , username: 'jojo123'}),
contentType: "application/json"
dataType: "json"
}).done(function(data) {
console.log(data);
})
}
request header裡面的content-type為application/json。

同樣的也成功取得API的回應

後端API 如果是使用.NET Framework WebAPI專案
在.NET Framework MVC專案的Controller使用[FromBody]會報錯

在.NET Framework WebAPI專案的Controller使用[FromBody]是不會報錯的

後端程式呼叫外部API
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, myURL);
httpRequestMessage.Content = new FormUrlEncodedContent(form);
HttpResponseMessage tokenResponse = httpClient.SendAsync(httpRequestMessage).Result;
Ref: