Resource Cannot Be Found Error on Using "HttpPost" in Controller - asp.net-mvc

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)

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

How to post a custom model to a controller in Umbraco 7.5.8?

I have a Document type, Template, and a page in the CMS content tree which uses these for a Contact page. The document type has no CMS data properties, because it doesn't need any. I use Models Builder for other pages with no issue, but for this page I've created my own custom model within my MVC project.
I've read every tutorial I can find, and looked at every forum post and issue on the Umbraco forums and Stackoverflow, and for the life of me I can't figure out what I'm doing wrong. The model name and namespace do not conflict with the autogenerated Models builder one.
My understanding is for posting forms a SurfaceController is the way to go - a RenderController is intended more for presenting stuff. So my controller extends SurfaceController. uses Umbraco.BeginUmbracoForm(etc)
I've tried every combination of SurfaceController and RenderController with UmbracoTemplatePage, UmbracoViewPage and every way of changing my model to extend both RenderModel and IPublishedContent to test each. When trying RenderController I've overridden default Index method with RenderModel parameter to create an instance of my model with the renderModel parameter.
Usually the error I get is "Cannot bind source type Umbraco.Web.Models.RenderModel to model type xxx". Sometimes combinations I've attempted allow the Get to succeed, then give this error on Post.
I've even tried to remove the page from the CMS and use a standard MVC controller and route - this allows me to display the page, and even using a standard Html.BeginForm on my view, I get an error when trying to post the form (despite a breakpoint in the code in controller being hit) which also states it "Cannot bind source type Umbraco.Web.Models.RenderModel to model type xxx"
This CANNOT be this difficult. I'm ready to throw laptop out window at this stage.
What am I doing wrong???? Or without seeing my code at least can anyone tell me how this is supposed to be done? How do you post a custom model form to an Umbraco 7.5 controller, with no CMS published content properties required?
As it stands, my View looks like this:
#inherits UmbracoViewPage<Models.Contact>
...
using (Html.BeginUmbracoForm<ContactController>("Contact", FormMethod.Post
My Controller looks like this:
public class ContactController : SurfaceController
{
public ActionResult Contact()
{
return View("~/Views/Contact.cshtml");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Contact(Contact contactForm)
{
...
}
And my model looks like this:
public class Contact : RenderModel
{
public Contact() : base(UmbracoContext.Current.PublishedContentRequest.PublishedContent, UmbracoContext.Current.PublishedContentRequest.Culture)
{
}
public Contact(IPublishedContent content) : base(content, CultureInfo.CurrentUICulture)
{
}
[Display(Name = "First Name", Prompt = "First Name")]
public string FirstName { get; set; }
...
Update: If I use the model for my CMS page created automatically by models builder, the Get and Post work ok. However when I customise the model (i.e. I put a partial class of the same name in ~/App_Data/Models and regenerate models on Developer tab), the custom properties in my posted model are always null.
I can populate these manually from the request form variables, however this seems wrong and messy. What's going on here?
public class ContactPageController : SurfaceController
{
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Contact(ContactPage contactForm)
{
try
{
contactForm.FirstName = Request.Form["FirstName"];
contactForm.LastName = Request.Form["LastName"];
contactForm.EmailAddress = Request.Form["EmailAddress"];
contactForm.Telephone = Request.Form["Telephone"];
contactForm.Message = Request.Form["Message"];
var captchaIsValid = ReCaptcha.Validate(ConfigurationManager.AppSettings["ReCaptcha:SecretKey"]);
if (ModelState.IsValid && captchaIsValid)
{
// Do what you need
TempData["EmailSent"] = true;
return RedirectToCurrentUmbracoPage();
}
if (!captchaIsValid)
{
ModelState.AddModelError("ReCaptchaError", "Captcha validation failed - Please try again.");
}
return RedirectToCurrentUmbracoPage();
}
catch (Exception ex)
{
LogHelper.Error(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, null, ex);
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
}
Further Info: Robert's approach first time, thanks for that. I had tried one approach using PartialViews and ChildActions but clearly I didn't do it correctly. Would I be right in saying the reason this approach is required at all (i.e. why you can't add the custom properties to the model the main view is bound to) is because I am using Models Builder?
So among the strange errors I received was one about 2 classes both wanting to represent the contact page, and another about it expecting one type of class in a dictionary (ContactPage) but receiving another (Contact) - even though I had made no reference to ContactPage in either view or controller. This suggests to me ModelsBuilder adds a mapping for document types to models on app startup behind the scenes? Which is maybe why you're better to take this approach of letting ModelsBuilder do its thing, and build your own model on top of that with a partial view in this way?
I've found the quality of documentation on this topic very poor indeed. Not sure if maybe the good stuff is behind closed doors, i.e. requires a paid Umbraco membership? For a supposedly open source system, that feels kinda shady to me.
Easy when you know how!!
For your situation, SurfaceController is the most likely candidate as you've surmised, however think of the actions in that Controller as applying to partial views, not the full view used by the page.
Don't try to use the ModelsBuilder ContactPage model, but rather create your own as you were originally (the original Contact model) - think of it as a Data Transfer Object perhaps if you do need to apply any of the properties back to the ContactPage model for any reason.
I've found I've had the greatest success with SurfaceController with the following conditions:
The Page Template does not inherit from the model intended for the Form; it inherits directly from the standard Umbraco PublishedContentModel or a ModelsBuilder generated model.
Implement a Partial View for the action defined in your SurfaceController - this view inherits from Umbraco.Web.Mvc.UmbracoViewPage<Contact> in your example.
The Partial View utilises BeginUmbracoForm<ContactController>, specifying POST action as one of the parameters (depending on which signature you're using)
You shouldn't need to populate any model properties using Request.Form like this.
For example, the code in one of my projects looks something like this:
SurfaceController
public class FormsController : SurfaceController
{
[ChildActionOnly]
public ActionResult ContactUs()
{
return PartialView(new ContactForm ());
}
[HttpPost]
public async Task<ActionResult> HandleContactUs(ContactForm model)
{
if (ModelState.IsValid)
{
if (!await model.SendMail(Umbraco)) // Do something with the model.
{
}
}
return RedirectToCurrentUmbracoPage(); // Send us back to the page our partial view is on
}
}
Partial View:
#inherits Umbraco.Web.Mvc.UmbracoViewPage<ContactForm>
#using Digitalsmith.ReCaptcha
#using (Html.BeginUmbracoForm<FormsController>("HandleContactUs"))
{
...
}
Contact Page Template:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage<ContactPage>
#{
Layout = "_Layout.cshtml";
}
#Html.Action("ContactUs", "Forms")

MVC Linking to views

New to MVC so learning (in a frustrated kind of way as finding simple things that used to take 5 minutes in webforms have become over complicated)
So I've set up a solution in VS2015 which comes with the pre-requisite Account section.
I've also set up tables in a database and built the entities from that (producing some CRUD)
So I can log in and get to my AccountHome view (return View("AccountHome") in the AccountController)
From my AccountHome page, I can navigate to another view using a seperate controller(#Html.ActionLink("Add Foods", "Create", "fad_userFoods", new { id = ViewBag.userID }, new { #class = "btn btn-default" }))
Here I do some form entry and the data is successfully added to the database but here is where my problem lies. After data entry I want to go back to the AccountHome view
So in my fad_userFoodsControler I have this code(return RedirectToAction("AccountHome","Account")) - says resource cannot be found!!!
In my AccountController I have the following code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AccountHome()
{
return View();
}
In my AccountViewModels I have this code:
public class AccountHomeViewModel
{
public string Email { get; set; }
}
Finally, in my AccountHome.cshtml page I have this declared at the top (#model FiveADayMVC2.Models.AccountHomeViewModel)
Where am I going wrong? Looked at numerous posts and they all do it this way. I know the view exists because I can get to it from login. Yet, if I type the url manually (i.e. my url followed by /Account/AccountHome) it's not found either???
It's going to be a lack of understanding on my part but where?
Thanks in advance for any pointers
Try using RedirectToAction This will navigate the user to the view of a specific action in a given controller.
if(actionSuccess)
{
return RedirectToAction("Index", "Home");
}
UPDATE:
Make sure you have a controller for HttpGet
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AccountHome()
{
return View();
}
public ActionResult AccountHome()
{
return View();
}
Since you added [HttpPost] to the controller it will be the one executed after a post was made on "AccountHome"

How to add WebApi in Asp.net MVC and then consume the WebAPI in the same application

I have achieved creating the WebApi itself and I can browse it from the browser and get the output.
The thing which is not working for me is that I am trying to consume the WebAPI from an MVC Controller, and I have written the code for calling the WebAPI in my "cshtml" view.
But it doesn't work, as I am getting the error in loading the page, I understand I am doing something wrong. So the first question would be: am I doing this correctly, or is it completely wrong to create a WebAPI part in an MVC Project and then try to consume it in the same MVC project from the controller?
To answer your question, it's actually "as designed" and recommended to have your WebAPI and MVC client inside the same project. This is why you have both a RouteConfig.cs and a WebApiConfig.cs inside your MVC project. RouteConfig.cs is for your MVC controllers, and WebApiConfig.cs is obviously for your Api Controllers.
To have both in the same project is easy. What I do is add a folder named "API" in my root, and place all my WebAPI controllers in there. Keep in mind, and I'm sure you know, that the only difference between a WebAPI controller and an MVC controller is that a WebAPI controller inherits ApiController which is part of System.Web.Http (I believe) whereas an MVC controller inherits Controller which is part of System.Web.MVC.
Below is the proper way to make GET/PUT/DELETE/POST requests TO your WebAPI FROM an MVC front end. It doesn't matter if it's in the same project or not because you specify the WebAPI URL in your controller's constructor. If your WebAPI is on a different server than your front end MVC app, you will need to enable CORS support, which is a feature available in WebAPI version 2 and above.
This is the proper way to call a WebAPI from your front end, MVC client.
In your controller page, remove anything that has to do with DbContext, Entity Framework, etc. The reason is by default, the controller will want to perform CRUD operations by calling the DbContext, and we don't want this. We want to call the WebAPI instead to do this. When I refer to "Controller", I'm referring to the MVC controller, not the WebAPI controller.
First and foremost, declare some member variables in your MVC controller. The rest of your MVC controller will utilize these:
HttpClient client = new HttpClient();
HttpResponseMessage response = new HttpResponseMessage();
Uri contactUri = null;
In your MVC controller, create a constructor for your controller, as such:
public ContactController()
{
// set base address of WebAPI depending on your current environment
// the URL below, if the API is in the same project, will be something
// like "http://server/YourProjectName" - replace server with either
// "localhost", etc.
client.BaseAddress = new Uri("http://server/YourAPI/");
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
Replace the Index action's code with something like the following. Note that the only relevant pieces are the client.GetAsync() call and the var contacts assignment. Everything else is not necessary for the context of this problem. The value inside the client.GetAsync() should be the name of your controller, prepended by any custom routing you set up in your WebApiConfig.cs - in my case, I added the api part in my route to distinguish between API calls and normal calls:
public ActionResult Index()
{
response = client.GetAsync("api/contact").Result;
if (response.IsSuccessStatusCode)
{
var contacts = response.Content.ReadAsAsync<IEnumerable<Contact>>().Result;
return View(contacts);
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Create action (the HttpPost action) with something like the following. Again, the only important piece is the client.PostAsJsonAsync() part - this is what calls the WebAPI's POST action which takes care of, in my case, inserting a new record into the database:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Contact contact)
{
// Create a new product
response = client.PostAsJsonAsync("api/contact", contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Edit action (the non-HttpPost action) with something like the following. This was a little tricky because in order to edit, you had to retrieve the record first, so basically, the HttpPost version of Edit will contain somewhat similar code, with an additional line of code that performs the edit POST (PUT). Below, we're getting the response from the WebAPI by passing it a specific record ID. So, just like for Index (GET), we are doing the same thing only passing in the ID so we only get back one record. Then, we cast the response to an actual object that can be operated on in the View:
public ActionResult Edit(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Replace the Edit action (the HttpPost action) with something like the following. Below, we're getting the record to be edited by calling client.GetAsync() and passing in the primary key as a parameter (contact_id). Then, we're getting the RequestUri from that response and saving it. Then, we're calling client.PutAsJsonAsync() and passing in the Uri.PathAndQuery (what we just saved) as well as the object to be edited.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Contact contact)
{
response = client.GetAsync(string.Format("api/contact/{0}", contact.contact_id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.PutAsJsonAsync(contactUri.PathAndQuery, contact).Result;
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
// add something here to tell the user hey, something went wrong
return RedirectToAction("Index");
}
}
Replace the Delete action (the non-HttpPost action) with something like the following. So again, we're getting the record from the database by simply calling client.GetAsync() and casting it to an actual object my app knows of.
public ActionResult Delete(int id = 0)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
Contact contact = response.Content.ReadAsAsync<Contact>().Result;
if (contact == null)
{
return HttpNotFound();
}
return View(contact);
}
Finally, replace the Delete action (the HttpPost action) with something like the following. Again, we're doing something similar to that of the Edit action. We are getting the record to be deleted, casting it to an object, and then passing that object into a client.DeleteAsync() call, as shown below.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
response = client.GetAsync(string.Format("api/contact/{0}", id)).Result;
contactUri = response.RequestMessage.RequestUri;
response = client.DeleteAsync(contactUri).Result;
return RedirectToAction("Index");
}

Get and post methods with different names

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/ .

Resources