【问题标题】:MVC4 how do you post from one controller to a different ControllerMVC4 如何从一个控制器发布到另一个控制器
【发布时间】:2013-12-29 05:31:57
【问题描述】:

我可以使用 Get 和 RedirectToAction 做到这一点,但想知道为什么我不能使用 Post 做到这一点? 我在 Subscriber 控制器中,想从 CreateTest 控制器返回一个视图。 当它返回时,它假定我仍在订阅者中并且不在控制器视图中查看。

public class SubscriberController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult CreateTestPortal()
    {
        CreateTestController ctc = new CreateTestController();
        return ctc.CreateTestPortal();
    }
}

public class CreateTestController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult CreateTestPortal()
    {
            //... validation logic
        var vm = new CBT.Models.CreateTest.CreateTestPortal();              
            //... build form logic
        return View(vm);
    }
}   

如果我从订阅服务器中的 CreateTestPortal 进行此调用,

return RedirectToAction("CreateTestPortal", "CreateTest");

它正确返回表单,但我必须使用 GET 方法并失去 ValidateAntiForgeryToken 的安全性。这是默认行为还是我错过了一步。

我正在使用 MVC4

【问题讨论】:

  • 您必须使用控制器来执行此操作吗?我认为答案是否定的,控制器应该呈现视图、重定向等等,但是当通过控制器创建对象的新实例时,我相信类会更合适。如果不是这种情况,您在提交给控制器时会得到什么?
  • 我收到此消息 --" '/' 应用程序中的服务器错误。未找到视图 CreateTestPortal 或其主服务器或没有视图引擎支持搜索的位置。搜索了以下位置:然后列出它在 Views/Subscriber 和 Views/Shared 中的搜索路径我想要它做的是搜索 CreateTest,因为视图在那里,但它从不查看该文件夹。它只查看订阅者和共享
  • 你真的不应该像这样手动实例化控制器。您真正想要实现的目标是什么?
  • 每个人都想明白的问题是:为什么需要从控制器 A POST 到控制器 B?根据您要完成的工作,有很好的解决方案:部分视图、服务类、帮助程序、将视图呈现为字符串...

标签: c# asp.net-mvc asp.net-mvc-4


【解决方案1】:

我将在这里冒险,因为您似乎没有意识到您可以 POST 到另一个控制器,但它发生在视图中,而不是来自一个动作。

Html.BeginForm() 有几个重载,可让您完全控制要将表单提交到的控制器/操作。假设这是FirstController/Index 的视图:

@using (Html.BeginForm("SomeAction", "SecondController", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    // rest of form
}

SecondController 可能如下所示:

public class SecondController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult SomeAction(SomeModel model)
    {
        if (ModelState.IsValid)
        {
            // Add to database, or whatever, and now redirect
            return RedirectToAction("Success");
        }

        // Redisplay the form if validation failed.
        return View(model);
    }
}

【讨论】:

  • 是的,确实有效。我试图通过控制器执行此操作的原因是原始页面具有多个按钮链接,这些链接是在使用 jQuery 和 javascript 创建页面时动态创建的。 (我这样做是因为根据用户的安全级别,会创建不同的按钮。即安全级别为 1 的用户 A 不会看到与安全级别 2 的用户 B 相同的选项)我试图集中控制器中的逻辑。如果我把逻辑留在页面上,那么你的方法就行了。
  • @edepperson 虽然我理解您对集中逻辑的担忧,但我不明白它的不同之处,即根据您描述的场景,这种方法为什么会起作用。我不断回到的问题是:为什么你需要 POST 到不同的控制器?有时它需要发生,但在我看来,无论你想要完成什么场景,你都希望你的动作在逻辑上组合在一起,在同一个控制器中。那么,如果是这种情况,为什么不直接 POST 对同一控制器执行操作?
  • 原始页面是门户页面。它上面的链接指向用户可以采取的几个不相关的选项。我不希望所有逻辑都集中在一个控制器中……它会给意大利面条代码的概念赋​​予新的含义。您的方法可行,但我需要使用 jQuery 设置表单的操作。围绕这篇文章的主题讨论之后,我得出的结论是,我原本想做的事情是做不到的。感谢大家的贡献
