今天來聊聊前後端分離的細節。雖然分離後商業邏輯的核心可以完全集中在後端,前端只需要專心負責呈現,再加上透過 SPA 的方式,也能更容易更新畫面並達到動態渲染的效果。
但實際上還是有許多細節需要注意,以下就來分享一些成員們曾經踩過的雷與必須留意的地方。
需特別注意前後端 Dto 的欄位名稱必須保持一致(包含大小寫),否則前端很容易出現欄位無法對應而導致資料為空的情況。
//C# 原生 Dto
public class OptionDto
{
public string? Text { get; set; }
public string? Value { get; set; }
public OptionProps? Props { get; set; }
}
前端收到的原生 JSON 會長這樣
{
"text": "...",
"value": "...",
"props": {
"disabled": false
}
}
解決方式 1:全域設定命名策略
如果想要關閉預設轉換,或改用其他命名策略,可以在 Program.cs
/Startup.cs
中進行設定:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddControllers()
.AddJsonOptions(opts =>
{
// 關閉自動 camelCase(改回跟 C# 一樣的 PascalCase)
opts.JsonSerializerOptions.PropertyNamingPolicy = null;
});
var app = builder.Build();
app.MapControllers();
app.Run();
解決方式 2:在屬性上標註 JsonPropertyName
如果不想調整全域命名策略,也可以在 Dto 物件的屬性上直接加入 [JsonPropertyName("SomeName")]
,序列化時就會以指定的名稱輸出。
/C# 原生 Dto
public class OptionDto
{
[JsonPropertyName("Text")]
public string? Text { get; set; }
[JsonPropertyName("Value")]
public string? Value { get; set; }
[JsonPropertyName("Props ")]
public OptionProps? Props { get; set; }
}
另一個常見的坑是欄位型別或必填限制不一致,在 Controller 類別上加上 [ApiController]
屬性時,框架會自動處理以下事項,並在失敗時回傳 400 Bad Request:
using Microsoft.AspNetCore.Mvc;
using Core.Models.Dto;
[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
[HttpPost]
public IActionResult Post([FromBody] TestDto req)
{
// 如果 HostName 型別不符(如傳數字),
// 框架在這裡之前就已回 400 了。
return Ok(new { message = "Success" });
}
}
public class TestDto
{
[JsonPropertyName("HostName")]
public string HostName { get; set; }
}
假設前端不小心傳了錯誤型別(int 而不是 string):
{ "HostName": 123 }
這種情況在初期開發很常見,尤其是欄位型別與必填限制不一致時,容易卡關。
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "...",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"req.HostName": [
"The JSON value could not be converted to System.String. Path: $.HostName ..."
]
}
}
前後端分離能帶來清晰的職責劃分與靈活的畫面更新,但細節處理不能掉以輕心。
命名策略:保持前後端 Dto 欄位一致,避免大小寫或格式差異。
型別驗證:善用 [ApiController] 與 DataAnnotation,自動攔截錯誤。
掌握這些原則,就能減少踩雷次數,讓開發更順暢,整理以下常見的錯誤與對應結果
錯誤情境 | 範例請求 / 狀況 | 後端處理結果 |
---|---|---|
欄位名稱不一致 | 前端傳 { "text": "AAA" } 後端期待 Text |
前端無法正確綁定 → 資料為空或 null |
大小寫不符 | 前端傳 { "Hostname": "ABC" } 後端期待 HostName |
無法對應 → 值為 null |
型別錯誤 | 前端傳 { "HostName": 123 } 後端期待 string |
ApiController 自動攔截 → 回 400 Bad Request |
必填欄位缺漏 | 前端傳 { } ,缺少 HostName |
DataAnnotation 驗證失敗 → 回 400 Bad Request |
傳遞額外欄位(後端無此欄位) | 前端傳 { "HostName": "ABC", "Ip": "127.0.0.1" } |
額外欄位會被忽略,僅綁定已定義的屬性 |
全部正確 | 前端傳 { "HostName": "ABC" } |
成功綁定 → 進入 Controller 邏輯 |