ASP.NET MVC - POST Action Method with Additional Parameters from URL - c#

With ASP.net MVC is it possible to POST a form to a controller action which includes parameters not in the form, but from the URL?
For example
The Action method in GroupController:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(int idOne, int idTwo, Model model)
{ ... }
The route:
"{controller}/{action}/{idOne}/{idTwo}"
Posted URL:
/Employee/Show/1/42
In this example, the form is being posted to a different controller, the model has the correct value, however the other parameters have default values 0.
The behavior I was expecting is that the ModelBinder would see that I have two parameters that match the given route, and assign the current values of 1 and 42 to the parameters in the same same way a GET operation works.
Is this behavior not supported, or am I missing something?
EDIT:
To be clear, the form on the Show view for the controller Employee contains a form which is posting to a different controller. We can call it Group.
The form action URL looks like this
/Groups/Create/0/0
The form is declared as follows
Html.BeginForm("Create", "Groups")
After trying many different overloads for Html.BeginForm I have found that the parameters are only mapped when the form action URL matches the current URL in the browser address bar.
So if i navigate to the URL /Groups/Create/1/42 I will have a new form. If I then submit the form, the URL route values are passed to the POST action.

If I understand your question correctly, you want the action of the rendered <form> element pointing to URL containing route values. This should be possible with one of the overloads of the HtmlHelper.BeginForm() extension method:
Html.BeginForm("action","controller", new { idOne=1, idTwo=2 }, FormMethod.Post);
Let me know if I got your question all wrong :)

I'm pretty sure that you can only post form data from inputs inside the form. Have you considered rendering the view in such a way to create form input values off of the URL (perhaps with an HTML helper?).
UPDATE: If you don't want to use the form at all, use ControllerContext.RouteData.Values["idOne"] as opposed to passing it in through the method signature.

I had a similar problem, the configuration RouteConfig has solved this problem.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
...
routes.MapRoute(
"MyRoute",
"{controller}/{action}/{idOne}/{idTwo}",
new
{
controller = "Employee", // as an example
action = "Show",
idOne = UrlParameter.Optional,
idTwo= UrlParameter.Optional
}, new { idOne = #"\d{1,5}" });
}
}
...And...
Html.BeginRouteForm()
#using (Html.BeginRouteForm("MyRoute", new { idOne = 1, idTwo= 2 }, FormMethod.Post))
Writes an opening tag to the response. When the user submits
the form, the request will be processed by the route target.
This member is overloaded. For complete information about this member,
including syntax, usage, and examples, click a name in the overload
list.
And all works

I recently had this issue too, and because I had a different route, it was mapping to the default route, without taking into account the extra route params that I was passing in.
So to get it working quickly, I wrote the form using form tags instead, and used #Url.Action to create the required action.

Once you get to your Create view, your route values used to get there need to be re-posted to the post action.
So, one option is having a pair of hiddens, to hold the ids that came from the route. This way, once you post the formas, its values are posted along with the other inputs.

Related

How to call a controller that is in another assembly when using Html.Action()?

I want to use method Html.Action() in my project.
I have two projects.
project 1 - area - HomeController - IndexAction
project 2 - i write a function helper to use in my layout.
public static IHtmlString RenderTest(this HtmlHelper htmlHelper)
{
string mhs = "";
mhs += htmlHelper.Action("Index", "Home", new { area = "area" });
return new MvcHtmlString(mhs);
}
for project 1, I write a route map:
context.MapRoute("area_default",
"Theme/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { string.Format("{0}.Controllers", this.GetType().Namespace) }
);
How can I use this function to load a controller that is in another assembly?
Html.Action("Index","Home", new { area = "area" });
In addition I have a duplicate controller names in each assembly,
e.g. Namespace1.FooController and Namespace2.FooController
I don't have a problem with my routes. Also, I can call any controller in different assemblies via URL/routes.
But I cann't use these urls in my HtmlHelper.Action().
Actually I want to call an action in controller that is in another assembly and get the view of that action as HtmlString but processed.
If you are sure you don't have duplicate controller names in each assembly, e.g. Namespace1.FooController and Namespace2.FooController, then you can simply just add all the namespaces that should be searched in the array for the namespaces param.
However, if you do have duplicate names, then the route will end up matching multiple controllers, which is a problem. There's no order of ops to the namespaces parameter - all are searched and all are treated equally, despite the order of the namespaces. If that's the case, then you'll have to define multiple routes, each tied to a specific namespace.
UPDATE
actually i want call a action in controller in another assembly and get the view of that action as HtmlString but processed.
Oh. You mean you literally want to call the action like a method in another piece of code, rather than get to it via a URL? You could use something like WebClient to actually make a request and get the response as a string. That's admittedly probably not the most efficient way, but it likely would be the easiest.
The alternative is much more complicated. If it's just a regular action that returns a ViewResult, then that's what you'll actually get back from calling it like a method. However, from a quick debugging session, it appears that the ViewResult is not processed until after it's returned from the action and goes back into pipeline (not just another action). As a result, all you get is an object with the name of the view to be used and all the view data that should be used to render it with, i.e. not an actual rendered string that you could do anything with. However, using something like RazorEngine, you might be able to manually take the data from the ViewResult and do something with it.

