I've made a mvc4 project in Visual Studio Express 2012 for web. And there I've made a search function. And a view to show the result.
So normally I would have added this to the _Layout.cshtml.
if (Request["btn"] == "Search")
{
searchValue = Request["searchField"];
if (searchValue.Length > 0)
{
Response.Redirect("~/Views/Search/Result.cshtml?searchCriteria=" + searchValue);
}
}
And that doesn't work. What whould be the alternative to Response.Redirect in mvc4, which still allows me to keep the searchCriteria to be read with Request.Querystring at the Result.cshtml page.
You should be definetly doing this in your controller, making it return an ActionResult and returning a RedirectResult, i.e.:
public ActionResult Search(string searchCriteria) {
return Redirect("~/Views/Search/Result.cshtml?searchCriteria="+searchCriteria);
}
Btw, I'd also say don't use neither of the Request stuff (or even Redirects), but actions with parameters that MVC will bind automagically from POST or GET parameters. E.g, "www.something.com/search?searchCriteria=hello" will automatically bind the searchCriteria parameter to the Action handling /search. Or, "www.something.com/search/hello" will bind to the parameter defined into your Routing config.
A simple example would be something like this:
Index.cshtml:
#using (Html.BeginForm("Results", "Search", FormMethod.Get))
{
#Html.TextBox("searchCriteria")
<input type="submit" value='Search' />
}
Then the controller:
public class SearchController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Results(string searchCriteria)
{
var model = // ... filter using searchCriteria
return View(model);
}
}
model could be of type ResultsViewModel, which would encase everything you need to display the results. This way, your search is setup in a RESTful way - meaning it behaves consistently each time.
I'm working on providing friendly names for my MVC 4 controllers and I want to do something like the [ActionName="My-Friendly-Name"] style, but for the whole controller.
I couldn't find any information about such an attribute, so how would I go about doing that? Also, will I need to add a new MapRoute to handle it?
EDIT:
For example, I'd like to route the following url:
http://mysite.com/my-reports/Details/5
be routed to the following controller:
[ControllerClass="my-reports"] // This attribute is made up. I'd like to know how to make this functionality
public class ReportsController : Controller
{
//
// GET: /Reports/
public ActionResult Index()
{
return View();
}
public ViewResult Details(int id)
{
Report report = db.Reports.Single(g => g.Id == id);
return View(report);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Report item)
{
try
{
if (ModelState.IsValid)
{
item.Id = Guid.NewGuid();
_context.Reports.AddObject(item);
_context.SaveChanges();
return RedirectToAction("Index");
}
return View(item);
}
catch (Exception)
{
return View(item);
}
}
}
Add a custom route that will match your specific name:
routes.MapRoute(
"MyCustomRoute",
"My-Friendly-Name/{action}/{id}",
new { controller = "ReportsController", action = "Index", id = "" }
);
Now every url that contains "My-Friendly-Name" for the controller will use your new route.
Checkout this post, particularly where it talks about Route formatting.
using Microsoft.AspNetCore.Mvc;
[ControllerName("SomeOtherName")] // This attribute is not made up.
public class ReportsController : Controller
{
...
I don't know if there's answer to your question, but even if there were one, why in the world would you do such a thing? Why not do standard way : call the controller class anything you like (as long as it makes sense) and have it called by the framework for you? Why create two artificial naming conventions and map between them? I see not a single gain, but multiple cons. Such as - its harder to read, harder to maintain, harder to understand, it is also insignificant, but still, a strain on performance ...
Update
Please look into this posting, I think they did solve the problem that you're talking about.
How to achieve a dynamic controller and action method in ASP.NET MVC?
Please let me know if it was of any help to you.
I have an "Add to Cart" button that if the browser supports JS + Ajax (and doesn't have it turned off) it POSTS using Ajax back to the site, however if they don't support it, or have it turned off it does the manual style POST.
What I am hoping to accomplish is two views - one when the user posts back using a regular POST and one when it comes from a AJAX POST. That way I can show an in-line message (partial) or a full screen.
I would prefer not having to duplicate the controller/action code twice, it just seems non-elegant.
Is there any recommended solutions or patterns for this type of issue?
John,
You can use the IsAjaxRequest method on the request to determine this. You would apply it to your scenario thusly:
public ActionResult AddToCart(YourCartViewmodel cartViewmodel)
{
if (ModelState.IsValid)
{
// do the standard/common db stuff here
if(Request.IsAjaxRequest())
{
return PartialView("myPartialView");
}
else
{
return View("standardView");
}
}
/* always return full 'standard' postback if model error */
return View(cartViewmodel);
}
altho not perhaps giving a complete solution, this should give you a good start...
You can have two different actions in your controller. One for regular post and one for AJAX.
public ActionResult AddToCart(Viewmodel vm)
{
if (ModelState.IsValid)
{
DoStuff(vm);
return View("ViewForRegularPost");
}
/* error */
return View(vm);
}
and
public ActionResult JsonAddToCart(Viewmodel vm)
{
if (ModelState.IsValid)
{
DoStuff(vm);
return View("ViewForJS");
}
/* error */
return View(vm);
}
Instead of repeating your controller code, have a separate method for actual controller code.
public void DoStuff(Viewmodel vm)
{
//TODO : Actual controller code goes here
}
At the moment I have 3 controllers: Home, Summary and Detail
However, each has only one action: Index, Display and Display respectively.
This smell bad to me.
I was hoping to use the MapRoute to allow:
myapp/Home
myapp/Summary/prop1/prop2
myapp/Detail/prop1/prop2/prop3
instead of
myapp/Home
myapp/Summary/Display/prop1/prop2
myapp/Detail/Display/prop1/prop2/prop3
and thereby miss out the "Display" part...but again, this doesn't smell right. Although it works, it means manually adding links instead of using Html.ActionLink(...)
Would it be better to have Home/Index, Home/Summary and Home/Detail all in one controller?
I was hoping to provide a simple URL structure so users who know what they are doing could simply type it in as above...the "Home" part seems wasted?
I agree with #Tim that you should use a single controller. A controller is a logical grouping of actions; for example the CRUD operations for Foo. NerdDinner is a good example of this.
I disagree with the routes. You can do whatever you want with the routing; but it should be meaningful to the user. You probably just have a single catchall route similar to the one below.
routes.MapRoute("Default", //RouteName
"{controller}/{action}/{id}", //RouteUrl
new { //RouteDefaults
controller = "Home",
action = "Index",
id = UrlParameter.Optional}
)
You can have the routes you want by using a single controller.
Your desired urls:
myapp/Home
myapp/Summary/prop1/prop2
myapp/Detail/prop1/prop2/prop3
The controller setup:
public class HomeController : Controller
{
public ActionResult Index() { ... }
public ActionResult Summary() { ... }
public ActionResult Details() { ... }
}
The routing setup:
routes.MapRoute("Home-Index", //RouteName
"myapp/Home", //RouteUrl
new { //RouteDefaults
controller = "Home",
action = "Index"});
routes.MapRoute("Home-Summary", //RouteName
"myapp/Summary/prop1/prop2", //RouteUrl
new { //RouteDefaults
controller = "Home",
action = "Summary",
prop1 = UrlParameter.Optional,
prop2 = UrlParameter.Optional});
routes.MapRoute("Default", //RouteName
"{controller}/{action}/{id}", //RouteUrl
new { //RouteDefaults
controller = "Home",
action = "Index",
id = UrlParameter.Optional}
)
Now there are a few important things to note:
Routing works like a switch statement, the first url that matches is
the one it will use, that's why you have the catchall as the last
one.
The url defined in your map route can be whatever you want. It
doesn't have to match with the controller, because once you remove
the placeholders ({controller}, etc) it uses the default for
navigation. So the Home-Index route url could be myapp/foo/bar/baz
and id would still take you to the Home index action.
The placeholders work automagically. I have not been able to find a good resource explaining how the default route works.
Hope this helps.
Not sure if I totally get your question, but what about creating a base controller class that inherits from Controller, and have your shared actions there instead? That way you don't need to repeat yourself as much.
You need only one controller and inside it multiple actions..
public class HomeController : Controller
{
public ActionResult Index()
{
}
public ActionResult Summary()
{
}
public ActionResult Details()
{
}
}
In the action link
#Html.ActionLink("Details", "Details","Home");
that's enough, no need to add multiple controller..
Hope these helps..
You can pretty much go down any route you want when it comes to this, all just depends on what you want to achieve.
You can stick all the actions in the Home controller so your routes would be
myapp/Home
myapp/Home/Summary/prop1/prop2
myapp/Home/Details/prop1/prop2/prop3
In this option you have 1 controller, 3 actions, with 2 additional routes to handle the URls
It depends on what the summary and details are of though? Like if it is the summary of an order, i would prefer
myapp/Orders/Summary/prop1/prop2
myapp/Orders/Details/prop1/prop2/prop3
In this you would have your Home controller and the Index action, then an Orders controller with two actions. I would say that Summary and Details would generally suggest that you are displaying something anyway, so you would not need the "Display" part as you have in your suggestions.
If you want your URLS to be
myapp/Home
myapp/Summary/prop1/prop2
myapp/Detail/prop1/prop2/prop3
Then you make 3 controllers
HomeController
SummaryController
DetailController
Each of these will have 1 Action
public ActionResult Index() {
}
For the SUmmary and Detail controller you will just pass in some extra paramaters to the Index action
I'm curious to see if you can overload controller methods in ASP.NET MVC. Whenever I try, I get the error below. The two methods accept different arguments. Is this something that cannot be done?
The current request for action 'MyMethod' on controller type 'MyController' is ambiguous between the following action methods:
You can use the attribute if you want your code to do overloading.
[ActionName("MyOverloadedName")]
But, you'll have to use a different action name for the same http method (as others have said). So it's just semantics at that point. Would you rather have the name in your code or your attribute?
Phil has an article related to this: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx
Yes. I've been able to do this by setting the HttpGet/HttpPost (or equivalent AcceptVerbs attribute) for each controller method to something distinct, i.e., HttpGet or HttpPost, but not both. That way it can tell based on the type of request which method to use.
[HttpGet]
public ActionResult Show()
{
...
}
[HttpPost]
public ActionResult Show( string userName )
{
...
}
One suggestion I have is that, for a case like this, would be to have a private implementation that both of your public Action methods rely on to avoid duplicating code.
Here's something else you could do... you want a method that is able to have a parameter and not.
Why not try this...
public ActionResult Show( string username = null )
{
...
}
This has worked for me... and in this one method, you can actually test to see if you have the incoming parameter.
Updated to remove the invalid nullable syntax on string and use a default parameter value.
No,No and No. Go and try the controller code below where we have the "LoadCustomer" overloaded.
public class CustomerController : Controller
{
//
// GET: /Customer/
public ActionResult LoadCustomer()
{
return Content("LoadCustomer");
}
public ActionResult LoadCustomer(string str)
{
return Content("LoadCustomer with a string");
}
}
If you try to invoke the "LoadCustomer" action you will get error as shown in the below figure.
Polymorphism is a part of C# programming while HTTP is a protocol. HTTP does not understand polymorphism. HTTP works on the concept's or URL and URL can only have unique name's. So HTTP does not implement polymorphism.
In order to fix the same we need to use "ActionName" attribute.
public class CustomerController : Controller
{
//
// GET: /Customer/
public ActionResult LoadCustomer()
{
return Content("LoadCustomer");
}
[ActionName("LoadCustomerbyName")]
public ActionResult LoadCustomer(string str)
{
return Content("LoadCustomer with a string");
}
}
So now if you make a call to URL "Customer/LoadCustomer" the "LoadCustomer" action will be invoked and with URL structure "Customer/LoadCustomerByName" the "LoadCustomer(string str)" will be invoked.
The above answer i have taken from this codeproject article --> MVC Action overloading
To overcome this problem you can write an ActionMethodSelectorAttribute that examines the MethodInfo for each action and compares it to the posted Form values and then rejects any method for which the form values don't match (excluding the button name, of course).
Here's an example:- http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/
BUT, this isn't a good idea.
As far as I know you can only have the same method when using different http methods.
i.e.
[AcceptVerbs("GET")]
public ActionResult MyAction()
{
}
[AcceptVerbs("POST")]
public ActionResult MyAction(FormResult fm)
{
}
I have achieved this with the help of Attribute Routing in MVC5. Admittedly I am new to MVC coming from a decade of web development using WebForms, but the following has worked for me. Unlike the accepted answer this allows all the overloaded actions to be rendered by the same view file.
First enable Attribute Routing in App_Start/RouteConfig.cs.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Optionally decorate your controller class with a default route prefix.
[RoutePrefix("Returns")]
public class ReturnsController : BaseController
{
//.......
Then decorate your controller actions that overload each other with a common route and parameters to suit. Using type constrained parameters you can use the same URI format with IDs of different types.
[HttpGet]
// Returns
public ActionResult Index()
{
//.....
}
[HttpGet]
[Route("View")]
// Returns/View
public ActionResult View()
{
// I wouldn't really do this but it proves the concept.
int id = 7026;
return View(id);
}
[HttpGet]
[Route("View/{id:int}")]
// Returns/View/7003
public ActionResult View(int id)
{
//.....
}
[HttpGet]
[Route("View/{id:Guid}")]
// Returns/View/99300046-0ba4-47db-81bf-ba6e3ac3cf01
public ActionResult View(Guid id)
{
//.....
}
Hope this helps and is not leading somebody down the wrong path. :-)
You could use a single ActionResult to deal with both Post and Get:
public ActionResult Example() {
if (Request.HttpMethod.ToUpperInvariant() == "GET") {
// GET
}
else if (Request.HttpMethod.ToUpperInvariant() == "POST") {
// Post
}
}
Useful if your Get and Post methods have matching signatures.
I've just come across this question and, even though it's quite old now, it's still very relevant. Ironically, the one correct comment in this thread was posted by a self-confessed beginner in MVC when he wrote the post. Even the ASP.NET docs are not entirely correct. I have a large project and I successfully overload action methods.
If one understands routing, beyond the simple {controller}/{action}/{id} default route pattern, it might be obvious that controller actions can be mapped using any unique pattern. Someone here talked about polymorphism and said: "HTTP does not understand polymorphism", but routing has nothing to do with HTTP. It is, simply put, a mechanism for string pattern matching.
The best way to make this work is to use the routing attributes, for example:
[RoutePrefix("cars/{country:length(3)}")]
public class CarHireController
{
[Route("{location}/{page:int=1}", Name = "CarHireLocation")]
public ActionResult Index(string country, string location, int page)
{
return Index(country, location, null, page);
}
[Route("{location}/{subLocation}/{page:int=1}", Name = "CarHireSubLocation")]
public ActionResult Index(string country, string location, string subLocation, int page)
{
//The main work goes here
}
}
These actions will take care of urls like /cars/usa/new-york and /cars/usa/texas/dallas, which will map to the first and second Index actions respectively.
Examining this example controller it's evident that it goes beyond the default route pattern mentioned above. The default works well if your url structure exactly matches your code naming conventions, but this is not always the case. Code should be descriptive of the domain, but urls often need to go further because their content should be based on other criteria, such as SEO requirements.
The benefit of the default routing pattern is that it automatically creates unique routes. This is enforced by the compiler since urls will match unique controller types and members. Rolling your own route patterns will require careful thought to ensure uniqueness and that they work.
Important note The one drawback is that using routing to generate urls for overloaded actions does not work when based on an action name, e.g., when using UrlHelper.Action. But it does work if one uses named routes, e.g., UrlHelper.RouteUrl. And using named routes is, according to well respected sources, the way to go anyhow (http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/).
Good luck!
You can use [ActionName("NewActionName")] to use the same method with a different name:
public class HomeController : Controller
{
public ActionResult GetEmpName()
{
return Content("This is the test Message");
}
[ActionName("GetEmpWithCode")]
public ActionResult GetEmpName(string EmpCode)
{
return Content("This is the test Messagewith Overloaded");
}
}
I needed an overload for:
public ActionResult Index(string i);
public ActionResult Index(int groupId, int itemId);
There were few enough arguments where I ended up doing this:
public ActionResult Index(string i, int? groupId, int? itemId)
{
if (!string.IsNullOrWhitespace(i))
{
// parse i for the id
}
else if (groupId.HasValue && itemId.HasValue)
{
// use groupId and itemId for the id
}
}
It's not a perfect solution, especially if you have a lot of arguments, but it works well for me.
I have faced same issue in my application too. Without Modifiyig any Method information, I have provided [ActionName("SomeMeaningfulName")] on Action head. issue resolved
[ActionName("_EmployeeDetailsByModel")]
public PartialViewResult _EmployeeDetails(Employee model)
{
// Some Operation
return PartialView(model);
}
}
[ActionName("_EmployeeDetailsByModelWithPagination")]
public PartialViewResult _EmployeeDetails(Employee model,int Page,int PageSize)
{
// Some Operation
return PartialView(model);
}
Create the base method as virtual
public virtual ActionResult Index()
Create the overridden method as override
public override ActionResult Index()
Edit: This obviously applies only if the override method is in a derived class which appears not to have been the OP's intention.
I like this answer posted in another thread
This is mainly used if you inherit from another controller and want to override an acction from the base controller
ASP.NET MVC - Overriding an action with differing parameters
There is only one public signature allowed for each controller method. If you try to overload it, it will compile, but you're getting the run-time error you've experienced.
If you're not willing to use different verbs (like the [HttpGet] and [HttpPost] attributes) to differentiate overloaded methods (which will work), or change the routing, then what remains is that you can either provide another method with a different name, or you can dispatch inside of the existing method. Here's how I did it:
I once came into a situation where I had to maintain backwards compatibility. The original method expected two parameters, but the new one had only one. Overloading the way I expected did not work because MVC didn't find the entry point any more.
To solve that, I did the following:
Changed the 2 overloaded action methods from public to private
Created one new public method which contained "just" 2 string parameters. That one acted as a dispatcher, i.e.:
public ActionResult DoSomething(string param1, string param2)
{
if (string.IsNullOrEmpty(param2))
{
return DoSomething(ProductName: param1);
}
else
{
int oldId = int.Parse(param1);
return DoSomething(OldParam: param1, OldId: oldId);
}
}
private ActionResult DoSomething(string OldParam, int OldId)
{
// some code here
return Json(result);
}
private ActionResult DoSomething(string ProductName)
{
// some code here
return Json(result);
}
Of course, this is a hack and should be refactored later. But for the time being, it worked for me.
You can also create a dispatcher like:
public ActionResult DoSomething(string action, string param1, string param2)
{
switch (action)
{
case "update":
return UpdateAction(param1, param2);
case "remove":
return DeleteAction(param1);
}
}
You can see, that UpdateAction needs 2 parameters, while DeleteAction just needs one.
Sorry for the delay. I was with the same problem and I found a link with good answers, could that will help new guys
All credits for BinaryIntellect web site and the authors
Basically, there are four situations: using differents verbs, using routing, overload marking with [NoAction] attribute and change the action attribute name with [ActionName]
So, depends that's your requiriments and your situation.
Howsoever, follow the link:
Link:
http://www.binaryintellect.net/articles/8f9d9a8f-7abf-4df6-be8a-9895882ab562.aspx
This answer for those who struggling with the same issue. You can
implement your own custom filter based on
ActionMethodSelectorAttribute. Here I found the best solution
for solving your question. Works fine on .net 5 project.
If you try to implement the same logic as was in web api controllers then use Microsoft.AspNetCore.Mvc.WebApiCompatShim. This nuget package provides compatibility in ASP.NET Core MVC with ASP.NET Web API 2 to simplify migration of existing Web API implementations. Please check this answer but consider that
starting with ASP.NET Core 3.0, the Microsoft.AspNetCore.Mvc.WebApiCompatShim package is no longer available.
If this is an attempt to use one GET action for several views that POST to several actions with different models, then try add a GET action for each POST action that redirects to the first GET to prevent 404 on refresh.
Long shot but common scenario.