overload action for number of parameters in asp.net mvc - asp.net-mvc

Is it possible to overload the action methods based on number of parameters in request?
Eg:
1.
domain.com/List/Filter/ByName
invokes -> public ActionResult Filter(string criteria1)
2.
domain.com/List/Filter/ByName/ByRanking
invokes -> public ActionResult Filter(string criteria1, string criteria2)
I'm using asp.net mvc2.

Action methods cannot be overloaded based on parameters because there would be no reasonable way to disambiguate a URL into multiple overloaded methods.
What you can do, though is either this:
public ActionResult Filter(string criteria1, string criteria2)
and then check whether criteria2 is null to filter only by name.
Alternatively, you can use ActionNameAttribute to decorate your action methods
[ActionName("FilterByName")]
public ActionResult Filter(string criteria1)
[ActionName("FilterByNameAndRanking")]
public ActionResult Filter(string criteria1, string criteria2)
and then use that name in route registration. This approach, however, can lead to much confusion.

If I'm not mistaken the best way to do this would be to add two different controller methods and map them to two different Urls.
public ActionResult Filter1(string criteria1);
public ActionResult Filter2(string criteria1, criteria2);
Then you have two route definitions:
This will map this URL List/Filter/xxCriteria/ to the first controller
routes.MapRoute(
"Filter", // Route name
"{controller}/Filter/{criteria1}", // URL with parameters
new { controller = "List", action = "Filter1", criteria="" } // Parameter defaults
);
This will map this URL List/Filter/xxCriteriaName/xxxCriteriaRank to the second controller. Without this route you could still map a url to the second method, but it would look like : List/Filter/?criteria1=xx&criteria2=xx
routes.MapRoute(
"Filter2", // Route name
"{controller}/Filter/{criteria1}/{criteria2}", // URL with parameters
new { controller = "List", action = "Filter2", criteria1 = "", criteria2 = "" } // Parameter defaults
);
Hope it helped.

Related

ASP.NET MVC Clean way to inject partial view from action

I have an app with many widgets and their content depends on the user requesting specific route. Simply put: if widget action is requested, its content must be rendered, otherwise it's empty. Consider routes/actions like this:
~/MyApp/Index -> without model; app HTML, without any widgets
~/MyApp/Foo/{id} -> uses FooModel; if ModelState is valid, returns
Index HTML with injected partial view of Foo's widget to div#foo;
otherwise redirects to Index.
~/MyApp/Bar/{id} -> same as Foo, but different model and widget
My foo action :
public ActionResult Foo(string id) {
if (ModelState.IsValid) {
var response = FooService.GetData(id);
// Inject Foo widget to Index
}
return RedirectToAction("Index");
}
I know that it is possible to use ViewBag or other means to send variables and using the condition to decide whether to render partial view or not. But... there should be a better way to do this, right?
I use MVC's Html.RenderActionResult when I want to build shared views with non-trivial binding logic (calling the database, composing complex objects, etc). The binding logic for each widget is contained in a PartialViewResult method, which is called from the *.cshtml file using Html.RenderAction().
ContentController:
public ActionResult Index(int id)
{
var indexViewModel = new IndexViewModel
{
Id = id,
Title = "My Title",
SubHeader = "Wow its 2016"
};
return View(indexViewModel);
}
public PartialViewResult PopularContent(int id)
{
var popularContentViewModel = new List<PopularContentViewModel>();
// query by id to get popular content items
return PartialView("_PopularContent", popularContentViewModel);
}
public PartialViewResult Widget2(int id)
{
return PartialView("_Widget2Partial");
}
Index.cshtml:
#model StackOverflow.RenderAction.ViewModels.IndexViewModel
<h1>#Model.Title</h1>
<h2>#Model.SubHeader</h2>
--RenderAction will call out to the specified route.
--Note the use of the Id parameter from the viewmodel.
#{Html.RenderAction("PopularContent", "Content", new {Model.Id});}
ASP.NET MVC Attribute Routing could a be a nice solution for this:
In your controller:
public class WidgetController : Controller
{
[Route("myapp/foowidget", Name = "FooWidget")]
public ActionResult FooWidget()
{
//create any model and return any view or partial or redirect
}
[Route("myapp/boowidget/{id:int}", Name = "BooWidget")]
public ActionResult BooWidget(int id)
{
//create any model and return any view or partial or redirect
}
}
And then in a View, you can call the Route by name:
#Url.RouteUrl("FooWidget")
or
#Url.RouteUrl("BooWidget")
or
#Html.RenderPartial("FooWidget")
#Url.RouteUrl("BooWidget") will render or concatenate the id that is in current url, if url is /myapp/something/id, because of your Route attribute definition: "myapp/boowidget/{id:int}". In fact #Url.RouteUrl("BooWidget") might extract the id from any current url of the format /controllerName/action/id, though you will have to test for sure.
And notice how you can have a separation of concerns with your WidgetController and your url Routes are not dependent on that controller's name in any way. That is a nice feature of Attribute Routing, you can declare custom routes as well as organize your controllers and break from nameing convention dependency of a controllerName being part of the url controllerName/action a user sees in their browser.
In regards to Html.RenderPartial, I am not sure if RenderPartial "connects" or will be able to route to your RouteName like "FooWidget". If it does great.
If not your solution is this:
public class WidgetController : Controller
{
public ActionResult FooWidget()
{
//model, you choose, return a partial
}
public ActionResult RedirectUser()
{
//do a redirect
}
public ActionResult BooWidget()
{
//any model, any partial
}
public ActionResult BooWidget(int id)
{
//any model, any partial
}
}
Each method in your controller is single purpose, has a distinct signature and does one thing, no conditions to pass in and no decisions required.

