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

如何将foreach生成的单选框值传入ASP.NET Core MVC控制器?

问题解决:ASP.NET Core MVC 收集动态单选框答案

问题背景

我有一个存储在数据库中的试题列表测试,试题数量未知,要求用户必须回答所有问题才能提交(类似Google Forms),需使用单选按钮选择答案。目前已通过视图生成动态单选框组,但不清楚如何在控制器的Vote动作中获取所有单选框的值,尝试的视图模型绑定始终为空。

当前视图代码:

@{
    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="radio-group-@question.Id" id="@answer.Id" value="option-@answer.Id">
                    <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]
    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(/* 需要填写参数 */)
    {
        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

解决方案

方案一:直接通过Request.Form获取所有提交值

不需要修改现有视图,直接在控制器中读取表单数据:

[Authorize]
[HttpPost]
public async Task<IActionResult> Vote()
{
    // 遍历所有表单键,筛选出单选框组的提交值
    var userAnswers = new Dictionary<int, int>();
    foreach (var key in Request.Form.Keys)
    {
        if (key.StartsWith("radio-group-"))
        {
            // 提取问题ID(去掉前缀,处理负号)
            if (int.TryParse(key.Replace("radio-group-", ""), out int questionId))
            {
                // 提取答案ID(去掉value前缀)
                var value = Request.Form[key];
                if (int.TryParse(value.Replace("option-", ""), out int answerId))
                {
                    userAnswers.Add(questionId, answerId);
                }
            }
        }
    }

    // 验证是否所有问题都已回答
    var allQuestions = await _dataManager.QuestionsRepository.GetEntitiesAsync();
    if (userAnswers.Count != allQuestions.Count())
    {
        ModelState.AddModelError("", "请回答所有问题");
        ViewBag.Questions = allQuestions;
        ViewBag.Answers = await _dataManager.AnswersRepository.GetEntitiesAsync();
        return View("Index");
    }

    // 这里可以处理用户答案,比如存入数据库
    // ...

    return RedirectToAction("Result"); // 跳转结果页
}

方案二:修改视图和模型实现强类型绑定

如果希望使用强类型模型绑定,需要调整视图中单选框的name属性,使其匹配模型结构:

1. 创建合适的视图模型

namespace StudyExamExample.Models;

// 单个问题的答案提交模型
public class QuestionAnswerViewModel
{
    public int QuestionId { get; set; }
    public int SelectedAnswerId { get; set; }
}

// 整个测试的提交模型
public class TestSubmissionViewModel
{
    public List<QuestionAnswerViewModel> Answers { get; set; } = new();
}

2. 修改视图生成正确的表单name属性

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

<form class="m-5" asp-area="" asp-controller="Test" asp-action="Vote" method="post">
    @for (int i = 0; i < questions.Count(); i++)
    {
        var question = questions.ElementAt(i);
        <div class="mb-3">
            <h5>@question.Content</h5>
            <!-- 隐藏字段存储问题ID -->
            <input type="hidden" name="Answers[@i].QuestionId" value="@question.Id" />
            @foreach (var answer in question.Answers)
            {
                <div class="form-check">
                    <input class="form-check-input" type="radio" 
                           name="Answers[@i].SelectedAnswerId" 
                           id="answer_@question.Id_@answer.Id" 
                           value="@answer.Id">
                    <label class="form-check-label" for="answer_@question.Id_@answer.Id">
                        @answer.Content
                    </label>
                </div>
            }
        </div>
    }
    <button class="btn btn-primary w-100 py-2" type="submit">Отправить</button>
</form>

3. 控制器中接收模型并验证

[Authorize]
[HttpPost]
public async Task<IActionResult> Vote(TestSubmissionViewModel submission)
{
    if (!ModelState.IsValid)
    {
        ViewBag.Questions = await _dataManager.QuestionsRepository.GetEntitiesAsync();
        ViewBag.Answers = await _dataManager.AnswersRepository.GetEntitiesAsync();
        return View("Index");
    }

    // 验证是否所有问题都有答案
    var allQuestions = await _dataManager.QuestionsRepository.GetEntitiesAsync();
    if (submission.Answers.Count != allQuestions.Count())
    {
        ModelState.AddModelError("", "请回答所有问题");
        ViewBag.Questions = allQuestions;
        ViewBag.Answers = await _dataManager.AnswersRepository.GetEntitiesAsync();
        return View("Index");
    }

    // 处理用户答案逻辑
    // ...

    return RedirectToAction("Result");
}

额外:前端验证必填项

为了提升用户体验,可以添加前端JS验证,确保所有单选框组都已选择:

<script>
document.querySelector('form').addEventListener('submit', function(e) {
    const radioGroups = {};
    // 收集所有单选框组
    document.querySelectorAll('input[type="radio"]').forEach(radio => {
        const groupName = radio.name;
        if (!radioGroups[groupName]) {
            radioGroups[groupName] = false;
        }
        if (radio.checked) {
            radioGroups[groupName] = true;
        }
    });

    // 检查是否有未选择的组
    const unselected = Object.values(radioGroups).some(selected => !selected);
    if (unselected) {
        e.preventDefault();
        alert('请回答所有问题');
    }
});
</script>

内容的提问来源于stack exchange,提问作者Aycon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.02 04:02:26