How to add multiple HttpPost method in one controller? - c#

I'm creating a simple ASP.NET MVC web app. In my controller I have two HttpPost methods and I call two different stored procedures. Everything worked well until I added a second HttpPost method. Now I have a problem with the second HttpPost method - when I click on it ("Go to view") I get a message
Unable to find matching view
My controller:
//...
public class OrderController : Controller
{
[ActionName("Index")]
public ActionResult Index(OrderListOfClass ttt)
{
//code
}
[HttpPost]
[ActionName("Index")]
public ActionResult Index(OrderListOfClass ttt, string send)
{
//calling stored procedure 1
}
[ActionName("Tank")]
public ActionResult Tank(OrderListOfClass ttt)
{
//code
}
[HttpPost]
[ActionName("Tank")]
public ActionResult Tank(OrderListOfClass ttt, string sendBatch)
{
//calling stored procedure 2
}
}
My view:
#model SlurryOrderTest.Models.OrderListOfClass
//...
#using (Html.BeginForm("Index", "Order", FormMethod.Post))
{
//textbox to be filled by user - input parameter for stored procedure 1
}
#using (Html.BeginForm("Index", "Order", FormMethod.Post))
{
//textbox which are filled by stored procedure 1
}
#using (Html.BeginForm("Tank", "Order", FormMethod.Post))
{
//textbox to be filled by user - input parameter for stored procedure 2
}
#using (Html.BeginForm("Tank", "Order", FormMethod.Post))
{
//textbox which are filled by stored procedure 2
}
Why doesn't the "Tank" action go into a view?
I'm probably making some stupid mistake but C# is a new language for me :(

Sounds like you need to tell it which view to use. By default it will look for one called tank.cshtml in the Views\Order folder. So I think you need this for your tank method
[HttpPost]
[ActionName("Tank")]
public ActionResult Tank(OrderListOfClass ttt, string sendBatch)
{
//calling stored procedure 2
return View("Index", ttt);
}
Alternatively if you want it to go to its own specific view then create a tank view by creating a file call Tank.cshtml in the order folder.

There are few things you need to understand when calling controller methods.
In order to go to the view from the controller by right click you probably need to have a view within the views folder with the same name as that method.
In a controller if there are two methods with the same name and if one has an [HttpPost] attribute then it will be called from Form.Post in view. If the other method has no attribute then it will automatically be the get method.

Related

How to prevent HttpGet method being called?

So i have a regular method and a HttpGet method:
//Create a new note
public ActionResult EditNote()
{
return View();
}
//Edit a selected note
[HttpGet]
public ActionResult EditNote(int id)
{
var model = NotesProcessor.LoadNote(id);
return View(model);
}
They both use the same views page, but only the HttpGet method will fill up the page with details since the user will be editing an existing note here.
So the first method should open up a page that is not filled with data.
My issue is that i don't know how to call the non HttpGet method from my views page since it will automatically call the HttpGet method and the page will give me an error:
The parameters dictionary contains a null entry for parameter 'id'
This is how I'm trying to call the regular method: (Which worked fine before adding the other one)
#Html.ActionLink("Create New", "EditNote")
And this is for the HttpGet method:
#Html.ActionLink("Edit", "EditNote", new { id = Model.Id })
Honestly i thought it would detect the non overloaded syntax and call the right method but it doesn't.
I could make another views page for creating a blank note but that's not very 'DRY'...
What should i do?
You can rename the method with no parameters:
//Create a new note
public ActionResult CreateNote()
{
return View("EditNote");
}
//Edit a selected note
[HttpGet]
public ActionResult EditNote(int id)
{
var model = NotesProcessor.LoadNote(id);
return View(model);
}
And then in your view:
#Html.ActionLink("Create New", "CreateNote")
#Html.ActionLink("Create New", "EditNote", new { id = Model.Id })
Try adding HttpGetAttribute with route template without parameters:
[HttpGet("[controller]/[action]")]
public ActionResult EditNote()
{
return View();
}
But I would argue that it is worth considering renaming EditNode() into CreateNote()

ASP.NET Core 2.1 Razor Form, Post not reaching Controller