Attribute routing with string parameters is ambiguous

I am getting the error
the current request is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Foo1(System.String) on type
Project.Web.Controllers.PageController
System.Web.Mvc.ActionResult Foo2(System.String) on type
Project.Web.Controllers.PageController
The a href calling the ActionResults are
Foo1
Foo2
I am basically passing the string instead of a int id ( I realise this is not the best way to do this , but that is not the issue i want to address here )
This allows me to add a string parameter to the Routing for clean urls, so my ActionResult is now
[AllowAnonymous, Route("{page}")]
public ActionResult Foo1(string page)
{
...
}
and
[AllowAnonymous, Route("{page}")]
public ActionResult Foo2(string page)
{
...
}
Why is there ambiguity when the links are being passed to different ActionResults and the parameters are different?
You seem to be misunderstanding what the [Route] attribute does. Your example is definitely ambiguous.
This:
[AllowAnonymous, Route("{page}")]
means "map this action to any path that has one and only one segment, and treat that segment as the page parameter.
So all of these URLs:
http://example.com/Foo1
http://example.com/Foo2
http://example.com/SomethingElse
would be mapped to the action that has that attribute.
If you have two actions with that same route, then the MVC framework doesn't know which action to map the URL to. Both of them are valid candidates.
I think there is a simpler way to implement what you are trying to do.
In RouteConfig.cs, add a route map that doesn't include the controller name in the URL pattern:
routes.MapRoute(
name: "Pages",
url: "{action}",
defaults: new { controller = "Page", action = "Index", id = UrlParameter.Optional }
);
Add an action parameter to your actions:
[AllowAnonymous]
public ActionResult Foo1(string action)
{
// action has the value Foo1
...
}
and
[AllowAnonymous]
public ActionResult Foo2(string action)
{
// action has the value Foo2
...
}
Remove the page stuff from your links:
Foo1
Foo2
And you should be all set.
Edit: If you just want to allow passing in a dynamic value for page, then you can get rid of the Foo1 and Foo2 actions, and just have one action:
[Route("{page=index}")]
public ActionResult Page(string page)
{
// page is the value specified in the url
}
For links within the site, just use ordinary <a> elements:
To Foo1
To Foo2
and I think that should do it.

overload views in MVC?

I want to have links http://localhost:2409/Account/Confirmation/16 and that link http://localhost:2409/Account/Confirmation/ (without parametr). But with this action methods, it isn't working. Why?
public ActionResult Confirmation(int id, string hash)
{
Some code..
return View();
}
second, I just want to return View, if parametr is empty.
public ActionResult Confirmation()
{
return View();
}
Error (translated):
The current request for action on a controller Confirmation
AccountController is ambiguous between the following methods of
action: System.Web.Mvc.ActionResult Confirmation (Int32,
System.String) for type TC.Controllers.AccountController
System.Web.Mvc.ActionResult Confirmation () for type
TC.Controllers.AccountController
You cannot have multiple actions with the same name using the same HTTP verb (in your case GET.) You can name your actions differently but this means the link will change or you can use different VERB but this can also leads to other problems like you cannot just enter the link in your browser.
What you should do is to change your id to be optional with int? and merge your two actions into one:
public ActionResult Confirmation(int? id, string hash)
{
if(id.HasValue)
{
//Some code.. using id.Value
return View();
}
//There was no Id given
return View();
}
You may also need to allow in your route that the id is optional. If you are using the default routes this should be the default setting:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
There is no need to make 2-methods for it. Your HTTP request get confused that which ActionMethod should be called on both cases;
http://localhost:2409/Account/Confirmation/16
http://localhost:2409/Account/Confirmation/
Instead of all this, just create a single method. Make its parameter optional or assign some default value to the parameters. Here are 2-examples to understand it.
// 1. Default value to paramter
public ActionResult Confirmation(int id = 0, string hash = null)
{
//Some code..
return View();
}
// 2. Make id optional
public ActionResult Confirmation(int? id, string hash)
{
//Some code..
return View();
}
You can adopt any one approach from them.

MVC3 RESTful API Routing & Http Verb Handling

