如何继承 ASP.NET MVC 控制器并仅更改视图?

Posted

技术标签:

【中文标题】如何继承 ASP.NET MVC 控制器并仅更改视图?【英文标题】:How can I inherit an ASP.NET MVC controller and change only the view? 【发布时间】:2009-10-14 15:28:05 【问题描述】:

我有一个从基本控制器继承的控制器,我想知道如何利用基本控制器的所有逻辑,但返回与基本控制器不同的视图。

基本控制器填充一个模型对象并将该模型对象传递给它的视图,但我不确定如何在子控制器中访问该模型对象,以便将它传递给子控制器的视图。

【问题讨论】:

您是否尝试仅从基本控制器继承并准备视图? 【参考方案1】:

几点。如果您知道这就是您要返回的全部内容,则可以将返回值键入为 ViewResult。然后,您可以从覆盖的实现中查询该值。更重要的是,根据 MVC v1 源码,调用 View(object) 只是在控制器上设置 ViewData.Model,然后构造一个 ViewResult。

Controller.cs:440

protected internal ViewResult View(object model) 
    return View(null /* viewName */, null /* masterName */, model);

Controller.cs:456

protected internal virtual ViewResult View(string viewName, string masterName, object model) 
    if (model != null) 
        ViewData.Model = model;
    

    return new ViewResult 
        ViewName = viewName,
        MasterName = masterName,
        ViewData = ViewData,
        TempData = TempData
    ;

所以你需要做的就是调用基础方法并调用 View(string)。

namespace BaseControllers

    public class CoolController
    
        public virtual ViewResult Get() 
        
            var awesomeModel = new object();
            return View(awesomeModel);
        
    


public class CoolController : BaseControllers.CoolController

    public override ViewResult Get() 
    
        var ignoredResult = base.Get();
        // ViewData.Model now refers to awesomeModel
        return View("NotGet");
    

当然,您会浪费 CPU 周期来构建您忽略的 ViewResult。因此,您可以这样做:

public class CoolController : BaseControllers.CoolController

    public override ViewResult Get() 
    
        var baseResult = base.Get();
        baseResult.ViewName = "NotGet";
        return baseResult;
    

如果您的基本控制器返回 ActionResult,您必须在更改 ViewName 之前将其强制转换为 ViewResult。

【讨论】:

【参考方案2】:

我的应用示例:

基类:

public abstract class BaseTableController<T,TU> : BaseController where TU : IGenericService<T>,IModelWrapperService

    protected readonly TU _service;

    public BaseTableController(TU service)
    
        _service = service;
        _service.ModelWrapper = new ControllerModelStateWrapper(ModelState);
    


    public ActionResult Index()
    
        return View(_service.List());
    

继承:

public class SeverityController : BaseTableController<Severity, ISeverityService>

    public SeverityController(ISeverityService service)
        : base(service)
    
    

   //NO CODE INSIDE

SeverityController.Index() 导致 Views/Severity/Index.aspx。只需要准备视图。严重性是我的错误跟踪应用程序中的字典之一。每个字典都有类似的逻辑,所以我可以分享一些代码。

【讨论】:

【参考方案3】:

根据此线程上给出的反馈,我实施了一个类似于 Antony Koch 提出的解决方案。

我没有使用抽象方法,而是使用了具体的虚拟 GetIndex 方法,这样我就可以在其中为基本控制器添加逻辑。

public class SalesController : Controller

    // Index view method and model
    public virtual ActionResult GetIndex()
    
         return View("Index", IndexModel);
    
    protected TestModel IndexModel  get; set; 

    public virtual ActionResult Index()
    
        ViewData["test"] = "Set in base.";

        IndexModel = new TestModel();
        IndexModel.Text = "123";

        return GetIndex();
    

    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Index(TestModel data, FormCollection form)
    
        TryUpdateModel(data, form.ToValueProvider());
        IndexModel = data;

        return GetIndex();
    


// This class will need to be in a different namespace or named differently than the
// parent controller
public class SalesController : MyApp.Controllers.BaseControllers.SalesController

    // Index view method and model
    public override ActionResult GetIndex()
    
        return View("ClientIndex", IndexModel);
    

    public override ActionResult Index()
    
        return base.Index();
    

    [AcceptVerbs(HttpVerbs.Post)]
    public override ActionResult Index(TestModel data, FormCollection form)
    
        return base.Index(data, form);
    

【讨论】:

我也遇到了同样的问题,谁能帮帮我? My Question 现在您只需为子控制器创建一个新文件夹,子控制器的操作方法就会出现在该文件夹中。【参考方案4】:
public class BaseController : Controller 
protected BaseController() 

public ActionResult Index()

    return GetIndex();


public abstract ActionResult GetIndex();  

public class MyController : BaseController 
public MyController() 

public override GetIndex()
   
    return RedirectToAction("Cakes","Pies");
 

只需使用抽象从子类中调用您需要的位。

【讨论】:

我做了类似的事情。请参阅我的新答案。我希望避免为每个控制器操作创建辅助方法,但我想这是最好的方法。【参考方案5】:

我最终只是在基本控制器上添加了一个额外的参数——viewName。

似乎工作得很好。

我是否遗漏了任何主要缺点?

public class SalesController : Controller

    public virtual ActionResult Index(string viewName)
    
        ViewData["test"] = "Set in base.";

        TestModel model = new TestModel();
        model.Text = "123";

        return String.IsNullOrEmpty(viewName) ? View(model) : View(viewName, model);
    

    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Index(TestModel data, FormCollection form, string viewName)
    
        TryUpdateModel(data, form.ToValueProvider());
        return String.IsNullOrEmpty(viewName) ? View(data) : View(viewName, data);
    


public class SalesController : MyApp.Controllers.BaseControllers.SalesController

    public override ActionResult Index(string viewName)
    
        return base.Index("ClientIndex");
    

    [AcceptVerbs(HttpVerbs.Post)]
    public override ActionResult Index(TestModel data, FormCollection form, string viewName)
    
        return base.Index(data, form, "ClientIndex");
    

【讨论】:

我担心的是用户可以通过 http 将参数发送到控制器中的 ActionResult。您冒着用户能够在不覆盖“索引”的控制器上调用索引的风险,从而能够传递具有意外结果的 viewName。不太可能,但仍有可能。 我不喜欢这个解决方案。您不必指定视图名称。想象一下,您有一个 BaseController 和两个继承 - ChildOneController 和 ChildTwoController。如果您调用 ~/ChildOne/Index,它将从 ~/ChildOne/Index/Index.aspx 获取视图。如果您调用 /ChildTwo/Index,它将从 ~/ChildTwo/Index/Index.aspx 获取视图。视图名称相同,但取自不同的地方。 我也遇到了同样的问题,谁能帮帮我? My Question

以上是关于如何继承 ASP.NET MVC 控制器并仅更改视图?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过更改 ASP.NET MVC 中的 Url 来阻止对控制器的访问?

asp.net mvc如何为控制器的动作指定视图

asp.net 如何变成 MVC 或 WebApi?

保存数据库控制器类 asp.net mvc5 身份中的更改

如何在 Asp.Net MVC5 中使用 Ajax 将数据库更改保存到拖放列表

ASP.NET MVC 控制器通过继承控制器来达到 过滤 并且多了一个IAuthenticationFilter