【解决方案2】:

你做错了!您不应该像这样创建控制器。控制器不应该像这样使用。如果您有一段对两个操作都执行相同操作的逻辑 - 将其提取到服务类或命令处理程序中,并从两个控制器中使用该类。

这可能看起来像很多额外的工作,但相信我,这对于可维护性是值得的。

了解 MVC 的一项规则 - 一个控制器操作对应一个应用程序中的一个逻辑操作。如果您尝试为每个控制器操作分配多个逻辑操作 - 事情会很快变得肮脏和骇人听闻。你最终会得到意大利面条代码。

很多时候,动作似乎在做同样的事情,而您尝试将其重构为常用方法。但通常这似乎只是类似的动作。实际上,要么重复的动作不应该存在,要么 2 个动作正在做不同的事情,只是看起来相似。

回答您的直接问题 - 不要做您正在做的事情。停下来,思考和重构,这样你就不需要做这些黑客了。

更新:您无法从其他控制器导航到 POST 操作是正确的。 POST 动作用于表单提交。用户提交表单并使用防伪令牌进入 POST 操作。如果你想重定向用户,你需要做这样的事情:

public class HomeController : Controller
{
    public ActionResult Blah(int someParam)
    {
        // do something

        return RedirectToAction("SomeOtherAction", "SomeOther");
    }
}

public class SomeOtherController : Controller
{
    public ActionResult SomeOtherAction()
    {
        // and do something here
        return View();
    }
}    

【讨论】:

  • 我最初的问题是如何在不同控制器中调用操作时使用 HTTPPost 属性。 @romias 说我不能,需要使用 GET 方法调用它。我明白那个。但后来我不能使用 AntiForgeryToken。你打算怎么做?如果每个动作都有自己的控制器,并且从控制器 A 到控制器 B 的唯一方法是通过 GET 方法,那么 AntiForgeryToken 就没用了
  • 我添加了一个代码示例。防伪令牌仅用于表单提交,不适用于重定向。
  • 原来我就是这么干的。我想你也没有回答我的问题。但也许我不了解 AntiForgeryToken。服务器是否在响应中使用与请求中相同的令牌,或者每次有响应时都会创建一个新的 AntiForgeryToken。如果同一个被重用,那么在我看来,微软没有提供一种通过重定向操作重用它的方法来关闭循环。
  • 我认为这件作品至关重要。尝试将令牌与请求一起发送,这意味着将经过验证的用户信息与调用一起作为额外参数(用户名和密码取决于您的身份验证技术)发送。请注意,我相信这个答案确实回答了您在原始帖子中提出的问题。
  • @edepperson 为什么你认为你需要防伪令牌来进行服务器重定向?我不是要回答你的问题。我是说你不应该做你想做的事。您的控制器中有不正确的数据流
【解决方案3】:

如果您正在调用另一个控制器操作或重定向,则不能使用 HTTPPost 属性。当您发布时,这是由用户完成的,并且您使用重定向或调用操作不是发布。当您有一个使用 HttpPost 修饰的操作并使用 GET 调用它时,它会显示一个错误,就好像该对象不存在一样。要完成这项工作,您应该在您的服务器中制作一个发布请求......这有点hacky。

在您的情况下,您可以将控制器操作的内容放在辅助方法中,并从两个操作中调用该方法。

这样,您可以在每个操作中保留他们的 HTTPPostValidateAntiForgeryToken 属性。

【讨论】:

  • 您有使用我当前请求上下文的示例吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-30
  • 1970-01-01
  • 2018-06-28
  • 2013-05-21
  • 1970-01-01
  • 2017-03-23
相关资源
最近更新 更多