在ASP.NET MVC框架中,Controller扮演着系统"交通指挥员"的角色。我刚开始接触MVC时,常常困惑于为什么要把业务逻辑放在Controller而不是直接写在View里。经过多个项目的实战后才明白,这种分离设计让代码维护性提升了至少三倍。
控制器本质上是个普通的C#类,继承自System.Web.Mvc.Controller基类。但它的特殊之处在于:
典型的HomeController骨架如下:
csharp复制public class HomeController : Controller
{
private readonly IProductService _service;
// 构造函数注入依赖项
public HomeController(IProductService service) {
_service = service;
}
public ActionResult Index() {
var model = _service.GetFeaturedProducts();
return View(model);
}
}
动作方法(Action)的命名遵循明确的约定:
我在实际项目中总结的最佳实践:
csharp复制// 推荐写法
public ActionResult GetUser(int id) {
//...
}
// 应避免的做法
public string GetUserString(int id) {
// 直接返回字符串不利于后期扩展
}
ASP.NET MVC提供了强大的模型绑定系统,这是我见过最智能的绑定方案之一:
csharp复制// URL: /products/details/5
public ActionResult Details(int id) {
// id自动绑定为5
}
csharp复制public ActionResult Create(Product product) {
// 自动将表单字段映射到product对象
}
csharp复制// 绑定到前缀不同的对象
public ActionResult Update([Bind(Prefix="item")]Product prod) {
// 处理item.Name等字段
}
重要提示:在接收用户输入时,务必使用[Bind]特性白名单或ViewModel模式,避免过度提交攻击。
过滤器是Controller的"中间件",我常用的有:
csharp复制[Authorize(Roles="Admin")]
public ActionResult Delete(int id) {
// 仅管理员可访问
}
csharp复制[HandleError(ExceptionType=typeof(DbException), View="DatabaseError")]
public ActionResult UpdateInventory() {
// 数据库异常时跳转到定制错误页
}
csharp复制public class AuditFilter : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
// 记录操作日志
Logger.Log(filterContext.ActionDescriptor.ActionName);
}
}
在高并发场景下,异步控制器能显著提升吞吐量。现代ASP.NET MVC推荐使用async/await模式:
csharp复制public async Task<ActionResult> Search(string keyword) {
var results = await _searchService.QueryAsync(keyword);
return View(results);
}
实测数据对比:
症状:404错误但控制器存在
解决方案:
csharp复制routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
);
典型表现:表单提交后模型属性为null
处理步骤:
错误信息:"The view 'Index' or its master was not found"
排查方案:
csharp复制return View("~/Views/Shared/SpecialView.cshtml");
通过诊断发现,控制器实例化可能成为性能瓶颈。我的优化方案:
csharp复制private Lazy<ReportService> _reportService = new Lazy<ReportService>(() => {
return new ReportService(ConfigurationManager.AppSettings["ReportPath"]);
});
csharp复制[OutputCache(Duration=3600, VaryByParam="category")]
public ActionResult GetProducts(string category) {
// 结果缓存1小时
}
csharp复制// 错误做法
foreach(var item in list) {
ViewData["Item"] = item;
return View(); // 每次循环都创建新ViewResult
}
// 正确做法
var model = list.Select(i => new ItemViewModel(i));
return View(model);
我推荐的测试组合:
csharp复制[Fact]
public void Index_ReturnsViewResult() {
var controller = new HomeController();
var result = controller.Index();
Assert.IsType<ViewResult>(result);
}
csharp复制[Fact]
public void Create_Post_ValidModel_Redirects() {
var mockService = new Mock<IProductService>();
var controller = new ProductController(mockService.Object);
var result = controller.Create(new ProductCreateVM {
Name = "Test",
Price = 9.99m
}) as RedirectToRouteResult;
Assert.Equal("Index", result.RouteValues["action"]);
}
必须使用的防护措施:
csharp复制[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateProfile(UserProfile model) {
// 处理表单提交
}
对应的视图需添加:
html复制@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
<!-- 表单内容 -->
}
分层验证方案:
html复制@Html.TextBoxFor(m => m.Email,
new { @class = "form-control", type = "email" })
csharp复制public class LoginVM {
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, MinimumLength = 6)]
public string Password { get; set; }
}
csharp复制if (_userService.IsEmailRegistered(model.Email)) {
ModelState.AddModelError("Email", "该邮箱已注册");
}
经过多个项目迭代,我总结出控制器的黄金法则:
改造前后对比:
csharp复制// 改造前
public ActionResult Create(ProductCreateVM model) {
if(ModelState.IsValid) {
var product = new Product();
product.Name = model.Name;
product.Price = model.Price;
// 10+行属性赋值...
_db.Products.Add(product);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
// 改造后
public ActionResult Create(ProductCreateVM model) {
if(ModelState.IsValid) {
_productService.CreateProduct(model);
return RedirectToAction("Index");
}
return View(model);
}
对于复杂业务系统,我采用命令查询分离模式:
csharp复制public class ProductsController : Controller {
private readonly IMediator _mediator;
public ProductsController(IMediator mediator) {
_mediator = mediator;
}
public ActionResult Index() {
var query = new GetActiveProductsQuery();
var model = _mediator.Send(query);
return View(model);
}
[HttpPost]
public ActionResult Create(CreateProductCommand command) {
var result = _mediator.Send(command);
if(result.Success) {
return RedirectToAction("Index");
}
// 处理错误
}
}
这种架构下,控制器仅作为协调者,不包含任何业务逻辑。