ASP.NET MVC在线考试系统源码解析:路由、控制器与计分全流程

发布时间:2026/9/13 0:46:44

ASP.NET MVC在线考试系统源码解析:路由、控制器与计分全流程 简介基于ASP.NET MVC的简易在线考试系统是一份面向计算机相关专业学生、教师及企业开发者的学习与实战资源适合用作课程设计、毕业设计或项目初期立项演示。项目代码经过运行验证功能完整可靠。压缩包共155个文件、约532KB主要包含68个cshtml视图模板、45个cs控制器与业务逻辑、13个js交互脚本以及配置文件、SQL数据库脚本和字体图标等覆盖了MVC分层架构中视图、控制器、模型与配置的典型组成。通过Visual Studio可直接打开解决方案运行便于对照学习用户登录、考试管理、题目管理等核心模块的编码方式。当前已有186人浏览学习具备一定的参考价值。下载后既可系统梳理ASP.NET MVC开发流程也能在此基础上扩展功能满足毕设、课设或实际项目二次开发需求。1. 一个基于 ASP.NET MVC 的简易在线考试系统先拆给你看打开压缩包的第一眼没有一堆 .aspx 页面文件而是 Global.asax、Web.config、UserController.cs、ExamController.cs、ExamViewModel.cs 这些文件把整套考试系统撑了起来。这就是 ASP.NET MVC 与 Web Forms 最直观的差别——请求先进路由再进控制器最后才轮到视图。这个简易系统麻雀虽小但用户登录、试题加载、答案提交、自动计分的完整闭环都在里面适合刚学完 C# 基础、想搞懂 MVC 设计模式真实跑法的开发者也适合做课程设计时快速判断“这套代码能不能改成自己的毕设”。下载之后如果没有 README建议按 Global.asax → Web.config → UserController → ExamController → ExamViewModel 的顺序拆代码正好对应一次请求从入口到绑定的完整链路。2. 路由与配置链路Global.asax、Web.config、packages.config 协同工作的底层逻辑2.1 为什么启动顺序先看 Global.asaxASP.NET MVC 不是靠物理 URL 去找页面而是靠 RouteTable 里注册的路由规则来匹配 controller/action。应用启动时Global.asax 里的 Application_Start 只执行一次负责注册路由、初始化数据库、绑定依赖注入容器等。这个项目里的 Global.asax 虽然只有短短几行但整个系统的入口就在这里。下面这段代码就是最常见的默认路由注册// Global.asax 关键代码 protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteTable.Routes.MapRoute( name: Default, url: {controller}/{action}/{id}, defaults: new { controller User, action Login, id UrlParameter.Optional } ); }这里的{controller}/{action}/{id}是占位符。默认请求/时由于 defaults 指定了 controllerUser、actionLogin所以会直接进入 UserController 的 Login 方法。如果访问/Exam/Index则 id 为空。MapRoute 的 name 参数只是路由名不参与匹配但后续用 Url.RouteUrl 生成链接时会用到。改动 defaults 里的 controller 或 action就能调整整个应用的首屏入口这也是排查 404 时最先该看的地方。顺带说一句如果你是先接触 Spring MVC 再回来看这个项目会发现两者的路由心智模型基本一致Spring 的 RequestMapping 在 ASP.NET MVC 里对应着 RouteTable 加 Action 方法上的路由特性。传统 ASP.NET MVC 4/5 采用集中式路由而 ASP.NET Core 更推荐用 Attribute Routing。简单项目用集中式路由足够了但若你打算以后迁移到 Core可以把 controller 和 action 上的路由特性提前用起来。2.2 Web.config连接字符串、认证与模块注册Web.config 承担运行时的上下文配置。简易在线考试系统的 Web.config 通常至少包含三块connectionStrings 存放数据库连接system.web 下的 compilation 控制调试状态和框架版本authentication 决定未登录用户跳转到哪个页面。下面是一个典型片段connectionStrings add nameOnlineExamDb connectionStringData Source.;Initial CatalogOnlineExam;Integrated SecurityTrue providerNameSystem.Data.SqlClient / /connectionStrings system.web compilation debugtrue targetFramework4.7.2 / authentication modeForms forms loginUrl~/User/Login timeout60 / /authentication /system.web这里 connectionString 里的Data Source.指的是本机 SQL Server如果你用的是 SQL Express要改成.\SQLEXPRESS数据库名 OnlineExam 需要先在 SQL Server 里创建否则页面在访问用户表时会直接抛异常。Forms 认证设置成登录页统一跳转到/User/Logintimeout 60 表示不操作 60 分钟后登录态失效。需要注意debugtrue只在开发阶段使用。它会让编译器生成更多调试符号并且页面异常时会显示详细堆栈。线上环境必须置为 false否则访问者能看到你的源码路径和内部变量名也会拖慢并发响应。Web.config 里还有一个容易忽略的节点是httpRuntime targetFramework4.7.2 /它和编译节点必须匹配实际安装的 .NET Framework 版本版本不一致会导致“无法加载程序集”的错误。2.3 packages.config依赖的版本锁packages.config 记录的是 NuGet 安装的每个包的精确版本。MVC 项目的 packages.config 里至少能看到 Microsoft.AspNet.Mvc 和 Microsoft.AspNet.Web.Optimization。当你在另一台机器上还原时NuGet 会按这个文件里的版本号去下载而不是随便拉最新版否则容易引发编译器兼容问题。下表对比了三个配置文件在不同阶段的作用| 配置文件 | 生命周期阶段 | 主要职责 | 调试时最常改什么 | | Global.asax | 启动时 | 路由注册、全局事件处理 | MapRoute 参数 | | Web.config | 运行时 | 数据库连接、认证、模块 | connectionString、debug | | packages.config | 还原依赖时 | 锁定 NuGet 包版本 | 包版本号 |如果你在项目里找不到数据库文件先别急着翻 Models 目录八成是连接字符串里的 Initial Catalog 和实际库名不一致。简易系统很多直接使用 MDF 文件此时连接写法是AttachDbFilename|DataDirectory|\Exam.mdf这种写法在部署到服务器时容易出现权限问题建议改成标准 SQL Server 连接。还原包时若发现某个包版本不存在打开 packages.config把版本号改成本地 NuGet 缓存里已有的版本再重新编译即可。3. UserController 到用户体系登录注册与 [Authorize] 过滤器的实现细节3.1 一个典型 UserController 的 Action 划分UserController 在在线考试系统里管的不只是考试账号还包括注册、登录、登出和“当前用户是否已登录”的校验逻辑。从文件命名也能看出来这个控制器把用户相关的所有 HTTP 入口集中到了一起而不是拆成 AuthController、AccountController 两个类简单项目这样做反而更好找。下面是一个基于 FormCollection 的登录 Action[HttpPost] [ValidateAntiForgeryToken] public ActionResult Login(FormCollection form) { string userName form[UserName]; string password form[Password]; if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password)) { ModelState.AddModelError(, 用户名和密码不能为空); return View(); } // 简易项目常见做法查内存集合或数据库 // 这里以固定账号示意 if (userName admin password 123456) { FormsAuthentication.SetAuthCookie(userName, false); return RedirectToAction(Index, Exam); } ModelState.AddModelError(, 用户名或密码错误); return View(); }这里的 FormCollection 把表单里所有 name 属性收进来灵活但容易拼错字符串。如果你手头这个资源用的是类似写法改用户名时一定要同步检查视图里的 input name 是否完全一致。正规一点的做法是定义一个 LoginViewModel用强类型绑定但简易系统里为了少写一个类用 FormCollection 也很普遍。SetAuthCookie的作用是往客户端写一个加密票据之后 [Authorize] 过滤器通过读取这个票据来判断用户是否通过验证。它是基于 FormsAuthenticationModule 的受 Web.config 里 authentication modeForms 控制。3.2 [Authorize] 与登录态校验在需要登录才能访问的方法或控制器上只要加一行[Authorize]未登录用户就会被自动重定向到登录页。比如登出方法[Authorize] public ActionResult LogOff() { FormsAuthentication.SignOut(); return RedirectToAction(Login, User); }如果没有 [Authorize]即使登录失败用户也能通过直接输入/User/LogOff来访问这个方法。加了过滤器后未登录请求会被拦截并重定向到 Web.config 里配置的 loginUrl。放在控制器类名上表示类内所有 Action 都生效放在方法上只对这单个 Action 生效。你还可以用[Authorize(Roles admin)]做角色隔离但简易系统通常用不到。使用 FormsAuthentication 时要注意票据默认会被写入 Cookie如果站点部署在 HTTPS 下建议给SetAuthCookie传入createPersistentCookie参数后同时在 Web.config 里配置forms requireSSLtrue /防止票据被明文截获。课程设计不强制要求但面试被问到安全加固时这算是加分项。3.3 注册逻辑密码不能明文存简单考试系统不一定要上 Identity但至少不要明文存密码。常见做法是取 MD5 或 SHA256 哈希后存库。下面这个函数可以直接放在 UserController 里私有方法private string GetMd5Hash(string input) { using (var md5 System.Security.Cryptography.MD5.Create()) { var bytes md5.ComputeHash(Encoding.UTF8.GetBytes(input)); var sb new StringBuilder(); foreach (var b in bytes) sb.Append(b.ToString(x2)); return sb.ToString(); } }调用时GetMd5Hash(password)得到 32 位十六进制字符串。需要注意MD5 已被认为不适合做高强度安全存储但作为课程设计或入门演示性能足够如果是正式上线的考试系统建议换用 BCrypt 或 ASP.NET Identity 自带的 PasswordHasher。注册时还要处理重名问题先查一下数据库里是否已存在相同的 UserName存在就返回ModelState.AddModelError(UserName, 该用户名已被占用)而不是依赖数据库抛唯一约束异常。下表列出了 UserController 里最常用的 Action 设计| Action 名 | HTTP 动词 | 功能 | 返回 | | Login | GET/POST | 登录 | View / RedirectToAction | | Register | GET/POST | 注册新用户 | View / RedirectToAction | | LogOff | POST | 登出 | RedirectToAction |实际项目中LogOff 最好声明为[HttpPost]避免用户被恶意链接强制登出。整个模块保护起来的方法也很简单在 ExamController 类名上方加一行[Authorize]就能让考试模块只对登录用户开放。这也是 MVC 过滤器最直接的应用解释了为什么资源里会有 UserController 又会有 ExamController——一个管身份一个管业务。4. ExamController 与 ExamViewModel考试答题、状态保持和计分的 MVC 玩法4.1 ViewModel 到底解决什么问题在线考试系统的页面要显示的不仅仅是数据库里的一张表而是“题目集合 用户答案 得分”的组合数据结构。这种跨实体、按视图裁剪的数据正好是 ViewModel 的用武之地。ExamViewModel.cs 就是把考试页所需的全部字段打包让 Controller 和 View 之间传递的是一个可测试的强类型对象而不是 ViewBag 里的散装数据。下面是一个典型的定义public class ExamViewModel { public string ExamName { get; set; } // 试卷名 public ListQuestion Questions { get; set; } // 题目列表 public Liststring UserAnswers { get; set; } // 用户提交的答案 public int Score { get; set; } // 总分 public bool IsSubmitted { get; set; } // 是否已交卷 } public class Question { public int Id { get; set; } public string Title { get; set; } // 题干 public string OptionA { get; set; } public string OptionB { get; set; } public string OptionC { get; set; } public string OptionD { get; set; } public string CorrectAnswer { get; set; } // 正确答案 }UserAnswers的索引和题目顺序一一对应UserAnswers[0]表示第一题的答案。这样的设计让前端表单可以非常规整地提交一个数组。你可能注意到Question里没有放 IsChecked 之类的视图状态字段因为用户是否选中应通过 UserAnswers 在 Action 里计算而不该污染题目本身。4.2 用模型绑定接收表单数组当用户点击交卷按钮表单会 POST 到 ExamController 的 Submit 方法。因为表单控件的 name 是UserAnswers[i]ASP.NET MVC 的模型绑定器会自动把它们组装成Liststring。下面的 Action 展示了完整处理[HttpPost] public ActionResult Submit(ExamViewModel submitted) { var vm new ExamViewModel(); vm.ExamName ASP.NET MVC 基础测试; vm.Questions LoadQuestions(); // 重新从库里取题目 vm.UserAnswers submitted.UserAnswers; vm.Score ComputeScore(vm.Questions, vm.UserAnswers); vm.IsSubmitted true; return View(Result, vm); }为什么需要重新加载题目因为表单不会把正确答案暴露给用户必须在服务端根据题目 ID 重新查一次避免用户改 HTML 作弊。如果用户直接伪造 POST 请求只提交自己想要的答案服务端因重新加载题目也能保证正确答案不会被恶意篡改。对应的 Razor 视图片段model ExamViewModel using (Html.BeginForm(Submit, Exam, FormMethod.Post)) { for (int i 0; i Model.Questions.Count; i) { p(i 1). Model.Questions[i].Title/p labelHtml.RadioButton($UserAnswers[{i}], A) Model.Questions[i].OptionA/label labelHtml.RadioButton($UserAnswers[{i}], B) Model.Questions[i].OptionB/label labelHtml.RadioButton($UserAnswers[{i}], C) Model.Questions[i].OptionC/label labelHtml.RadioButton($UserAnswers[{i}], D) Model.Questions[i].OptionD/label } button typesubmit交卷/button }注意Html.RadioButton的第一个参数是 name第二个参数是 value。由于 name 相同同一组单选按钮只能选一个不同的i对应不同组。这里使用了$UserAnswers[{i}]字符串插值编译后等价于普通字符串拼接。计分逻辑可以写成独立的私有方法private int ComputeScore(ListQuestion questions, Liststring userAnswers) { int score 0; for (int i 0; i questions.Count; i) { if (string.Equals(questions[i].CorrectAnswer, userAnswers?[i], StringComparison.OrdinalIgnoreCase)) { score 10; // 每题10分 } } return score; }userAnswers?[i]中的?是空条件运算符防止用户漏答导致索引越界。如果题目数量超过 100建议把每题分值改成从设计题目时传入或根据题目难度设定权重避免所有题平均分配导致区分度不足。4.3 Session 与答题进度保持HTTP 是无状态的用户做到一半刷新页面所有答案就丢了。简易考试系统里最常见的方案是把 ExamViewModel 塞进 Session。在加载试题时存一下[HttpGet] public ActionResult Index() { var vm new ExamViewModel(); vm.ExamName ASP.NET MVC 基础测试; vm.Questions LoadQuestions(); Session[ExamProgress] vm; // 保存试卷快照 return View(vm); }提交时再读var saved Session[ExamProgress] as ExamViewModel; if (saved null) return RedirectToAction(Index);这里要注意Session 默认存在 IIS 进程内存中应用池回收或服务器重启都会丢失。如果考试人数较多把 Session 持久化到 SQL Server 或 Redis 是更稳的做法但简易课设完全够用。另外Session 的 Key 别用魔法字符串满天飞建议定义成常量防止多控制器之间拼写不一致。下表列出了表单绑定与 ViewModel 属性的对照关系| HTML name | ViewModel 属性 | 说明 | | UserAnswers[0] | UserAnswers[0] | 第一题答案 | | UserAnswers[1] | UserAnswers[1] | 第二题答案 | | ExamName | ExamName | 隐藏字段或只读展示 |如果想要支持多选题可以把UserAnswers从Liststring改成ListListstring或者用逗号分隔字符串存到一个字段里。前端改成 checkbox用UserAnswers[ i ][]作为 name模型绑定器也能正确收集到集合但解析时要额外做字符串拆分。5. 把项目跑起来applicationhost.config、IIS Express 与 Release 配置的验证技巧5.1 定位 applicationhost.config 并启动站点在 Visual Studio 中按 F5 时IIS Express 会读取项目里隐藏的 .vs 目录生成的 applicationhost.config。如果目录结构变化导致站点无法启动可以手动指定配置文件运行。下面这条命令在无 Visual Studio 的环境下也适用C:\Program Files\IIS Express\iisexpress.exe /config:D:\OnlineExam\.vs\OnlineExam\config\applicationhost.config启动后控制台会列出当前站点名与物理路径看到 Successfully registered 说明站点已经加载。项目若用到 SQL Server 本地库注意 IIS Express 工作进程身份是当前用户默认有权限访问 LocalDB如果连的是 SQL Server 的 Windows 认证实例一般也没问题。5.2 端口冲突与 binding 修改同一台机器上开过多个网站时很容易碰到端口被占用。IIS Express 报错信息里会给出被占用的端口号。此时可以直接改 applicationhost.config 里的 site 节点bindings binding protocolhttp bindingInformation*:8080:localhost / /bindings把 8080 换成 5200 之类的空闲端口。保存后重新执行 /config 命令访问http://localhost:5200/User/Login即可。5.3 Release 配置如何清理调试信息Web.Release.config 和 Web.Debug.config 是 MSBuild 发布时的转换脚本。在不使用发布向导、直接复制代码到服务器时最需要关注的是编译节点里的 debug 属性。Debug 模式下编译体积大、执行路径中有额外日志长期运行最好关掉。可以在 Web.Release.config 里写system.web compilation xdt:TransformRemoveAttributes(debug) / /system.web这样发布后 Web.config 中的debugtrue会被自动移除而本地 Debug 运行不受影响。5.4 用 curl 和浏览器做快速验证启动站点后先访问默认路由看是否跳转到登录页curl -I http://localhost:5200/User/Login正常返回 200 后再继续访问考试首页。如果某个 Action 返回 404先看 Global.asax 里的路由注册再看控制器类名是否以 Controller 结尾如果 500马上打开 Web.config 设置system.web customErrors modeOff / /system.web然后重启 IIS Express让异常信息直接渲染在页面上。根据堆栈里的文件名和行号能快速定位是数据库连接失败还是空引用。修复后记得把 customErrors 改成 RemoteOnly避免线上泄露内部信息。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/13 0:41:43

