【问题标题】:What Views can a Controller Action return?控制器动作可以返回哪些视图?
【发布时间】:2014-05-02 08:31:51
【问题描述】:

在 ASP.NET MVC 5 中,是否每个 Controller Action 都必须返回与 Controller 同名的 View?

这是我的项目。有一个网页,其中包含一个将图像上传到数据库的按钮。加载网页时,我希望它显示已上传的所有图像的列表。因此,该控制器的索引(默认)操作从数据库中加载图像,并返回索引视图,该视图依次显示图像列表:

public ActionResult Index()
{
    // Load the images from the database
    var images = GetImages();

    return View(images);
}

在同一个网页上,有一个按钮允许用户将图像上传到数据库。该按钮调用上传操作,它根据传递的“文件”和“文件夹”参数上传文件,然后最终再次返回索引视图:

[HttpPost]
public ActionResult Upload(HttpPostedFileBase file, string folder)
{
    // Upload the file from the specified folder
    // ...
    // ...
    // ...

    return Index();
}

但是,当用户单击此上传按钮时,会显示以下错误消息:

The view 'Upload' or its master was not found or no view engine supports the searched locations

但我并没有尝试渲染名为“Upload”的视图 - 我正在尝试渲染名为“Index”的视图,这就是我使用 return Index(); 行的原因。

对我哪里出错有帮助吗?

【问题讨论】:

标签: asp.net-mvc


【解决方案1】:

回答

虽然 VitaliyNathan A 提供了充分的答案,但我想解释一下为什么您的初始方法不起作用,因为这是一个很好的问题而且似乎没有意义。

要得到我们的答案,我们必须查看ASP.NET MVC source code

在我们开始之前,让我们来看看你的代码。

  1. 用户访问(或发布到)/Controller/Upload
  2. 我们做一些逻辑然后返回Index()
  3. Index() 是一种方法,它使用自己的模型返回自己的视图
  4. MVC 找不到“上传”视图并引发异常

出了什么问题?

首先知道Index()正在被调用并成功返回。模型对象也被传递给视图(如果找到的话)。

当您返回Index() 时,它会返回View(),这是从Controller 类继承的方法,它返回ViewResult

ViewResult 继承自 ViewResultBase

当返回 ViewResult 时,它会调用 ExecuteResult()

看看ExecuteResult()的源代码:

public override void ExecuteResult(ControllerContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException("context");
    }
    if (string.IsNullOrEmpty(this.ViewName))
    {
        this.ViewName = context.RouteData.GetRequiredString("action");
    }
    ViewEngineResult viewEngineResult = null;
    if (this.View == null)
    {
        viewEngineResult = this.FindView(context);
        this.View = viewEngineResult.View;
    }
    TextWriter output = context.HttpContext.Response.Output;
    ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output);
    this.View.Render(viewContext, output);
    if (viewEngineResult != null)
    {
        viewEngineResult.ViewEngine.ReleaseView(context, this.View);
    }
}

这里的关键是context.RouteData.GetRequiredString("action")。此代码获取操作名称,以便可以找到并呈现视图。请注意,它使用的是ControllerContext

因此,您的操作实际上是在首次调用 Upload() 方法时设置的。如果您单步执行 Index() 方法,您将看到调用 context.RouteData.GetRequiredString("action") 将返回字符串 "Upload"

这是因为,在用户请求的上下文中,操作实际上是上传(即他们请求的页面)。

趣事

如果您返回 Index() 并且该方法恰好改变了 ViewBag (ViewData),那么无论呈现什么,ViewData 都会被改变。

如果您的Upload() 这样做:

ViewBag.Test = "Upload method";

然后你返回 Index() 并且你的 Index() 这样做:

ViewBag.Test = "Index method";

那么Test的值将是“索引方法”

【讨论】:

  • 简短的回答是:使用RedirectToAction()
【解决方案2】:

查看有关 View 方法的文档。它有几个你可以提供的参数,其中一个是视图名称的字符串,但如果你想返回一个视图,你总是使用 View() 方法。

但是,如果您不想使用默认视图名称(作为操作方法的名称),只需使用字符串指定新名称,如下所示:

public ActionResult Upload(HttpPostedFileBase file, string folder)
{
    return View("Index");
}

【讨论】:

    【解决方案3】:

    你可以这样做:

    return RedirectToAction("Index");
    

    或:

    return View("Index");
    

    【讨论】:

    • 不同之处在于,使用重定向,浏览器上的 url 实际上会匹配您的索引页面,而使用 view 方法,您会看到您的索引页面,但 URL 仍然是“上传”。为了减少混淆,我建议重定向是更好的选择。
    • 谢谢,效果很好。但是,我仍然对为什么我的方法不起作用感到困惑。我的上传函数需要一个 ActionResult 返回值,它返回 Index(),它本身返回一个 ActionResult 值。因此,该函数最终会获得预期的视图 - 为什么会导致错误?
    【解决方案4】:

    只是一个旁注,您不必为控制器名称显示 View 文件夹中的视图。你可以这样做

    public ActionResult something()
    {
        return View("../OtherView/somethingElse");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多