I spent a good margin of time searching and working through this problem yesterday without coming up with a solution. Here is the guide I used as reference.
The problem is that the data from the form is not reaching my controller. My goal is to take the form data and pass it to my controller/ model so that I can use the values throughout my code and store them in the database. Below is what I have so far...
In my Browse.cshtml (View)
#model Collect
<form asp-action="Collect" asp-controller="Collect" method="post">
<input type="hidden" asp-for="GameId" name="#game.id"/>
<button type="submit" class="dropdown-item btn btn-block">Default</button>
</form>
In my CollectController.cs (Controller)
using System;
using GameLibrary.me.Models;
using Microsoft.AspNetCore.Mvc;
namespace GameLibrary.me.Controllers
{
public class CollectController : Controller
{
[HttpGet]
public IActionResult Collect()
{
return View();
}
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Collect(Collect model)
{
Console.WriteLine("**********\n"+model.GameId+"\n**********");
return Content($"Hello {model.GameId}");
}
}
}
In my Collect.cs (Model)
namespace GameLibrary.me.Models
{
public class Collect
{
public int GameId { get; set; }
}
}
EDIT: Here is what my IDE is telling me...
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 POST https://localhost:5001/browse?game=eevee application/x-www-form-urlencoded 7
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 1.0139ms 200
info: Microsoft.AspNetCore.Server.Kestrel[32]
Connection id "0HLJHIUOU6AKO", Request id "0HLJHIUOU6AKO:00000003": the application completed without reading the entire request body.
Any guidance on what I am doing wrong would be great appreciated... Also can I send multiple values through the hidden field type, or should I make a new hidden field type for each value?
There was a lot of different help here, thanks especially to Kirk Larklin! There were three issues that was preventing my controller from picking up the data.
Browse.cshtml was missing the #addTagHelpers... I added the following:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#addTagHelper *, AuthoringTagHelpers
My CollectController.cs was missing a route... I added the following:
[HttpPost, ValidateAntiForgeryToken]
[Route("Index/Collect")]
Finally, I renamed my controller post method from 'Collect' which conflicting with another method to Index and updated the asp-action in my Browse.CSHTML file to match.
public IActionResult Index(Collect model)
Thanks for all the help!
-Travis W
First I would pass in the model to the view with the Id initialised:
public IActionResult Collect()
{
return View(new Collect { GameId = "5"});
}
Inside your view update the form to the following:
<form asp-action="Collect" asp-controller="Collect" method="post">
#Html.HiddenFor(m => m.GameId)
<button type="submit" class="dropdown-item btn btn-block">Default</button>
</form>
The Html helper will create the html coded for the field. Once you click submit the value will be correct.
In my case, I updated the login view with this line of code. that's fixed.
<form method="post" asp-controller="account" asp-action="login">
Based on your example I do not believe that you want custom actions, you should be able to achieve your goal with the default framework mapping.
public class CollectController : Controller
{
[HttpGet]
public IActionResult Index() => View();
[HttpPost]
public IActionResult Index([FromBody]CollectionModel model) => Content(...);
}
To clarify the magic, the declared Post method I utilize a HttpBody attribute, otherwise the framework will expect the parameter contents to be defined via Query String in the URL. The framework by default looks for Index when a Controller has been hit, if you do not need the URL to represent that, then do not use it.
The routing pattern usually follows:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
URL:
http://www.sample.com/Collect ---> Initial index Get would be hit

Search results getting lost on post back

I am new to this MVC Architecture so please execuse for basic or amature questions.
I need to implement a search functionality. I created an action in Home Controller SearchFNW(searchstring). In this method I got a list of ViewModels with searched data.
I am passing this list to Index.cshtml and trying to display results in a table.
I am able to get the View Model's List data to View.
Index.cshtml
#model List<First_Niagara_Works.ViewModels.AccountViewModel>
#{
#using (Html.BeginForm("Index", "Home"))
{
if (Model != null)
{
// code to populate table using model
}
}
}
Homecontroller.cs
public ActionResult SearchFNW(string FNWtodo, string SearchInput)
{
// all code to load viewmodels to list
return View(#"~/Views/Home/Index.cshtml", lstAccountVM);
}
public ActionResult Index()
{
return View();
}
Problem: After execution of this code it redirects to Layout.cshtml and from there it calls Index action again .. and started all over again with #Html.beginform(). so not able to see table data with searched results..
try:
[HttpPost]
public ActionResult Index(string FNWtodo, string SearchInput)
{
// all code to load viewmodels to list
return View(lstAccountVM);
}
public ActionResult Index()
{
return View();
}
Your form #using (Html.BeginForm("Index", "Home")) submits a POST back to the Index Action in the controller. You just need to use the Index action that captures the [HttpPost] and it will return the Index view but with your populated model.

Alternative to Response.Redirect with Request.Querystring

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.

How to use RedirectToAction while passing view model to a view with different route

I have a ASP.NET MVC 3 app with a common scenario where there is with an "About" controller using "Contact" and "ThankYou" actions.
I want user to go to /about/contact, fill out the form, submit it, and be taken to /about/contact/thankyou * page where the form contents (aka view model) will be displayed.
** Note there is no "Contact" controller nor do I want to create one for this purpose if possible.*
I am using RedirectToAction to prevent resubmission (Post/Redirect/Get pattern) and TempData to pass on view model to thank you page.
I can also use TempData to check if thank you page was reached directly and redirect back to contact form page (so it would not register as a "goal" in web analytics)
But one thing I could not figure out is how to use different route for thank you page so it appears as /about/contact/thankyou
Am I doing this right?
Is there a better way?
Here are relevant actions in AboutController
<!-- language: c# -->
[RequireHttps]
public ActionResult Contact()
{
var viewModel = new ContactViewModel();
return View(viewModel);
}
[RequireHttps]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Contact(ContactViewModel viewModel)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
// not sure if this is the best way or how to redirect to /contact/thankyou
TempData["viewModel"] = viewModel;
return RedirectToAction("ThankYou");
}
[RequireHttps]
public ActionResult ThankYou()
{
var viewModel = TempData["viewModel"];
// upon direct access, viewModel will be null and missing, so redirect to Contact form
if (viewModel == null)
{
return RedirectToAction("Contact");
}
return View(viewModel);
}
You could define a custom route before the default route:
routes.MapRoute(
"contact",
"about/contact/thankyou",
new { controller = "About", action = "ThankYou" }
);
Now when you navigate to /about/contact the Contact GET action will be executed (You have to rename it as in your code it is called ContactUs). Then the user fills the form and submits to the Contact POST action. The url stays the same for the moment: /about/contact. After the redirect the ThankYou action is executed and the url changes to /about/contact/thankyou.

Categories

Resources