Get and post methods with different names - asp.net-mvc

I am using ASP.NET MVC 3.
I have an action method called New. When form validation succeeds then I want Create to handle the request.
public ActionResult New()
{
// Code
}
[HttpPost]
public ActionResult Create()
{
// Code
}
I have 2 questions regarding this. Firstly, how do I modify my Html.BeginForm to handle the above? I tried the following but I get an error, it is looking for the physical file Create (News is my controller name):
#using (Html.BeginForm("Create", "News"))
{
<!-- Code -->
}
Secondly, how do I prevent a user from typing in /News/Create in the URL. Create should handle only my post requests coming from New.
UPDATE:
I managed to get something working. The original URL looks like this:
http://localhost:33947/News/New
After I click the submit button and validation fails then the URL looks like:
http://localhost:33947/News/Create
Why does it do this? I want it to stay:
http://localhost:33947/News/New
Here is my action method code:
public ActionResult New()
{
return View();
}
[HttpPost]
public ActionResult Create(NewsViewModel newsViewModel)
{
if (!ModelState.IsValid)
{
return View("New", newsViewModel);
}
return View("Index");
}

#using (Html.BeginForm("Create", "News", FormMethod.Post))
You cannot prevent user from typing address manually, but you can add another Create action marked with [HttpGet] attribute which will show user the error page. Anyway, your current Create action method will not handle such requests. Probably user will get standard File not found error in this case.
UPDATE: Html.BeginForm("Create", "News") means to submit the form to action "Create" of the controller "News", so it works as it should. If you want to submit to /News/New, replace it with Html.BeginForm("New", "News").

