HTTPPOST Attribute not working in MVC - asp.net-mvc

i have a Controller in which two actions are defined.
public class ExamlpeController : Controller
{
[Route("Index")]
public ActionResult Index()
{
return View(new ExampleViewModel { Message = new MessageDisplay { MessageVisible = false, IsGoodMessage = true, Message = String.Empty } });
}
// POST:
[HttpPost]
[Route("Index/{exampleData?}")]
public ActionResult Index(ExampleViewModel exampleData)
{
if (!ModelState.IsValid) // If model state is invalid
// Return view with validation summary
return View(new ExampleViewModel { Message = new MessageDisplay { MessageVisible = false, IsGoodMessage = true, Message = String.Empty } });
else // If model state is valid
{
// Process further
bool isGoodMessage = true; // Default
string message = "success";
isGoodMessage = true;
message = "test data";
// Clear model state if operation successfully completed
if (isGoodMessage)
ModelState.Clear();
return View(new ExampleViewModel { Message = new MessageDisplay { IsGoodMessage = isGoodMessage, MessageVisible = true, Message = message } });
}
}
}
so, when my view is called then first "Index" action is called but when i post my form it also called first index method.
this code is working fine in old build, new build contains some changes which are not related to this Controller, but it is not working,
when i add HTTPGET Attribute with first action then it is working fine,
first action called on page load and second action is called on page post.
so, my question is that how Routes are maintained the route table and what is the reason for that condition.

On your POST action change [Route("Index/{exampleData?}")] to [Route("Index")] and it should work. You don't include the POSTed view model as part of the route - think about it, how would it display that posted data in the URL anyway?

Related

Show a message from ViewBag set in a controller on an Exception and Stay in the same View

What I have
A single view 'AddEdit.cshtml' that is used to both Edit and also Delete a Student entity.
Edit ActionResult:
public ActionResult Edit(int Id)
{
// code to get the Student object create a VM and call the view passing the VM
ViewBag.Mode = "Edit";
return View("AddEdit", studentVm);
}
Delete button in the same view:
#Html.ActionLink("Delete Student", "Delete", new { id = Model.StudentId }, new { role = "button", #class = "btn btn-danger",id="deleteStudent" })
In case of an exception when deleting I want to inform the user by showing a message in a alert so I added this code:
public ActionResult Delete(int id)
{
var student = _publisherManager.GetStudentById(id);
try
{
_studentManager.DeleteStudent(student);
}
catch (Exception ex) {
// Logging code
ViewBag.DeleteStudentError = "This student is currently acitve and cannot be deleted.";
// Code to get the VM same as in the Edit method and call the view with it
return View("AddEdit", studentVm);
}
// All well go to the Index view
return RedirectToAction("Index");
}
Not able to do
Show the message from ViewBag
The Url after the click says
Student/Delete/14
Instead of
Student/Edit?Id=14
Any clues?
Thanks in advance.
Your url will be always Student/Delete/14. If you want to change this to Edit you must redirect your action.
That is,
public ActionResult Delete(int id)
{
var student = _publisherManager.GetStudentById(id);
try
{
_studentManager.DeleteStudent(student);
}
catch (Exception ex) {
// Logging code
// You can't use ViewBag because we will redirect to another action.
//ViewBag.DeleteStudentError = "This student is currently acitve and cannot be deleted.";
// You can use TempData to pass parameter across actions
TempData["DeleteStudentError"] = "This student is currently acitve and cannot be deleted.";
// Code to get the VM same as in the Edit method and call the view with it
return RedirectToAction("Action_Name_Of_Edit",student);
}
// All well go to the Index view
return RedirectToAction("Index");
}
And you can use this TempData["DeleteStudentError"] in your AddEdit.cshtml.
Your view should be,
<span class="alert"> #(TempData["DeleteStudentError"]??string.Empty) <span>

Calling RedirectToAction

I have a form that calls this action to build the CompareEvents page:
[HttpPost]
public ActionResult CompareEvents(int[] EventsList, bool showIndex, bool showFRN, bool showProvider)
{
var viewModel = new EventsListViewModel
{
Events = EventsList,
ShowFRN = showFRN,
ShowIndex = showIndex,
ShowProvider = showProvider
};
return View(viewModel);
}
in the CompareEvents view there is another form that allows the user to update information:
[HttpPost]
public ActionResult UpdateSolution(IEnumerable<Solution> sol)
{
//update solution code
int[] eventList = { '85' };
return RedirectToAction("CompareEvents", new { EventsList = eventList, showIndex = true, showFRN = true, showProvider = true });
}
When this information is update, I would like to reload the page. I plan on doing this by calling the CompareEvents action again, however my stacktrace is saying that A public action method 'CompareEvents' was not found on controller
How can I accomplish this?
You cannot redirect to an action that is marked [HttpPost]. RedirectToAction uses a GET.
Source:
Returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action.
Reference.

