You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 19:20:55