1

I can do this using Get and RedirectToAction, but wonder why I cant do it using Post? I'm in the Subscriber controller and want to return a view from the CreateTest controller. When it returns, it assumes I'm still in Subscriber and doesn't look in the Controller views.

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);
    }
}   

If I make this call from the CreateTestPortal in Subscriber,

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

It returns the form correctly, but I have to use the GET method and lose the security of the ValidateAntiForgeryToken. Is this default behavior or am I missing a step.

I'm using MVC4

4
  • 3
    Do you have to use a controller to do this? I think the answer is no, controllers are supposed to render views, redirects and what not, but when creating a new instance of an object through a controller, I believe a class would be more suitable. If that is not the case, what is it that you're getting when submitting to the controller? Commented Dec 24, 2013 at 16:40
  • I get this message -- " Server Error in '/' Application. The view CreateTestPortal or its master was not found or no View engine supports the searched locations. The following locations were searched: then it list its search path in Views/Subscriber and Views/Shared What I want it to do is search the CreateTest because the view is there, but it never looks in that folder. It only looks in Subscriber and Shared Commented Dec 24, 2013 at 16:45
  • 4
    You really shouldn't be instantiating controllers manually like this. What is it you're really trying to achieve? Commented Dec 24, 2013 at 17:20
  • 3
    The question everyone is trying to understand is: Why do you need to POST from Controller A to Controller B? Depending on what you are trying to accomplish there are good solutions: partial views, service classes, helpers, rendering views to strings... Commented Dec 24, 2013 at 18:29

3 Answers 3

3

I'm going to go out on a limb here, because it seems you may be unaware that you can POST to a different controller, but that it happens in the view, rather than from an action.

There are several overloads of Html.BeginForm() that give you complete control over what controller/action you want a form to be submitted to. Assuming this is the view for FirstController/Index:

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

SecondController could look like this:

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);
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

yes, that does work. The reason I was trying to do it through the controller was the the original page has multiple button links that are created dynamically when the page is created using jQuery and javascript. (I'm doing it that way because depending on the security level of the user, different buttons are created. That is user A with security level 1 will not see the same options as user B with security level2) I was trying to centralize the logic in the Controller. If I leave the logic on the page, then your way works.
@edepperson Although I understand your concerns regarding centralising the logic, I don't understand the difference it makes, as to why this approach will work or not, based on the scenario you describe. The question I keep coming back to is: why do you need to POST to a different controller? There are times where it needs to happen, but it sounds to me like you want your actions to be logically grouped together, within the same controller, for whatever scenario you're trying to accomplish. So, if that's the case, why not just POST to an action on the same controller?
The original page is a portal page. The links on it point to several unrelated options the user can take. I don't want all that logic grouped in one controller...it would give new meaning to the concept of spaghetti code. Your approach will work, but I'll need to set the action of the form using jQuery. After the discussion surrounding the topic of this post, I've concluded that what I wanted to do originally cannot be done. Thanks to all for their contributions
2

You are doing it wrong! You should not be creating controllers like this. Controllers are not meant to be used like this. If you have a piece of logic that does same for both of the actions - extract that into a service class or command handler and use that class from both of the controllers.

This might look like a lot of extra work, but trust me, this is worth for maintainability.

Learn one rule for MVC - one controller action for one logical action in your application. If you try to assign more than one logical action per controller action - things get dirty and hacky quick. And you'll end up with spaghetti code.

A lot of the times it seems that actions are doing the same thing and you try to refactor it out into common methods. But usually this only seems to be similar actions. In reality either the duplicate action should not exist or 2 actions are doing different things and only look similar.

To answer your direct questions - don't do what you are doing. Stop, think and refactor so you don't need to do these hacks.

UPDATE: And it is correct that you can't navigate to POST actions from other controllers. POST actions are for form submissions. User submits a form from and that goes to POST action with anti-forgery token. If you want to redirect user, you need to do something like this:

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();
    }
}    

7 Comments

My original question was how to use HTTPPost attributer when calling an action in a different controller. @romias says I can't, need to call it using the GET method. I understand that. But then I cant use the AntiForgeryToken. How do you propose to do that? If every action has its own controller and the only way you can go from Controller A to Controller B is by the GET method, then the AntiForgeryToken is useless
I've added a code sample. Anti-forgery token is only for form submissions, not for re-directs.
That is what I was doing originally. I don't think you've answered my question either. But maybe I don't understand the AntiForgeryToken. Does the server use the same token in the Response as was in the Request or is a new AntiForgeryToken created each time there is a Response. If the same one is reused, then it seems to me that Microsoft failed to close the loop by not providing a way to reuse it with a redirect action.
I think that piece is vital. Try sending the token along with the request, meaning, send your validated user info along with the call as extra parameters (username and password depending on your authentication technique). Note that I believe this answer DOES answer the question you asked from your ORIGINAL post.
@edepperson why do you think you need Anti-forgery token for server redirects? And I'm not trying to answer your question. I'm saying that you should not be doing what you are trying to do. You have an incorrect flow of data in your controllers
|
0

If you are calling another controller action or redirecting, you cannot use the HTTPPost attribute. When you post, this is done from the user, and you use a redirect or calling an action is not post. When you have an action decorated with HttpPost, and you call it with a GET, it displays an error as if the object doesn't exists. To make this work you should craft a post request in your server... and this is kind of hacky.

In your case, you can have the content of your controller action in a helper method and call that method from both actions.

That way, you can leave their HTTPPost and ValidateAntiForgeryToken attributes in each action.

1 Comment

Do you have an example of doing that, using my current request context?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.