passing in an additional parameter with the model to a View

Still learning Mvc so sorry if this question is a bit weird.
I am passing the model in my Controller method and want to pass an additional parameter.
what I want to achieve is something like this but the parameter overload does not allow for the 'additional' parameter.
#using (Html.BeginForm("SubmitCall", "Home", new { Id = Model.ProductDetail.ProductId }))
and then in the controller
public ActionResult SubmitCall(ViewModel model, int Id)
{
return RedirectToAction("DetailViewById", model, new {productId = Id});//not allowed
}
Is there a better way of achieving this?
Kind regards
If your only intention is to redirect the client to another URL then the above scenario is not the right way to achieve this.
RedirectToAction will send a HTTP 302 with the Location information generated from your route information (again, in your case this would be the URL to DetailViewById with productId as a parameter or part of the URL - depends on your route configuration).
If you need to persist the ViewModel that is submitted by the client and operate on it once the user is requesting your redirected action, then i would suggest to use TempData which is designed for between-request persistence of such data.
Usually, the ViewModel is a combination of what you have coming back from database (DTO) + extra bits and pieces you need TO and FROM View.
So in my opinion, you should add this extra property to your Viewmodel.
Create an anonymous object for parameters:
return RedirectToAction("DetailViewById",
new {
productId = Id,
model.Property1,
model.Property2,
...
});

Multiple URL routes to a single action method

