如何把通过foreach生成的输入传递给控制器?

前端开发 2026-07-09

我的测试包含一个存放在数据库中的题目列表。事先并不知道有多少道题,但提交测试时只有当用户为每道题都给出答案时才会被接受(就像Google表单一样)。

根据技术规范,我需要使用单选按钮来选择答案。遗憾的是,我在ASP.NET Core MVC方面经验并不丰富,难以设想模型应该如何组织,才能在点击“Submit”按钮时,在控制器的 Vote 动作中成功检索到所有RadioButton组。

我尝试用这段视图生成表单(单选按钮组):

@{
    string title = "Тест";
    ViewBag.Title = title;
    IEnumerable<Question>? questions = ViewBag.Questions;
    IEnumerable<Answer>? answers = ViewBag.Answers;
}

<form class="m-5" asp-area="" asp-controller="Test" asp-action="Vote" method="post">
    @foreach (var question in questions)
    {
        <div class="mb-3">
            <h5>@question.Content</h5>
            @foreach (var answer in question.Answers)
            {
                <div class="form-check">
                    <input class="form-check-input" type="radio" name="[email protected]" id="@answer.Id" value="[email protected]">
                    <label class="form-check-label" for="@answer.Id">
                        @answer.Content
                    </label>
                </div>
            }
        </div>
    }
    <button class="btn btn-primary w-100 py-2" type="submit">Отправить</button>
</form>

如何在控制器中获取这些值?

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using StudyExamExample.Domain;
using StudyExamExample.Models;

namespace StudyExamExample.Controllers;

public class TestController(DataManager dataManager) : Controller
{
    private readonly DataManager _dataManager = dataManager;

    [Authorize]
    // Roles = "Admin"
    public async Task<IActionResult> Index()
    {
        ViewBag.Questions = await _dataManager.QuestionsRepository.GetEntitiesAsync();
        ViewBag.Answers = await _dataManager.AnswersRepository.GetEntitiesAsync();
        return View();
    }

    [Authorize]
    public async Task<IActionResult> Vote(/*What there?*/)
    {
        return View();
    }
}

我尝试用视图模型集合来实现,但不起作用 :(

namespace StudyExamExample.Models;

public class RadioButtonViewModel
{
    public string GroupId { get; set; } = null!;

    public string SelectedElementId { get; set; } = null!;
}

public class RadioButtonsSetViewModel
{
    public IEnumerable<RadioButtonViewModel> Items { get; set; } = null!;
}

而且现在控制器(参数总是为空):

public class TestController(DataManager dataManager) : Controller
{
    private readonly DataManager _dataManager = dataManager;

    [Authorize]
    // Roles = "Admin"
    public async Task<IActionResult> Index()
    {
        ViewBag.Questions = await _dataManager.QuestionsRepository.GetEntitiesAsync();
        ViewBag.Answers = await _dataManager.AnswersRepository.GetEntitiesAsync();
        return View();
    }

    [Authorize]
    public async Task<IActionResult> Vote(RadioButtonsSetViewModel radioButtons)
    {
        return View();
    }
}

从浏览器发送的数据(忽略双破折号;在数据库中,ID为负数):

radio-group--5=option--15  
radio-group--4=option--12  
radio-group--3=option--8  
radio-group--2=option--6  
radio-group--1=option--3

网站

解决方案

如果你想发布的请求体与 RadioButtonsSetViewModel 匹配,而不需要自定义逻辑来遍历 Request.Form 的值,表单结构应如下所示:

<form class="m-5" asp-area="" asp-controller="Test" asp-action="Vote" method="post">
    @{
        int itemId = 0;

        foreach (var question in questions)
        {
            <div class="mb-3">
                <h5>@question.Content</h5>

                <input type="hidden" name="Items[@itemId].GroupId" id="Items[@itemId].GroupId" value="@question.Id" />

                @foreach (var answer in question.Answers)
                {
                    string answerFieldId = $"question_{question.Id}_{answer.Id}";
                    <div class="form-check">
                        <input class="form-check-input" type="radio" name="Items[@itemId].SelectedElementId" id="@answerFieldId" value="@answer.Id" />
                        <label class="form-check-label" for="@answerFieldId">
                            @answer.Content
                        </label>
                    </div>
                }
            </div>

            itemId++;
        }
        <button class="btn btn-primary w-100 py-2" type="submit">Отправить</button>
    }
</form>

变更如下:

  1. 通过对 itemId 的迭代,在 Items 数组中构建 RadioButtonViewModel 对象。
  2. 增加一个隐藏输入,将 Items[@itemId].GroupId 的值设为 question.Id
  3. 通过问题和答案的ID组合,使答案的 id 唯一:question_{question.Id}_{answer.Id}
  4. Items[@itemId].SelectedElementId 赋值为 answer.Id

因此,当提交表单时,请求体将与以下结构匹配:

Items[0].GroupId=5
Items[0].SelectedElementId=15
Items[1].GroupId=4
Items[1].SelectedElementId=12
Items[2].GroupId=3
Items[2].SelectedElementId=8
Items[3].GroupId=2
Items[3].SelectedElementId=6
Items[4].GroupId=1
Items[4].SelectedElementId=3

对于你的控制器动作:

public class TestController(DataManager dataManager) : Controller
{
    ...

    [Authorize]
    [HttpPost]
    public async Task<IActionResult> Vote([FromForm] RadioButtonsSetViewModel model)
    {
        // Implementation for the Vote Action
    }
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章