营销自动化数据驱动:多源数据OLAP架构演进实录

接手营销自动化平台的数据架构时,我们面对的是一堆散落在各个业务库里的用户行为、订单、投放回执和客服记录,运营同学每次做活动复盘都要在 Excel 里手工拉数。后来我们把多源数据汇总进统一的 OLAP 分析引擎,营销活动的定向圈人、漏斗分析、…

2026/9/13 0:41:43

客服场景中单Agent与多Agent系统的技术选型与实践

1. 项目概述:客服场景中的Agent技术选型之争在AI客服领域,单Agent(Single-Agent)与多Agent(Multi-Agent)系统的选择一直是技术团队面临的现实难题。去年我们团队在银行智能客服系统升级时,就曾为…

2026/9/13 0:41:43

AI如何解决本科论文写作痛点:智能工具与应用

1. 本科论文写作的痛点与AI解决方案 本科论文写作是每个大学生必须面对的挑战,但传统写作过程中存在诸多痛点:选题困难、资料收集耗时、格式调整繁琐、查重压力大。这些因素叠加导致学生普遍存在"论文焦虑",严重影响写作效率和质量…

2026/9/13 1:32:10

Django医院挂号系统实战:从数据建模到并发防超卖的完整方案

简介:这是一份基于Python Django框架开发的医院挂号诊疗系统毕业设计源码案例,适合计算机相关专业学生用于毕业设计、课程设计或项目初期演示,也适合希望学习Django Web开发的小白进阶。项目已获导师认可并通过答辩评审,代码经过测…

2026/9/13 1:32:10

MATLAB实现语音识别:MFCC与GMM实战指南

1. 项目概述:当MATLAB遇见语音识别第一次接触语音识别是在研究生时期,实验室的师兄演示了一个能区分不同人说话的MATLAB程序。当时觉得这简直是魔法——机器怎么能听懂人话?更神奇的是,核心算法只用了不到200行代码。如今十年过去…

2026/9/13 1:32:10

硬件工程师必备:常用保护电路设计与选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/13 1:32:10

基于Matlab的光伏与燃气轮机混合动力系统建模与仿真分析

简介:这份Matlab代码资源围绕太阳能-燃气轮机混合动力电力系统,完成从建模、仿真到结果分析的全流程,适合能源、电气、计算机等专业学生进行课程设计、毕业设计,也可供相关工程人员做新能源方案预研。资源包共17个文件&#xff0c…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/12 6:37:43

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码