在不同的控制器操作方法之间传递数据

78

我正在使用ASP.NET MVC 4。我试图从一个控制器将数据传递到另一个控制器,但是我做不对。 我不确定这是否可能?

这是我想要从中传递数据的源操作方法:

public class ServerController : Controller
{
     [HttpPost]
     public ActionResult ApplicationPoolsUpdate(ServiceViewModel viewModel)
     {
          XDocument updatedResultsDocument = myService.UpdateApplicationPools();

          // Redirect to ApplicationPool controller and pass
          // updatedResultsDocument to be used in UpdateConfirmation action method
     }
}
我需要将它传递给此控制器中的此操作方法:
public class ApplicationPoolController : Controller
{
     public ActionResult UpdateConfirmation(XDocument xDocument)
     {
          // Will add implementation code

          return View();
     }
}

我已经在ApplicationPoolsUpdate动作方法中尝试了以下代码,但它无法工作:

return RedirectToAction("UpdateConfirmation", "ApplicationPool", new { xDocument = updatedResultsDocument });

return RedirectToAction("UpdateConfirmation", new { controller = "ApplicationPool", xDocument = updatedResultsDocument });

我该如何实现这个目标?


3
这个问题困扰我很久,基本上可以使用TempData / Session。请参考https://dev59.com/5XRB5IYBdhLWcg3wSVYI - glosrob
5个回答

78

HTTP和重定向

首先让我们回顾一下ASP.NET MVC是如何工作的:

  1. 当收到一个HTTP请求时,它会与一组路由进行匹配。如果路由与请求匹配,则调用与路由对应的控制器操作。
  2. 在调用操作方法之前,ASP.NET MVC执行模型绑定。模型绑定是将HTTP请求的内容(基本上只是文本)映射到操作方法的强类型参数的过程。

让我们也提醒一下什么是重定向:

HTTP重定向是Web服务器向客户端发送的响应,告诉客户端在不同的URL下查找所请求的内容。新的URL包含在Web服务器返回给客户端的Location头中。在ASP.NET MVC中,通过从操作中返回RedirectResult来进行HTTP重定向。

传递数据

如果您只想传递简单的值,比如字符串和/或整数,您可以将它们作为查询参数传递到Location头中的URL中。如果使用以下内容,则会发生这种情况:

return RedirectToAction("ActionName", "Controller", new { arg = updatedResultsDocument });

正如其他人建议的那样,这种方法不起作用的原因是XDocument可能是一个非常复杂的对象。ASP.NET MVC框架没有直接的方法将文档序列化为适合于URL的内容,然后从URL值绑定回您的XDocument操作参数。

通常情况下,将文档传递给客户端,以便客户端在下一个请求中将其传递回服务器,是一种非常脆弱的过程:它需要各种序列化和反序列化,各种问题都可能出现。如果文档很大,这也可能是带宽的严重浪费,可能会严重影响应用程序的性能。

相反,您要做的是在服务器上保留文档,并返回一个标识符给客户端。然后客户端将标识符与下一个请求一起传递,服务器使用此标识符检索文档。

存储数据以供在下一个请求中检索

那么,现在的问题是,服务器在此期间存储文档的位置在哪里?好吧,这取决于您的特定情况,最佳选择将取决于您的具体情况。如果这个文档需要长期可用,您可以将其存储在磁盘或数据库中。如果它只包含暂时的信息,将其保存在Web服务器的内存中、在ASP.NET缓存或Session(或TempData,最终与Session几乎相同)中可能是正确的解决方案。无论哪种方式,您都需要使用一个键来存储文档,以便稍后检索文档:

int documentId = _myDocumentRepository.Save(updatedResultsDocument);

然后将该密钥返回给客户端:

return RedirectToAction("UpdateConfirmation", "ApplicationPoolController ", new { id = documentId });
当您想要检索文档时,只需根据关键字获取它:
 public ActionResult UpdateConfirmation(int id)
 {
      XDocument doc = _myDocumentRepository.GetById(id);

      ConfirmationModel model = new ConfirmationModel(doc);

      return View(model);
 }

1
谢谢Rune。很好。 - Devsainii

69

你尝试过使用 ASP.NET MVC TempData 吗?

