ASP.NET Core MVC中单选按钮选项无法渲染问题排查
ASP.NET Core MVC单选按钮无法从配置渲染问题
我正在开发一个带有单选按钮的ASP.NET Core MVC应用,该应用从appsettings.json文件中获取DocSource选项。运行应用时,控制器已能正确读取配置中的对应值,但这些值似乎无法正确传递到视图,导致单选按钮选项未正常渲染。以下是相关代码信息:
appsettings.json配置
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "DocumentSources": [ { "DocSource": "Supplier", "DocumentTypes": [ "Ocean Bill of Lading", "Container Packing List", "Suppliers Invoice", "Required USDA Information", "Spur Note", "Arrival Notice", "Freight Invoice", "Other" ] }, { "DocSource": "Customer", "DocumentTypes": [ "Purchase Order", "Correspondence", "Remittance Notice" ] }, { "DocSource": "Vendor", "DocumentTypes": [ "Action Invoice", "Rhody Invoice", "Vendor Invoice", "Product / Service Quote", "Order Confirmation" ] }, { "DocSource": "Other", "DocumentTypes": [ "See Description" ] } ] }
模型类代码
namespace RLPDocumentUpload.Models { public class Document { public int Id { get; set; } public DateTime DocumentDate { get; set; } public string DocumentDesc { get; set; } = string.Empty; public string DocumentSource { get; set; } = string.Empty; public string DocumentType { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; } public class DocumentSettings { public List<string> DocumentSources { get; set; } } }
视图代码
@{ var documentSources = ViewBag.DocumentSources as List<DocumentSource>; } <!DOCTYPE html> <html> <head> <title>Upload Document</title> <link rel="stylesheet" type="text/css" href="/css/site.css" /> <script src="/js/site.js"></script> </head> <body> <div class="form-group" style="padding-top: 10px;"> <table width="100%"> <tbody> <tr> <td style="width: 31.489%;"> <img src="https://rlpritchard.com/wp-content/uploads/2020/03/ship.jpg" alt="RLP Logo" width="228" height="141" /> </td> <td style="width: 65.511%;"> <h1 style="text-align: center;"><strong>RL Pritchard & Co., Inc.</strong></h1> </td> </tr> <tr> <td> </td> <td style="color: #007bff; text-align: center;"><h2>Document Upload</h2></td> </tr> </tbody> </table> </div> <form action="/Document/Upload" method="post" enctype="multipart/form-data"> <div class="form-group"> <label for="DocumentDesc" style="font-weight: bold; color: #3264a8;">Document Description</label> <input type="text" id="DocumentDesc" name="DocumentDesc" class="form-control" /> <span class="text-danger"></span> </div> <div class="form-group" style="padding-top: 10px;"> <label for="DocSource" style="font-weight: bold; color: #3264a8;">Document Source</label> <div id="documentSourceGroup" style="padding-left: 15px;"> <!-- Radio buttons will be populated here by JavaScript --> </div> <span class="text-danger"></span> </div> <div class="form-group" style="padding-top: 10px;"> <label for="DocumentType" style="font-weight: bold; color: #3264a8;">Document Type</label> <select id="documentTypeDropdown" name="DocumentType" class="form-control"> <!-- Options will be populated here by JavaScript --> </select> <span class="text-danger"></span> </div> <div class="form-group" style="padding-top: 10px;"> <label for="DocumentDate" style="font-weight: bold; color: #3264a8;">Document Date</label> <input type="date" id="DocumentDate" name="DocumentDate" class="form-control" /> <span class="text-danger"></span> </div> <div class="form-group" style="padding-top: 10px;"> <label for="upload" style="font-weight: bold; color: #3264a8;">Upload File</label> <input type="file" name="upload" class="form-control" /> </div> <button type="submit" class="btn btn-primary" style="margin-top: 15px;">Upload</button> </form> @if (ViewBag.SuccessMessage != null) { <script> alert('@ViewBag.SuccessMessage'); window.location.href = '/Document/Upload'; </script> } <script> document.addEventListener('DOMContentLoaded', function () { const documentSources = @Html.Raw(Json.Serialize(ViewBag.DocumentSources)); const documentSourceGroup = document.getElementById('documentSourceGroup'); const documentTypeDropdown = document.getElementById('documentTypeDropdown'); function populateDocumentSources() { documentSources.forEach(source => { const radio = document.createElement('input'); radio.type = 'radio'; radio.name = 'DocSource'; radio.value = source.DocSource; radio.id = source.DocSource; if (source.DocSource === 'Supplier') { radio.checked = true; } const label = document.createElement('label'); label.htmlFor = source.DocSource; label.textContent = source.DocSource; label.style.color = '#3264a8'; const div = document.createElement('div'); div.appendChild(radio); div.appendChild(label); documentSourceGroup.appendChild(div); }); } function populateDocumentTypes(selectedSource) { documentTypeDropdown.innerHTML = ''; const source = documentSources.find(s => s.DocSource === selectedSource); source.DocumentTypes.forEach(type => { const option = document.createElement('option'); option.value = type; option.textContent = type; documentTypeDropdown.appendChild(option); }); } documentSourceGroup.addEventListener('change', function (event) { if (event.target.name === 'DocSource') { populateDocumentTypes(event.target.value); } }); populateDocumentSources(); populateDocumentTypes('Supplier'); }); </script> </body> </html>
控制器代码
using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using RLPDocumentUpload.Models; using System.Collections.Generic; public class DocumentController : Controller { private readonly IConfiguration _configuration; public DocumentController(IConfiguration configuration) { _configuration = configuration; } public IActionResult Upload() { var documentSources = _configuration.GetSection("DocumentSources").Get<List<DocumentSource>>(); ViewBag.DocumentSources = documentSources; return View(); } [HttpPost] public IActionResult Upload(DocumentUploadModel model) { if (ModelState.IsValid) { // Handle the file upload ViewBag.SuccessMessage = "Document uploaded successfully!"; } return View(); } [HttpGet] public IActionResult GetDocumentSources() { var documentSources = _configuration.GetSection("DocumentSources").Get<List<DocumentSource>>(); return Json(documentSources); } } public class DocumentSource { public string DocSource { get; set; } = string.Empty; public List<string> DocumentTypes { get; set; } = new List<string>(); } public class DocumentUploadModel { public string DocumentDesc { get; set; } public string DocSource { get; set; } public string DocumentType { get; set; } public DateTime DocumentDate { get; set; } public IFormFile? Upload { get; set; } // Made nullable }
Startup.cs代码
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using RLPDocumentUpload.Models; public class Startup { private readonly IConfiguration Configuration; public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.Configure<DocumentSettings>(Configuration.GetSection("DocumentSources")); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Document}/{action=Upload}/{id?}"); }); } }
Program.cs代码
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); // Access configuration settings var documentSources = builder.Configuration.GetSection("DocumentSources").Get<List<DocumentSource>>(); // Register the configuration settings as a singleton service builder.Services.AddSingleton(documentSources); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Document}/{action=Upload}/{id?}"); app.Run();
渲染效果

问题原因及修复方案
1. 命名空间不匹配导致类型转换失败
控制器中定义的DocumentSource和DocumentUploadModel未归属到RLPDocumentUpload.Models命名空间,视图默认引用该命名空间,导致ViewBag.DocumentSources转换为List<DocumentSource>时返回null,JavaScript无法获取数据生成单选按钮。
修复:将这两个类移到RLPDocumentUpload.Models命名空间下:
namespace RLPDocumentUpload.Models { public class DocumentSource { public string DocSource { get; set; } = string.Empty; public List<string> DocumentTypes { get; set; } = new List<string>(); } public class DocumentUploadModel { public string DocumentDesc { get; set; } public string DocSource { get; set; } public string DocumentType { get; set; } public DateTime DocumentDate { get; set; } public IFormFile? Upload { get; set; } } }
2. 修正视图中的类型转换
更新视图顶部的类型转换代码,指定完整命名空间:
@{ var documentSources = ViewBag.DocumentSources as List<RLPDocumentUpload.Models.DocumentSource>; }
3. 修复Post请求后的数据回显
当前Post方法返回视图时未重新设置ViewBag.DocumentSources,提交后单选按钮会消失。修改Post方法:
[HttpPost] public IActionResult Upload(DocumentUploadModel model) { if (ModelState.IsValid) { // 处理文件上传逻辑 ViewBag.SuccessMessage = "文档上传成功!"; } // 重新加载配置数据到ViewBag var documentSources = _configuration.GetSection("DocumentSources").Get<List<DocumentSource>>(); ViewBag.DocumentSources = documentSources; return View(model); }
4. 优化配置读取(可选)
既然已在Program.cs中注册List<DocumentSource>为单例,可直接注入控制器,避免重复读取配置:
private readonly List<DocumentSource> _documentSources; public DocumentController(List<DocumentSource> documentSources) { _documentSources = documentSources; } public IActionResult Upload() { ViewBag.DocumentSources = _documentSources; return View(); }
5. 验证数据传递
在浏览器开发者工具中查看页面源码,确认@Html.Raw(Json.Serialize(ViewBag.DocumentSources))输出了正确的JSON数组,确保JavaScript能正常读取数据。
内容的提问来源于stack exchange,提问作者Matt Paisley
相关产品推荐
相关产品推荐