Regarding your second question, instead of:
if (!ModelState.IsValid)
{
return View("New", newsViewModel);
}
you should use :
if (!ModelState.IsValid)
{
return RedirectToAction("new");
}
Now this has the undesired side affect that your Modelstate is lost. (no validation errors are shown in your form.
To fix this add the ModelStateToTempData attribute to both your new and create action.
This attribute come as part of http://mvccontrib.codeplex.com/ .

Related

return RedirectToAction("Edit", Model)

Afternoon Folks,
I have a question in reference to how to redirect to another model within my mvc project.
I have a controller with CRUD operations, and i have a [HttpPost] action on Create. The system saves the data to the database as required but instead of me sending the user back to the "Index" page i want to sent them to another model.
I know how to use the redirect option...
// POST: CarerDetailsNew/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CarerViewModel carerViewModel)
{
if (ModelState.IsValid)
{
db.CarerDetails.Add(carerViewModel.carer);
db.SaveChanges();
return RedirectToAction("Edit", carerViewModel.carer);
//return RedirectToRoute("ClientRecordsController");
}
return View(carerViewModel);
}
However i have read that i may be able to use the "RedirectToRoute" option to get back to another model.
The model that i am in is my "Carer" model but i want to get back to my "Client" model and specifically the "Edit".
I have tried to replace my RedirectToAction to one of the following but this fails to see the "Client" model.
return RedirectToRoute("Edit", clientViewRecord.client);
Or
return RedirectToRoute(ClientRecordsController, "Edit", clientViewRecord.client);
Any suggestions are more than welcome as i am struggling to get this to work.
Regards
Betty

SurfaceController generate incorrect URL?

A Form is posted to a SurfaceController 'Submit' action. After saving to the database, it redirects to another action 'LeadContact', in the same controller (using RedirectToAction()), passing in 'Id' as a paramter. The model is then populated and passed to the 'LeadContact' view.
Not sure if I'm doing this correctly, but when 'LeadContact' renders in the browser, the URL shows as
http://localhost:50656/umbraco/Surface/HealthInsurance/LeadContact?leadOptionId=70`
while I'm expecting it to be
http://localhost:50656/HealthInsurance/LeadContact?leadOptionId=70
In short it adds /umbraco/SurfaceContact' into url.
Can you please advise how I can correct it and what I'm doing wrong ?
public ActionResult Submit(FormCollection form)
{
//Some logic and later redirect to another action 'LeadContact'
return RedirectToAction("LeadContact", new { leadOptionId = _id});
}
public ActionResult LeadContact(int leadOptionId)
{
MyViewModel model = new MyViewModel();
//Lines of code to populate data into model
return View("LeadContact", model);
}
Thanks for your help and sharing.
Check your project properties, under Web you most likely have a virtual path specified.

MVC detect when model is empty

I'm new to MVC, so I'm trying to figure out some best practices.
Suppose I have a controller HomeController method Index(MyViewModel model):
public ActionResult Index(MyViewModel model)
{
//if loading the page for the first time, do nothing
//if the page has been posted data from somewhere, then I want to use
// some of the arguments in model to load other data, like say search results
}
When I navigate to the /Index page, I (myself) expect the model object to come through as null, but it doesn't. MVC (somehow) creates a MyViewModel for me.
My question is, what's the best way or most consistent to determine if model was created automatically, or via a post?
Ideas:
Create a property on MyViewModel that gets set when the view is posting back
Check for if the Request.HttpMethod == "GET" or "POST"
Something else?
You should use different actions for your GET and POST requests. Don't try and make a single method do too much.
[HttpGet]
public ActionResult Index()
{
// handle the GET request
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (ModelState.IsValid)
{
// it's a post and the data is valid
}
}
The correct method will then be called depending on whether it's a GET or POST
Create two actions, one which accepts a model instance and one which doesn't.
Even though you're "going to the same page" you are in fact performing two distinctly different actions. The first action loads an initial page, the second action posts some value to be acted upon. Two actions means two methods:
[HttpGet]
public ActionResult Index()
{
// perform any logic, but you probably just want to return the view
return View();
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// respond to the model in some way
return View(model);
// or return something else? a redirect? it's up to you
}
Note that this kind of breaks your restful URLs. Consider semantically what you're doing in these actions:
Viewing an index
Posting to an index
The first one makes sense, but the second one probably doesn't. Normally when you POST something you're doing something related to a model or action of some sort. "Index" doesn't really describe an action. Are you "Create"-ing something? Are you "Edit"-ing something? Those sound like more meaningful action names for the POST action.

Resource Cannot Be Found Error on Using "HttpPost" in Controller

Hello Am Working on a ASP.Net MVC 3 project and am getting a error called "Resource cannot be found" My situation is i have
1: am Using my own views and returning them in actions for instance i created a view manually first called "Create.cshtml" and manually added it to a action like this
[HttpPost]
Public ActionResult CreateStudent(StudentIfo studentinfo)
{
db.StudentInfo.add(studentinfo);
db.SaveChanges();
Return View("~/Views/Student/Create.cshtml");
}
[HttpGet] Before this Action works good but why Not HttpPost???
My Route Map says:
routes.MapRoute(" ",
"{controller}/{action}/{id}",
new { controller = "Student", action = "CreateStudent", id = UrlParameter.Optional }
);
2: Whenever i write [HttpPost] i get this Error and if I remove it then everything works good if such thing continues then how to save the data??
3: My Create.cshtml has a #Html.BeginForm("CreateStudent","Student",FormMethod.Post) am not getting what the issue is?? i have searched a lot but not getting a good answer.
4: What is best way for CURD operation when we are using our own views rather using Visual studios Scaffolding templates i.e am I going in right way?? i want my own views and then write my controllers according to them not as that of Visual Studio way first write controller then right click "Add-View"
Please recommend some good ways or any site or tutorials regarding it.
In short you will need both, you need a [HttpGet] action to return the initial form that the user can enter values into and then a [HttpPost] version to do the persistence. From this [HttpPost] method you should then RedirectToAction (return RedirectToAction(...)) to ensure that reloading of the page does not re-run the post operation.
So:
[HttpGet]
public ActionResult CreateStudent()
{
var viewModel = new CreateStudentViewModel { /* Set properties here or load data for it */ };
return View(viewModel);
}
[HttpPost]
public ActionResult CreateStudent(PostedValues values)
{
if (ModelState.IsValid)
{
// Create Student Here
return RedirectToAction(/* Jump to a logical place with a Get */)
}
/* Initialize correct error viewModel again and show the create student screen with validation errors */
return View(viewModel)
}
Personally I name these methods GetCreateStudent and PostCreateStudent and add two routes with a route constraint that limits the Http Method (see here)

How does the controller receive parameters on HttpPost methods?

Take this snippet out of a controller, for example:
public ActionResult Login()
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Home");
}
else
{
return View();
}
}
//
// POST: /User/Login
[HttpPost]
public ActionResult Login(LoginModel lm, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(lm.UserName, lm.Password))
{
FormsAuthentication.SetAuthCookie(lm.UserName, lm.RememberMe);
if (Url.IsLocalUrl(returnUrl) &&
returnUrl.Length > 0 &&
returnUrl.StartsWith("/") &&
!returnUrl.StartsWith("//") &&
!returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "Username and Password combination is incorrect");
}
}
return View();
}
My question is, with the overloaded method of Login() (for the HTTP POST), what calls this method with the first parameter as a LoginModel class, and the second parameter as a string? My Login.cshtml view uses a submit button, so I am curious as to how these parameters get passed to the Login() method? What's to stop me from adding a third parameter? And how would that parameter get passed??
I know this is a basic question, I'm just trying to understanding the connecting piece between Views and Controllers.
This process is called Model Binding, lots of resources on it...I would start with How to implement a custom one because from that you will then understand how and why the process works.
http://buildstarted.com/2010/09/12/custom-model-binders-in-mvc-3-with-imodelbinder/
http://bradwilson.typepad.com/blog/2010/10/service-location-pt9-model-binders.html
EDIT:
There is a default model binder which exists which takes items in the post collection like form data and url parameters and tries to match them up with items which you have told the controller to expect. It works something like this...
Request comes in,
MVC decides which controller should get the request
MVC then looks at the HTTP Method, was it a GET or POST?
If it was a post then it looks for the controller action with the HttpPost attribute
MVC then looks at the parameters you have defined...
In your case it says I have a LoginModel and a returnUrl;
MVC now is happy - it knows where things are going so now it looks at what you sent...
It looks at your post data (the form fields and query string parameters)
One by one it looks at their names and then tries to find a match in your controller parameters.
It finds a Username field in the form data, it takes a look at your parameters and asks if there is a Username? No, well maybe Username is a property of LoginModel...it then looks through LoginModel. Aha it says, found one. MVC then creates an instance of LoginModel and sets the Username property to the value you posted.
This process continues for each of the things it finds in your form and query string. There are a bunch of rules it follows but you get the picture - this matching is part of the 'convention over configuration' beauty of MVC.
This whole process above it model binding and since MVC implements this default behavior you do not see it written out in the Global.asax or anything, it is just part of MVC.
Take a look at your LogOn view, this is where the post method gets the returnUrl parameter
Html.BeginForm("LogOn", "Account", new { returnUrl = #Request.QueryString["ReturnUrl"] }

Resources