前端 (JS) 序列化 HTTP POST 反序列化 後端 (C#) ┌─────────┐ ┌──────────┐ ┌────────────┐ ┌───────────┐ ┌─────────┐ │ JS 物件 │ ──▶ │ $.toJSON │ ──▶ │ application│ ──▶ │ JsonHelper│ ──▶ │ 強型別 │ │ │ │ │ │ /json │ │ .Deserialize│ │ 物件 │ └─────────┘ └──────────┘ └────────────┘ └───────────┘ └─────────┘
<!-- 必須在 jQuery 之前或之後均可 -->
<script src="/Scripts/jquery.json-2.4.min.js"></script>
或使用現代瀏覽器原生 JSON.stringify(obj) (IE8+),無需額外庫。
function saveExam() {
// 1. 蒐集表單資料組成 JS 物件
var exam = {
ExamId: '<%= ExamId %>',
YearTerm: $('#<%= DDLYearTerm.ClientID %>').val(),
ExamType: $('#<%= DDLExamType.ClientID %>').val(),
ExamDate: $('#ExamDate').val(),
ExamName: $('#ExamName').val()
};
// 2. 序列化為 JSON 字串
var json = JSON.stringify(exam); // 或 $.toJSON(exam)
// 3. 呼叫 ASP.NET AJAX 產生的 Proxy 方法
AjaxController.EditExam(json, onSuccess, onError, null);
}
function onSuccess(result) {
if (result === "Success") {
alert("儲存成功");
parent.$.fancybox.close(); // 關閉彈窗
} else {
alert("失敗:" + result);
}
}
function onError(err) {
alert("AJAX 錯誤:" + err.get_message());
}
[Serializable]
public class ExamDto
{
public string ExamId { get; set; }
public string YearTerm { get; set; }
public string ExamType { get; set; }
public string ExamDate { get; set; }
public string ExamName { get; set; }
}
[WebService(Namespace = "https://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[ScriptService] // 關鍵:啟用 AJAX/JSON 支援
public class AjaxController : WebService
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = false)]
public string EditExam(string value)
{
try
{
// 1. 驗證登入狀態
if (!HttpContext.Current.Request.IsAuthenticated)
return "會話已過期,請重新登入";
// 2. 反序列化 JSON 字串為強型別物件
var exam = JsonHelper.Deserialize(value);
// 3. 業務邏輯
new ExamService().UpdateExam(exam);
return "Success";
}
catch (Exception ex)
{
// 記錄日誌...
return "伺服器錯誤:" + ex.Message;
}
}
}
public static class JsonHelper
{
private static readonly JavaScriptSerializer _serializer = new JavaScriptSerializer();
public static T Deserialize(string json)
{
return _serializer.Deserialize(json);
}
public static string Serialize(object obj)
{
return _serializer.Serialize(obj);
}
}
protected void Page_Load(object sender, EventArgs e)
{
// 註冊型別讓 ASP.NET AJAX 產生 JS Proxy
Ajax.Utility.RegisterTypeForAjax(typeof(AjaxController));
if (!IsPostBack) BindData();
}
這會在頁面輸出 <script src="/AjaxController/jsdebug">,產生 AjaxController.EditExam 等 Proxy 方法。
// Web API Controller (ASP.NET 4.5+ / Core)
[Route("api/exam")]
public class ExamController : ApiController
{
[HttpPut]
public IHttpActionResult Update(ExamDto dto)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
_service.Update(dto);
return Ok("Success");
}
}
// 前端呼叫 (jQuery / fetch)
fetch('/api/exam', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(exam)
}).then(r => r.json()).then(console.log);
優點:RESTful、無 ViewState/ScriptManager 包袱、易測試、前後端完全分離、支援 Swagger/OpenAPI 文件自動生成。
| 現象 | 原因 | 解決 |
|---|---|---|
| 前端收到 "Invalid web service call, missing value for parameter" | JSON 字串參數名稱不匹配 | 確認 JS 物件屬性名稱與 C# 參數名稱一致 (value) |
| ScriptMethod 回傳 XML 而非 JSON | Request Content-Type 非 application/json | 確認前端送出 headers 或加上 [ScriptMethod(ResponseFormat=Json)] |
| DateTime 反序列化失敗 | JSON 日期格式不符 | 使用 ISO 8601 字串 "2024-01-15T14:30:00" 或自訂 JavaScriptSerializer.Converters |