ASP.NET MVC TempData 字典用于在控制器操作之间共享数据。 TempData 的值会一直存在,直到它被读取或当前用户的会话超时为止。在 TempData 中持久化数据在重定向等场景中非常有用,因为可能需要这些值跨越单个请求。

代码大致如下:

[HttpPost]
public ActionResult ApplicationPoolsUpdate(ServiceViewModel viewModel)
{
    XDocument updatedResultsDocument = myService.UpdateApplicationPools();
    TempData["doc"] = updatedResultsDocument;
    return RedirectToAction("UpdateConfirmation");
}

在ApplicationPoolController中:

public ActionResult UpdateConfirmation()
{
    if (TempData["doc"] != null)
    {
        XDocument updatedResultsDocument = (XDocument) TempData["doc"];
            ...
        return View();
    }
}

1
我理解的是,如果我没有在TempData下面放置一个共享会话缓存,那么它就不能安全地用于跨多个负载平衡服务器。 - GGleGrand
我仍然相信我们应该尽一切可能避免在这种情况下使用Session,而应该使用ViewData。它们没有类型,并且仅在“In-proc”会话模式下工作。对于任何使用“State Sever”或“Sql Server”会话模式的负载平衡服务器,ViewData都会丢失。 - Blaise
我们能在大型复杂对象中使用TempData吗? - Ali123

11

个人而言,我不太喜欢使用TempData, 而更喜欢传递类型强制的对象,如在ASP.Net-MVC中控制器间传递信息中所解释的。

您应该总是找到一种使其明确且符合预期的方法。


TempData是有意制作的,用于在不暴露所有数据于客户端URL或请求中的情况下,在视图控制器之间传递一些简单的数据。它并不理想,但比Sessions更有效率。它可以完美处理ViewModels(或简单模型)。这是一个方便的功能要使用。 - Piotr Kula
2
它不处理ViewData,因为ViewData不可序列化,这是在无法在进程内运行的会话(Web Farm)中的要求...我非常不喜欢TempData(这是我能说的最好的方式)。 - Novox

4

我更喜欢使用这个替代 TempData

public class Home1Controller : Controller 
{
    [HttpPost]
    public ActionResult CheckBox(string date)
    {
        return RedirectToAction("ActionName", "Home2", new { Date =date });
    }
}

另一个 控制器操作

public class Home2Controller : Controller 
{
    [HttpPost]
    Public ActionResult ActionName(string Date)
    {
       // do whatever with Date
       return View();
    }
}

虽然已经晚了,但我希望将来能为任何人提供帮助


3
但在许多情况下,我们希望在“RedirectToAction”中传递一个复杂的模型。 - Blaise

-3
如果您需要从一个控制器传递数据到另一个控制器,必须通过路由值传递数据。因为它们是不同的请求。如果您需要从一页发送数据到另一页,则必须使用查询字符串(与路由值相同)。
但是您可以做一个技巧:
在调用操作中将被调用的操作称为简单方法:
public class ServerController : Controller
{
 [HttpPost]
 public ActionResult ApplicationPoolsUpdate(ServiceViewModel viewModel)
 {
      XDocument updatedResultsDocument = myService.UpdateApplicationPools();
      ApplicationPoolController pool=new ApplicationPoolController(); //make an object of ApplicationPoolController class.

      return pool.UpdateConfirmation(updatedResultsDocument); // call the ActionMethod you want as a simple method and pass the model as an argument.
      // Redirect to ApplicationPool controller and pass
      // updatedResultsDocument to be used in UpdateConfirmation action method
 }
}

4
你认为这样做值得吗?当你尝试在UpdateConfirmation操作中返回View()时,它会改变路由值和正确加载视图吗? - K D
肯定的。因为如果你调用UpdateConfirmation(),那么返回的视图是基于“UpdateConfirmation”操作的,“/ ApplicationPool / UpdateConfirmation”视图。你应该尝试这个。我认为会起作用。 - jishnu saha
1
不会的。它不会改变路由值字典的值,因此MVC将尝试为首先执行的操作加载视图 :) - K D
但是从第一个操作中,您返回一个包含第二个视图地址的视图。因此它会起作用。 - jishnu saha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接