MVC ASP.NET - Redirect to anchor with model state

The following code redirects to a view with the anchor and works. However I need to send the model state through for validation and I am not sure how to do that while using a redirect. I want to set the model error to populate the validation summary.
[HttpPost]
public ActionResult Send(QuoteModel model, string CatchAll)
{
try
{
if (ModelState.IsValid)
{
}
else
{
ModelState.AddModelError(string.Empty, "There is something wrong with Foo.");
return Redirect(Url.RouteUrl(new { controller = "Home", action = "Index"}) + "#quote");
}
}
catch
{
return View();
}
}
I think you can't do it this way, try to add an additional parameter for your action to determine if the error occurs
return Redirect(Url.RouteUrl(new { controller = "Home", action = "Index"}) + "?modelerror=true" + "#quote");
and check this url parameter in your action.

Rewriting the URL for a Controllers Action Method

I have a controller called Person and it has a post method called NameSearch.
This method returns RedirectToAction("Index"), or View("SearchResults"), or View("Details").
The url i get for all 3 possibilities are http://mysite.com/Person/NameSearch.
How would i change this to rewrite the urls to http://mysite.com/Person/Index for RedirectToAction("Index"), http://mysite.com/Person/SearchResults for View("SearchResults"), and http://mysite.com/Person/Details for View("Details").
Thanks in advance
I'm assuming your NameSearch function evaluates the result of a query and returns these results based on:
Is the query valid? If not, return to index.
Is there 0 or >1 persons in the result, if so send to Search Results
If there is exactly 1 person in the result, send to Details.
So, more of less your controller would look like:
public class PersonController
{
public ActionResult NameSearch(string name)
{
// Manage query?
if (string.IsNullOrEmpty(name))
return RedirectToAction("Index");
var result = GetResult(name);
var person = result.SingleOrDefault();
if (person == null)
return RedirectToAction("SearchResults", new { name });
return RedirectToAction("Details", new { id = person.Id });
}
public ActionResult SearchResults(string name)
{
var model = // Create model...
return View(model);
}
public ActionResult Details(int id)
{
var model= // Create model...
return View(model);
}
}
So, you would probably need to define routes such that:
routes.MapRoute(
"SearchResults",
"Person/SearchResults/{name}",
new { controller = "Person", action = "SearchResults" });
routes.MapRoute(
"Details",
"Person/Details/{id}",
new { controller = "Person", action = "Details" });
The Index action result will be handled by the default {controller}/{action}/{id} route.
That push you in the right direction?

ASP.NET MVC: Server Validation & Keeping URL paramters when returning the view

I currently have the following code for the POST to edit a customer note.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditNote(Note note)
{
if (ValidateNote(note))
{
_customerRepository.Save(note);
return RedirectToAction("Notes", "Customers", new { id = note.CustomerID.ToString() });
}
else
{
var _customer = _customerRepository.GetCustomer(new Customer() { CustomerID = Convert.ToInt32(note.CustomerID) });
var _notePriorities = _customerRepository.GetNotePriorities(new Paging(), new NotePriority() { NotePriorityActive = true });
IEnumerable<SelectListItem> _selectNotePriorities = from c in _notePriorities
select new SelectListItem
{
Text = c.NotePriorityName,
Value = c.NotePriorityID.ToString()
};
var viewState = new GenericViewState
{
Customer = _customer,
SelectNotePriorities = _selectNotePriorities
};
return View(viewState);
}
}
If Validation fails, I want it to render the EditNote view again but preserve the url parameters (NoteID and CustomerID) for something like this: "http://localhost:63137/Customers/EditNote/?NoteID=7&CustomerID=28"
Any ideas on how to accomplish this?
Thanks!
This action is hit by using a post. Wouldn't you want the params to come through as part of the form rather than in the url?
If you do want it, I suppose you could do a RedirectToAction to the edit GET action which contains the noteId and customerId. This would effectively make your action look like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditNote(Note note)
{
if (ValidateNote(note))
{
_customerRepository.Save(note);
return RedirectToAction("Notes", "Customers", new { id = note.CustomerID.ToString() });
}
//It's failed, so do a redirect to action. The EditNote action here would point to the original edit note url.
return RedirectToAction("EditNote", "Customers", new { id = note.CustomerID.ToString() });
}
The benefit of this is that you've removed the need to duplicate your code that gets the customer, notes and wotnot. The downside (although I can't see where it does it here) is that you're not returning validation failures.

Resources