I'm a little new to asp.net mvc and I have a question (very basic). I have hacked around but I am not totally sure about this and I could'nt find anything particularly helpful.
Assume that I have 2 controllers A and B and 2 views FullView and PartView
public class AController:...
{
//Renders FullView
public ActionResult Create
{
....
}
[HttpPost]
public ActionResult Create
{
....
}
}
public class BController:...
{
//Renders an Arbitrary partial View (PartView)
public ActionResult Create
{
....
}
//Saves the data of the partial View
[HttpPost]
public ActionResult Create
{
....
}
}
the 1st view (FullView) has the code
<%Html.RenderAction("Create", "B"); %>
my question is on submit will BController's action ([HttpPost] Create) run?
Thank you
That depends on what action you specify in your <form /> tag. This doesn't have anything to do with asp.net mvc. If you use Html.BeginForm() without parameters it will post to the current url (not the create action on BController).
Well 1st thing you could do is toggle some breakpoints in your actions and hit f5.
Second - what action is called purely depends on what url you hit with what http method.
But for your case, when you post form A and controller A processes post you might get into validation problems and that's when you return View() on a post action and that's why form B is rendered via its post method.
Related
I'm working on an REST API in ASP.NET. One of the first things I made was a Model Binder for the model, binded with the model by a code like this:
[ModelBinder(typeof(MyModelModelBinder))]
public class MyModel { ... }
And used it in an action
public IHttpActionResult Post(MyModel model) { ... }
I realised, quite suddenly, that after I added the [FromBody] before the parameter type...
public IHttpActionResult Post([FromBody]MyModel model) { ... }
...the Model Binder gets ignored. I tried to debug it by puting a breakpoint on the beginning of the BindModel method and at the beginning of the action and realised that when I call the action I get straight to the action. When I delete the FromBody Attribute the breakpoint in the ModelBinder becomes active again.
Why can't I use both? Why is the FromBody attribute ignoring the ModelBinder?
I use the default route definition:
{controller}/{action}/{id}
where id = UrlParameter.Optional. As much as I understand it this means when id is not being part of the URL this route value will not exists in the RouteValues dictionary.
So this also seems perfectly possible (both GET):
public ActionResult Index() { ... } // handle URLs: controller/action
public ActionResult Index(int id) { ... } // handle URLs: controller/action/id
When id is missing the first action would be executed, but when id is present, the second one would execute. Fine, but it doesn't work. It can't resolve actions.
How can I accomplish this?
I'm thinking of writing a custom action method selector attribute like:
[RequiresRouteValue(string valueName)]
This would make it possible to use this kind of action methods. But is this the only way of doing it?
Is there something built-in I can hang on to?
Use either:
[HttpGet]
public ActionResult Index() { ... } // handle URLs: controller/action
[HttpPost]
public ActionResult Index(int id) { ... } // handle URLs: controller/action/id
Or just have one with a nullable param:
public ActionResult Index(int? id) { ... } // handles both instances
EDIT:
Would something like this work?
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/", // URL with parameters
new { controller = "Login", action = "Index" } // Parameter defaults
);
routes.MapRoute(
"DefaultWithValue", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Login", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Well from the exception that action can't be determines is pretty clear that actions are resolved first then data binder comes into play and examines action's parameters and tries to data bind values to them. Makes perfect sense.
This makes perfect sense. There would be no point in first trying to data bind values to all possible types and see what we get and then look for an appropriate action. That would be next to impossible.
So. Since action selection is the problem here I guess the best (and only) way to solve this (if I don't want to use a multifaceted single action method) is to write a custom action method selector attribute.
You can read all the details and get the code on my blog:
Improving Asp.net MVC maintainability and RESTful conformance
When I call other action in one action, it also display itself template, in Play 1.1 RC
and when I Redirect("...url") but it does not work, is there someone that can help me?
Just to add to the answers above, here's how you redirect to an external url:
public static void index() {
redirect("http://geeks.aretotally.in");
}
To redirect, you simply call the action. From the example in the documentation:
public static void show(Long id) {
Article article = Article.findById(id);
render(article);
}
public static void edit(Long id, String title) {
Article article = Article.findById(id);
article.title = title;
article.save();
show(id);
}
At the end of the edit action, the call to show(...) will cause a redirect on the client's browser as if they had hit the same URL that routes to the show method.
Since none of these answers provide a general/reusable method to do this, here is my code. This allows you to create any number of redirects in the conf/routes file without creating a controller for each.
Yes, this is trivial, but perhaps it is of use to someone.
conf/routes:
GET /admin Application.redirect(url:'/admin/index.html')
app/controllers/Application.java:
public class Application extends Controller {
public static void redirect(String url) {
redirect(url, true);
}
}
In the play framework, when you call an action, by default it renders the template associated with that action.
For example, a Contoller named Application
public static void index()
Will render
app/views/Application/index.html
To make it render a different view, then you can specify the template as the first parameter in the render method.
So,
renderTemplate("Application/myOtherTemplate.html");
Redirect should only really be used if you are redirecting to a URL outside of your application.
Is it possible to use a custom action invoker without having to instantiate it in the Controller handler factory? For example in custom controller factory:
IController IControllerFactory.CreateController(RequestContext reqContext, string controllerName)
{
var controller = base.CreateCOntroller(reqContext,controllerName ) as Controller;
controller.ActionInvoker = new CustomActionInvoker();
}
Or is there another way I can execute an MVC action without having to use a custom action invoker?
Updating the question
I have a controller, say HomeController and Index action. Index is the main action in the controller. Once the Index action gets executed, the MVC view will fire multiple actions using Ajax - GET requests (we using jTemplates).
Example
// Controller actions
// main action and View
public ActionResult Index() { ... }
public ActionResult AjaxAction1(string id) { ... }
public ActionResult AjaxAction2() { ... }
public ActionResult AjaxAction3() { ... }
Now I want to filter some of these actions not to execute depending on certain scenarios. For example I want to stop executing AjaxAction1 when the id is equal to 2.
Back to my original question. Is there a way to achieve this without using the action invoker. The reason that i don't want to use the action invoker is the way my project being structured ended up with circular references.
Any ideas greatly appreciated.
Found the answer you can subclass the Controller and create the ControllerActionInvoker there.
Using action method selector
Depending on what happens when id equals 2, but this could quite easily be done by writing a custom action method selector.
Using action method selector you could provide your actions that would execute depending on your parameter values:
[RequiresParameterValue("id", #"^2$")]
[ActionName("AjaxAction")]
public ActionResult AjaxAction1(string id) { ... }
[RequiresParameterValue("id", #"^[^2]*$")]
[ActionName("AjaxAction")]
public ActionResult AjaxAction2(string id) { ... }
As you can see from this example the custom action method selector takes two parameters:
controller action parameter name (id in example)
regular expression that checks controller action parameter value
Action method selector sees all route values as strings when they're coming from the client so you can actually pull this off by using regular expressions. And it makes it also very very flexible.
The first action method will get executed when id == "2" and the second one when id != "2".
I'm having difficulty getting data from a textbox into a Controller. I've read about a few ways to accomplish this in Sanderson's book, Pro ASP.NET MVC Framework, but haven't had any success.
Also, I've ran across a few similiar questions online, but haven't had any success there either. Seems like I'm missing something rather fundamental.
Currently, I'm trying to use the action method parameters approach. Can someone point out where I'm going wrong or provide a simple example? Thanks in advance!
Using Visual Studio 2008, ASP.NET MVC2 and C#:
What I would like to do is take the data entered in the "Investigator" textbox and use it to filter investigators in the controller. I plan on doing this in the List method (which is already functional), however, I'm using the SearchResults method for debugging.
Here's the textbox code from my view, SearchDetails:
<h2>Search Details</h2>
<% using (Html.BeginForm()) { %>
<fieldset>
<%= Html.ValidationSummary() %>
<h4>Investigator</h4>
<p>
<%=Html.TextBox("Investigator")%>
<%= Html.ActionLink("Search", "SearchResults")%>
</p>
</fieldset>
<% } %>
Here is the code from my controller, InvestigatorsController:
private IInvestigatorsRepository investigatorsRepository;
public InvestigatorsController(IInvestigatorsRepository investigatorsRepository)
{
//IoC:
this.investigatorsRepository = investigatorsRepository;
}
public ActionResult List()
{
return View(investigatorsRepository.Investigators.ToList());
}
public ActionResult SearchDetails()
{
return View();
}
public ActionResult SearchResults(SearchCriteria search)
{
string test = search.Investigator;
return View();
}
I have an Investigator class:
[Table(Name = "INVESTIGATOR")]
public class Investigator
{
[Column(IsPrimaryKey = true, IsDbGenerated = false, AutoSync=AutoSync.OnInsert)]
public string INVESTID { get; set; }
[Column] public string INVEST_FNAME { get; set; }
[Column] public string INVEST_MNAME { get; set; }
[Column] public string INVEST_LNAME { get; set; }
}
and created a SearchCriteria class to see if I could get MVC to push the search criteria data to it and grab it in the controller:
public class SearchCriteria
{
public string Investigator { get; set; }
}
}
I'm not sure if project layout has anything to do with this either, but I'm using the 3 project approach suggested by Sanderson: DomainModel, Tests, and WebUI. The Investigator and SearcCriteria classes are in the DomainModel project and the other items mentioned here are in the WebUI project.
Thanks again for any hints, tips, or simple examples!
Mike
try strongly typing the page to use SearchCriteria to autopost the data like that ex:
public partial class Search: ViewPage<SearchDetails>
This should do it for you (unable to verify this is perfect - typed this from memory):
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SearchDetails(FormCollection formValues)
{
var txtContents = formValues["Investigator"];
// do stuff with txtContents
return View();
}
1.) Have you looked into ViewModels for your View? In essence that is what your SearchCriteria class is. Make sure you strongly type your view with that model:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/MyMaster.Master" Inherits="System.Web.Mvc.ViewPage<SearchCritieria>"
Also make sure that you use the HtmlHelper.TextBoxFor method to map this Investigator property to the SearchCritiera model. On Post back your text box value should be there:
'<%=Html.TextBoxFor(model => model.Invesigator)%>'
Good luck!
Also here is a great reference on using ViewModels that I have looked at a lot recently:
http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx
Thanks for the tips everyone. For learning purposes, I need to go back and follow the strongly typed route. I'm curious if I would have run into this problem if I would have done that from the beginning.
Until then, the following worked:
Use a submit button
Use this code for the form:
<% using(Html.BeginForm(new { Action = "SearchResults"})) { %> <% } >
Thanks again for you help!
Mike