实现IEnumerable后CardList接收Fetch数据为null的问题排查
问题描述
在C#控制器的Create方法中,对通过[FromBody]接收的CardList对象使用foreach循环时出现矛盾情况:
- 未实现
IEnumerable接口时,编译器报错:Type 'WordStudy.Models.CardList' can only be used in 'foreach' statements if it implements 'IEnumerable' or 'IEnumerable<T>', or if it has a suitable 'GetEnumerator' method whose return type has a 'Current' property and a 'MoveNext' method - 实现
IEnumerable<Card>接口后,CardList无法正常接收Fetch API提交的数据,始终为null - 移除
IEnumerable实现后,CardList能正常接收数据,但无法通过foreach遍历其中的卡片
script.js
var currentCardNumber = 1; function addCard() { currentCardNumber++; let newCardHTML = ` <div class="header d-flex justify-content-between p-3"> <h4 class="card-counter">${currentCardNumber}</h4> </div> <div class="card-body row"> <div class="term col"> <h3>术语</h3> <input class="form-control w-100" name = "term" > </div> <div class="definition col"> <h3>定义</h3> <input class="form-control w-100" name="definition" > </div> </div> </div> `; let newCard = document.createElement('div'); newCard.classList.add('card'); newCard.classList.add('mt-3'); newCard.innerHTML = newCardHTML; let addNewCard = document.querySelector('.cards'); let referenceNode = document.getElementById('create-cards'); addNewCard.insertBefore(newCard, referenceNode); } function saveCardDataToServer() { let cards = []; document.querySelectorAll('.cards > .card').forEach((card) => { let termInput = card.querySelector('input[name="term"]'); let definitionInput = card.querySelector('input[name="definition"]'); if (termInput && definitionInput) { let term = termInput.value.trim(); let definition = definitionInput.value.trim(); cards.push({ Term: term, Definition: definition }); } else { console.error("卡片中未找到输入元素:", card); } }); fetch('/Cards/Create/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({Cards: cards}) }) .then(response => response.json()) .then(data => { if (data.success) { console.log("数据保存成功: ", data); window.location.href = "/Cards/Learning"; } else { console.error("保存卡片出错: ", data.message); } }); } document.addEventListener('DOMContentLoaded', function() { let newCard = document.querySelector('.new-card'); newCard.addEventListener('click', addCard); document.querySelector('form.cards').addEventListener('submit', function() { saveCardDataToServer(); }); });
视图代码
@model CardList <!DOCTYPE html> <html lang="zh-CN"> <head> <script src="~/js/script.js"></script> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous" asp-append-version="true"> </head> <body> <main class="main"> <div class="container"> <div class="new-module d-flex justify-content-between mt-5"> <h1>创建新模块</h1> <a class="btn btn-primary" asp-controller="Cards" asp-action="learning" type="submit">创建</a> </div> <div class="card mt-5 p-2 pb-3"> <h3>标题</h3> <input class="form-control w-100" placeholder="输入名称"> </div> <form asp-controller="Cards" asp-action="Create" method="post" class="cards"> <div class="card mt-6" id="card"> <div class="header d-flex justify-content-between p-3"> <h4 class="card-counter">1</h4> </div> <div class="card-body row"> <div class="term col"> <h3>术语</h3> <input class="form-control w-100" name = "term" > </div> <div class="definition col"> <h3>定义</h3> <input class="form-control w-100" name = "definition" > </div> </div> </div> <input type="submit" value="创建" id="create-cards" class="btn btn-primary mt-2"/> </form> <button class="new-card w-100 d-flex justify-content-center align-items-center mt-4 mt-s rounded-2" style="height: 5rem" type="button"> <h3>+ 添加卡片</h3> </button> </div> </main> </body> </html>
模型代码(初始版本)
public class Card { public int CardId { get; set; } [Required] public string Term { get; set; } = string.Empty; public string Definition { get; set; } = string.Empty; } public class CardList { public List<Card> Cards { get; set; } }
实现IEnumerable接口的CardList版本
public class CardList : IEnumerable<Card> { public List<Card> Cards { get; set; } public CardList() { Cards = new List<Card>(); } public IEnumerator<Card> GetEnumerator() { return Cards.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
控制器代码
public class CardsController : Controller { public IActionResult Learning() { var cards = CardsRepository.GetCards(); return View(cards); } public IActionResult Create() { return View(); } [HttpPost] [Route("Cards/Create/")] public IActionResult Create([FromBody] CardList cards) { if (cards != null) { foreach (var card in cards) { if (!string.IsNullOrWhiteSpace(card.Term) && !string.IsNullOrWhiteSpace(card.Definition)) { CardsRepository.AddCard(card); } } return Json(new { success = true, message = "卡片保存成功" }); } var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage); return Json(new { success = false, message = "验证错误", errors = errors }); } }
解决方案
方案一:直接遍历Cards属性(最简单)
无需修改CardList类,直接在控制器中遍历CardList的Cards属性即可,同时增加空值检查避免异常:
[HttpPost] [Route("Cards/Create/")] public IActionResult Create([FromBody] CardList cards) { if (cards != null && cards.Cards != null) { foreach (var card in cards.Cards) { if (!string.IsNullOrWhiteSpace(card.Term) && !string.IsNullOrWhiteSpace(card.Definition)) { CardsRepository.AddCard(card); } } return Json(new { success = true, message = "卡片保存成功" }); } var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage); return Json(new { success = false, message = "验证错误", errors = errors }); }
方案二:保留IEnumerable实现,添加序列化特性
若要让CardList支持直接foreach遍历,给类添加[JsonObject]特性,告知JSON序列化器将其视为普通对象而非集合:
using System.Text.Json.Serialization; [JsonObject] public class CardList : IEnumerable<Card> { public List<Card> Cards { get; set; } public CardList() { Cards = new List<Card>(); } public IEnumerator<Card> GetEnumerator() { return Cards.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
额外优化
前端表单提交时需阻止默认行为,避免页面刷新中断请求:
document.querySelector('form.cards').addEventListener('submit', function(e) { e.preventDefault(); saveCardDataToServer(); });
内容的提问来源于stack exchange,提问作者Egor
相关产品推荐
相关产品推荐