This is probably something very simple, but Googling feebly isn't getting me anywhere.
In the web application we're building, we've got Projects, and Templates. Templates are really just Projects, with the IsTemplate flag set to true. So naturally, we've got a single Project controller that handles (or will handle) both cases.
We've got one route to a New action method on the controller:
Project/New
That's just the standard {controller}/{action}/{id} route handling that one. Now, the New action method has an IsTemplate parameter. I'd like to have one route where that's passed in as false (the one above), and a second one where it's passed in as true:
Templates/New
What's the proper way to mask an arbitrary action method parameter with varying URLs like that? I tried the following, but it just confused the routing (Html.ActionLink ends up pointing at Templates/New):
routes.MapRoute(
null,
"Template/New",
new { controller = "Project", action = "New", IsTemplate = true }
);
Or would it be a lot simpler for me to just split this into two action methods, and have those call a single private controller method with a hard-coded parameter value?
Seems like this might be the simplest option. I've split the single New action method into two action methods, both of which have different routes defined, and a third private method that does all the work. I'm still open to recommendations if there's a prettier way to do it.
[HttpGet]
//Action method for a new template
public ActionResult NewTemplate() {
return New(true, null);
}
[HttpGet]
//Action method for a new project
public ActionResult New(int? TemplateProjectID) {
return New(false, TemplateProjectID);
}
private ActionResult New(bool IsTemplate, int? TemplateProjectID) {
//Assorted code follows
Perhaps RouteUrl can help you?
If you named the route to TemplateNew for example, you should be able to create an url for that:
New Template

Handling extra MVC parameter with different ActionResult

I'm working on a website that has 4 individual sections, each with their own controller. These controllers share a few models, however the model will do things slightly differently based on the controller calling it.
The route that I'd like to follow is {controller}/{model}/{id}/{action} so I could use controller1/cars/5/edit rather than controller1/editcar/5.
Is there any way to have my controller understand a model+action combination and handle them with different ActionResults, or am I forced to use an ActionResult on the model name and then use a conditional to figure out what action to take?
In Ruby I could use get "/controller1/cars/:id/edit" do. I'm just looking for something similar in MVC4.
Example routs:
/controller1/cars
(returns a view containing all cars from controller1)
/controller1/cars/5
(returns a view containing car with the ID 5 from controller1)
/controller1/cars/5/edit
(returns a view to edit car with ID 5 from controller1)
/controller2/cars
(returns a view containing all cars from controller2)
/controller2/boats
(returns a view containing all boats from controller2)
I think this route meets your needs. It requires some clever logic in your action methods but should give you the tools to handle it. Read my description of behavior.
routes.MapRoute(
"Default", // Route name
"{controller}/{model}/{id}/{action}", // URL with parameters
new { controller = "Home", action = "View", id = UrlParameter.Optional } // Parameter defaults
);
This route will Default to an action called View (that will presumably be used for Display) and has an option to direct to a different Action.
Model and id will be passed as arguments to you action method. Unfortunately, Model will not be sent as a type but a string (you may feed that into a Factory class).
if if is left out (eg /controller2/boats) it will be passed to your action as a null. This requires logic to handle but gives you the tools to handle it.
Thanks for the responses. The reason that I was having so much trouble with this was because I couldn't figure out how to separate controllers with a rout properly. This resulted in my idea breaking away from MVC standards and almost trying to implement a controller of controllers.
When I finally stumbled upon "Areas", I quickly realized that this is what I needed. I was able to add an Area for each section (or area) of my website, and within each area, I could define individual controllers and views, while my models remained in a shared directory outside of all areas. This now works perfectly.

UrlHelper.Action doesn't map to the area that the controller is in correctly

UPDATE 2
Ok - So it looks like my question is changing again slightly :-)
I've realised now that UrlHelper.Action doesn't seem to correctly resolve the URL in any Area unless the area name is explicitly specified. If it's not specified it seems to return whatever area name we're currently in which makes it look like it's working from one part of the site but then the same link in another area resolves to the wrong Area name.
Either I've done something funky to make it do this or I'm not quite understanding how this Action method is meant to work.
UPDATE 1
I can make this work by doing the following:
return helper.Action("add", "product",new {area = "storemanagement"});
which changes my question slightly.
How come the MVC routing doesn't disambiguate the controllers with the same name and resolve to the one with the action method specified?
Origional post
Hey everyone,
I've created a helper method on the UrlHelper class and am having a small problem with one of the routes.
Here's the code for the helper method in question:
public static string AddProduct(this UrlHelper helper)
{
return helper.Action("add", "product");
}
I basically have two controllers named "product" which are in different areas of the site. One of them in used for browsing of products and the other for management of products. Only one of the product controllers contains an action method "Add".
When I output the value of AddProduct
<%: Url.AddProduct() %>
The area name is resolved to the current area I'm browsing and not the correct area for the product controller containing the Add action method.
Is there something I need to set up in the routes? I'm not exactly sure how the routing works with UrlHelper.Action so I dunno if it's possible to do what I'm trying.
Cheers for any help.
Just to put the answer in the answer section for clarity...
You'll need to add Area to the RouteValues anytime you use UrlHelper.Action to generate a link path.
If you're in the Controller, you can stub a UrlHelper like this:
var httpContext = new HttpContextWrapper(System.Web.HttpContext.Current);
var requestContext = new RequestContext(httpContext, new RouteData());
var urlHelper = new UrlHelper(requestContext);
You can always get the current area like this:
HttpContext.Current.Request.RequestContext.RouteData.DataTokens["area"]
Finally, when using UrlHelper pass the area in the RouteValues object like this:
var url = urlHelper.Action("Index", "Home", new { area = "Pages" }));
That is the default behavior of ASP.NET Routing.
When in an "Area" the action (and view) are searched for by controller and action name in default locations.
The view is presumed to be ActionName unless stated otherwise in the action e.g. return PartialView("_MyPartialView.cshtml", ViewModel)
The default locations are these: {Controller} = controller name, {Area} = area name
Controller:
"Areas/{Area}/{Controller}/"
"Controllers/{Controller}"
Views:
"Areas/{Area}/Views/{Controller}/"
"Areas/{Area}/Views/Shared/"
"Views/Shared"
If you don't give the Area in the route values it will never search outside these locations. The Area is the same as the location where you are calling your helper. If you are in the root level (not in an area) it will be limited to
"Controllers/{Controller}" // controller
"Views/{Controller}/" // views
"Views/Shared" // views
The problem was that when you where in one Area1 it searched for
"Areas/Area1/{Controller}/" and when you were in Area2 it searched "Areas/Area2/{Controller}/" (And both searched the Controllers/Product and Controllers/Shared). It was able to find it when you where in the right area because it fit the default search location, but not while in the other area because the controller was only in the one physical area.
Adding the Area as you did, tells it to search in a predefined Area so it will go directly there.
"Areas/Storemanagement/Views/product/" and search for the view defined in the Add Action.
There is little point in having an empty helper method returning a Url.Action method, but perhaps that was just for demonstration.
(I just noticed the question is quite old but I guess this can be here for future reference)

Categories

Resources