I have two actions, one that accepts a ViewModel and one that accepts two parameters a string and an int, when I try to post to the action, it gives me an error telling me that the current request is ambiguous between the two actions.
Is it possible to indicate to the routing system which action is the relevant one, and if it is how is it done?
You can decorate it with HttpGet HttpPost
Look under "Overriding the HTTP Method Verb"
http://www.asp.net/learn/whitepapers/what-is-new-in-aspnet-mvc
You can also use the ActionName attribute. Look under "ActionNameAttribute"
http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx
You can't overload controller actions, though as Raj said, you can differentiate them by allowing them to respond to different requests (get, post, etc).
You might also find this helpful: How a Method Becomes An Action.
This is how it's done
A simplified example:
[HttpGet] // this attribute is't necessary when there are only 2 actions with the same name
public ActionResult Update(int id)
{
return View(new Repository().GetProduct(id));
}
[HttpPost]
public ActionResult Update(int id, Product product)
{
// handle POST data
var repo = new Repository();
repo.UpdateProduct(product);
return RedirectToAction("List");
}
And if you'd need two actions that would have completely same signature (same name and exactly the same number of parameters of the same type) in that case you would have to use another attribute like this:
public ActionResult SomeAction(int id)
{
return View(new Repository().GetSomething(id));
}
[HttpPost]
[ActionName("SomeAction")]
public ActionResult SomeActionPost(int id)
{
// handle POST data
var repo = new Repository();
repo.UpdateTimestamp(id);
return View(repo.GetSomething(id));
}
Related
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.
Maybe the answer to the following is simple, but I have hard time finding the answer:
When I have a GET method in a controller that is secured with the [Authorize] attribute, and a POST method (defined with [HttpPost]), will the same restrictions apply to it as well? Both methods have the same name, but differ in parameters.
Example code:
[Authorize(Roles = "Administrator")]
public ActionResult Delete()
{
return View();
}
[HttpPost]
public ActionResult Delete(int id)
{
/* the method's logic omitted */
return RedirectToAction("Index");
}
No, the controller considers these two separate actions (since that's what they are), and as such, don't share restrictions.
PUT and POST do a very similar thing in REST. The assumption being a POST means create a new entry and PUT means update an existing entry.
I had always assumed you could only have a single routing attribute on a controller action method, but now I have a situation where I want a method to respond to either HttpPost or HttpPut.
Update:
Tried a few variations and the actions were not hit if more than one routing attribute was applied. Like these:
[HttpPost]
[HttpPut]
public ActionResult Include(int id, int order, int parent)
{
return "...some result";
}
[HttpPost, HttpPut]
public ActionResult Include(int id, int order, int parent)
{
return "...some result";
}
The question now is: How do you respond to both PUT and POST requests in the same controller action?
There's builtin way to do that. Use AcceptVerbsAttribute
[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Put)]
public ActionResult Include()
{
}
in Asp.Net MVC if I decorate an action method with attribute NonAction then it wont be allowed to be called by the user visiting the site.
same happens when I make it private
So whats the difference between the two and is there a special purpose for which NonAction attribute has been made?
For example whats the difference between
[NonAction]
public ActionResult SomeAction(){}
And
private ActionResult SomeAction(){}
in the context of asp.net MVC of course I know one is public and the other one is private
That's the only difference. The attribute is used when you want a method that has a signature that would make it an action, but that you don't want to be an action.
An example for a use for that is a method that action methods call to produce the ActionResult for them:
[NonAction]
public JsonResult JsonInfo(string id, string value) {
return Json(new { id = id, value = value });
}
public JsonResult GetBusInfo() {
return JsonInfo("4", "Bus");
}
public JsonResult GetCarInfo() {
return JsonInfo("8", "Car");
}
The reason to make it public instead of private would be so that actions in other controllers could also use it.
Both works same with action method,you can use them seperately or together.
[NonAction]
private ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
FEED_TBL fEED_TBL = db.FEED_TBL.Find(id);
if (fEED_TBL == null)
{
return HttpNotFound();
}
return View(fEED_TBL);
}
If declare it like the above code then when we will try to go to details action method it will not go to it.Rather it will show the error.
{{ HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.}}
This shows that our detail link on view does found any reference to details action method and our controller to.
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.