I want to build a RESTful Json Api for my MVC3 application. I need help with handling multiple Http Verbs for the manipulation of a single object instance.
What I've read/studied/tried
MVC attributes (HttpGet, HttpPost, etc.) allow me to have a controller with multiple actions sharing the same name, but they still must have different method signatures.
Route constraints happen in the routing module before MVC kicks in and would result in me having 4 explicit routes, and still require individually named controller actions.
ASP.NET MVC AcceptVerbs and registering routes
Building a custom Http Verb Attribute could be used to snatch the verb used to access the action and then pass it as an argument as the action is invoked - the code would then handle switch cases. The issue with this approach is some methods will require authorization which should be handled at the action filter level, not inside the action itself.
http://iwantmymvc.com/rest-service-mvc3
Requirements / Goals
One route signature for a single instance object, MVC is expected to handle the four main Http Verbs: GET, POST, PUT, DELETE.
context.MapRoute("Api-SingleItem", "items/{id}",
new { controller = "Items", action = "Index", id = UrlParameter.Optional }
);
When the URI is not passed an Id parameter, an action must handle POST and PUT.
public JsonResult Index(Item item) { return new JsonResult(); }
When an Id parameter is passed to the URI, a single action should handle GET and DELETE.
public JsonResult Index(int id) { return new JsonResult(); }
Question
How can I have more than one action (sharing the same name and method signature) each respond to a unique http verb. Desired example:
[HttpGet]
public JsonResult Index(int id) { /* _repo.GetItem(id); */}
[HttpDelete]
public JsonResult Index(int id) { /* _repo.DeleteItem(id); */ }
[HttpPost]
public JsonResult Index(Item item) { /* _repo.addItem(id); */}
[HttpPut]
public JsonResult Index(Item item) { /* _repo.updateItem(id); */ }
For RESTful calls, the action has no meaning, since you want to differ only by HTTP methods. So the trick is to use a static action name, so that the different methods on the controller are only different in the HTTP method they accept.
While the MVC framework provides a solution for specifying action names, it can be made more concise and self-explaining. We solved it like this:
A special attribute is used for specifying RESTful methods (this matches to a special action name):
public sealed class RestfulActionAttribute: ActionNameSelectorAttribute {
internal const string RestfulActionName = "<<REST>>";
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) {
return actionName == RestfulActionName;
}
}
The controllers use it in combination with the HTTP method attributes:
public class MyServiceController: Controller {
[HttpPost]
[RestfulAction]
public ActionResult Create(MyEntity entity) {
return Json(...);
}
[HttpDelete]
[RestfulAction]
public ActionResult Delete(Guid id) {
return Json(...);
}
[HttpGet]
[RestfulAction]
public ActionResult List() {
return Json(...);
}
[HttpPut]
[RestfulAction]
public ActionResult Update(MyEntity entity) {
return Json(...);
}
}
And in order to bind those controllers successfully, we use custom routes with the static action name from the beforementionned attribute (which at the same time also allow for customizing the URLs):
routes.MapRoute(controllerName, pathPrefix+controllerName+"/{id}", new {
controller = controllerName,
action = RestfulActionAttribute.RestfulActionName,
id = UrlParameter.Optional
});
Note that all your requirements can be easily met with this approach as far as I can tell; you can have multiple [HttpXxx] attributes on one method to make one method accept multiple HTTP methods. Paired with some smart(er) ModelBinder this is very powerful.

QueryString id parameter not being used

I've got a very basic ASP.Net MVC project where I'd like to use a parameter name of id on one of my controller actions. From everything I've read that shouldn't be a problem but for some reason using a parameter name of id fails to get the value extracted from the query string but if I change it to any other different name it will work.
I only have a single route in my global.asx
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
My controller method is:
public ActionResult Confirm(string id)
{
....
}
A URL of http://mysite/customer/confirm/abcd works. A URL of http://mysite/customer/confirm?id=abcd fails.
If I change the controller method to:
public ActionResult Confirm(string customerID)
{
....
}
then a URL of http://mysite/customer/confirm?customerID=abcd works.
Is there something special about using "id" as a parameter in an ASP.Net MVC query string?
Update: Changed id from 1234 to abcd, my id's are actually strings.
If you do not apply an id parameter (either querystring or POST), the system just ignores it, and you can remove the "id" parameter in your controller:
public ActionResult Confirm()
In your case, you would just stick with the id parameter. Why make an ugly customerID parameter, when id is "mapped" automatically?
This is an easy and simple example of the use of id parameter.
public ActionResult Confirm(int? id)
{
if (id.HasValue && id.Value > 0) // check the id is actually a valid int
_customerServer.GetById(id.Value);
// do something with the customer
return View();
}
This works too, for me. We're doing it in our application right now with a standard route:
public ActionResult Confirm(string id)
{
if (!string.IsNullOrEmpty(id)) // check the id is actually a valid string
_customerServer.GetByStringId(id);
// do something with the customer
return View();
}
If you need to have id in query string, then don't create route with 'id' parameter.
In case you have route "{controller}/{action}" then you can use public ActionResult Confirm(string id) as your controller method.
Routes don't care about query strings.

Resources