Handling checkbox values being modified from 'true' - c#

During a scan of an application at work, it became clear that modifying an input's value, then posting it potentially causes rendering issues if you try to pass that model back to the view.
For example, with the model:
public class Survey
{
public bool LikesCandy { get; set; }
}
The actions:
public ActionResult Survey()
{
return View(new Survey());
}
[HttpPost]
public ActionResult Survey(Survey model)
{
//do something worthwhile with the model, like validation, etc. etc.
//even setting the model's boolean doesn't help the rendering.
model.LikesCandy = true;
//then return to the page
return View(model);
}
And a basic razor view:
#model BoolTest.Models.Survey
#using (Html.BeginForm())
{
#Html.EditorFor(i => i.LikesCandy)
<input type="submit" value="Submit"/>
}
The form gets rendered something like:
<form action="/Home/Survey" method="post">
<input checked="checked" id="LikesCandy" name="LikesCandy" type="checkbox" value="true">
<input name="LikesCandy" type="hidden" value="false">
<input type="submit" value="Submit">
</form>
Posting that works fine.
However, if someone changes the input value like so:
<form action="/Home/Survey" method="post">
<input checked="checked" id="LikesCandy" name="LikesCandy" type="checkbox" value="foobar">
<input name="LikesCandy" type="hidden" value="false">
<input type="submit" value="Submit">
</form>
The model looks reasonable on the server-side (it defaults LikesCandy to false), but the view rendering will always fail with String was not recognized as a valid Boolean..
Can someone explain why this is so? And is there some idiomatic way to fix/handle this? I can do something like use reflection to compare the model's property's types to the request form and give the user some "stop that!" message, or write my own html tags and manually bind the model to those, but neither seem very clean/extensible/correct.

Edit: As stated in the comments, the model binder is expecting a true or a false. If you submit something else for that field then a 500 error is the expected response. Also, if you change the value of the model during the post processing and then redisplay the view then you're not going to see the model change made in the controller reflected in the POST response. See here:
http://patrickdesjardins.com/blog/modelstate-clear-is-required-to-display-back-your-model-object
public ActionResult Survey()
{
return View(new Survey());
}
[HttpPost]
public ActionResult Survey(Survey model)
{
//do something worthwhile with the model, like validation, etc. etc.
//even setting the model's boolean doesn't help the rendering.
model.LikesCandy = true;
//clear model state so that the change on the line above is applied
ModelState.Clear();
//then return to the page
return View(model);
}

Related

Is it possible to populate a datamodel using a form and send it to the controller from Razorpage?

What is the best way to fill the field of a datamodel instance using a form on a Razorpage?
Is it possible to instantiate and submit a model directly from the Razorpage, or do i have to read each individual field of the form and assign them to a model instance in the controller?
Currently i am doing something like this: (not my actual code)
In the view:
<form method="post" action="some_action">
<input type="text" name="name" />
<input type="submit" value="submit" />
</form>
In the controller:
[HttpPost("some_action")]
Public IActionResult create_using_form()
{
person p = new person();
p.name = Request.Form["name"];
/*do whatever with person, like save to a db*/
return view();
}
I imagine there is a better solution, one where i dont have to populate every single field on my person object, one after the other. Any advice?
You don't need to deal with each field in the form separately. Just use strongly typed view by defining the data model by using #model.
Let say you have data model class DataModel.
Then short way:
public IActionResult Index()
{
return View(new DataModel());
}
[HttpPost("some_action")]
public IActionResult create_using_form(DataModel model)
{
if (ModelState.IsValid)
{
//...
}
return View("Index", model);
}
The Html.EditorForModel() helper was developed in ASP.NET Framework MVC, but it still has implementation in the ASP.NET Core MVC. The Html.EditorForModel() returns an HTML input element for each property in the model.
#model DataModel
<form method="post" action="some_action">
#Html.EditorForModel()
<input type="submit" value="OK" />
</form>

how to take input from TextBox and show in view in MVC

I want to show one TextBox. In that if give any input string and button clicked it should so like this
hai , what is ur name
[TextBox]
welcome,ur name is "xyz"
I am new in MVC. Please help me to do this.
View
#{
ViewBag.Title = "MyPage";
}
<h2>Mymethod</h2>
<h3>#ViewBag.Message</h3>
#Html.TextBox("Name")
<form method="post">
<input type="submit" value="Submit" name="btn" />
</form>
HomeController.cs
public ActionResult Mymethod()
{
ViewBag.Message = "Hello what is ur name ??? ";
return View();
}
There are many ways to do this to accomplish what you want. I will provide you with a simplistic approach, please modify and change it to fit in with your scenario.
I would normally recommend using a view model above any other way, for example using a single string value or using FormCollection or ViewBag. They can work but I prefer to use view models.
I answered a question on what view models are and what they are supposed to do, please read it:
What is ViewModel in MVC?
First you will create a view model that will handle your input data, like first name, last name, age, etc. You will then pass this view model through to the view. In your example I will only include name:
public class ViewModel
{
public string Name { get; set; }
}
In your Create action method you will instantiate this view model and pass it to the view. And when you click on the button to submit the form then the post action method will receive this view model as input parameter:
public ActionResult Create()
{
ViewModel model = new ViewModel();
return View(model);
}
[HttpPost]
public ActionResult Create(ViewModel model)
{
if (!ModelState.IsValid)
{
// If validation fails send the view model back to the view and fix any errors
return View(model);
}
// Do what you need to do here if there are no validation errors
// In your example we are posting back to the current view to
// display the welcome message
return View(model);
}
And then finally you view will look like this:
#model Project.Models.ViewModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.Name)
<button type="submit">Submit</button>
if (!string.IsNullOrWhiteSpace(Model.Name))
{
<p>welcome, your name is #Model.Name</p>
}
}
Please spend some time reading through the many online tutorials on ASP.NET MVC.
Modify your current view to
#using(Html.BeginForm("ControllerName","Mymethod",FormMethod.Post))
{
<input type="submit" value="Submit" name="btn" />
}
Add another action method in your controller like this :
[HttpPost]
public ActionResult Mymethod(FormCollection form)
{
string Name = form["Name"];
Viewbag.Name = Name;
return View()
}
Then add view to this controller and write this into it.
Hi , Your Name is #Viewbag.Name
You should wrap your form in form tag. It is a form after all. So when you click submit, you are submitting the form.
<form method="post">
<h2>Mymethod</h2>
<h3>#ViewBag.Message</h3>
#Html.TextBox("Name")
#if (!String.IsNullOrEmpty(ViewBag.Name))
{
<h3>
welcome,ur name is #ViewBag.Name
</h3>
}
<input type="submit" value="Submit" name="btn" />
</form>
On the controller, you need to add HttpPost handler for your Mymethod action. This is where your web server is accepting the form you've submitted.
[HttpPost]
public ActionResult Mymethod(string name)
{
ViewBag.Message = "Hello what is ur name ???";
ViewBag.Name = name;
return View();
}

Action method not being recognised when trying to insert data to database MVC4

I have an Action result method that is supposed to add data into a database when a button is clicked. Although, when I click on the button to add the data using the AddDetails Action method I get the following Error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /home/AddDetails
Controller:
namespace //not included
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Details()
{
return View();
}
[HttpPost]
public ActionResult AddDetails(Customer customer)
{
if (ModelState.IsValid)
{
var db = new CustomerContext();
db.Customers.Add(new Customer
{
ItemName = customer.ItemName,
Price = customer.Price
});
db.SaveChanges();
return RedirectToAction("Index");
}
return View(customer);
}
}
}
View:
#{
ViewBag.Title = "Details";
}
<h2>Add Items</h2>
<div class="container">
<form class="form-signin" role="form">
<h2>Please type in Item Name and price paid</h2>
<input type="text" class="form-control" placeholder="Name" required autofocus />
<input type="number" pattern="[0-9]+([\.|,][0-9]+)?" step="0.01" min="0" class="form-control" placeholder="Price" required autofocus />
<input type="button" class="btn btn-lg btn-primary btn-block" value="Add" onclick="location.href='#Url.Action("AddDetails", "home")'" />
</form>
</div>
You're posting to /Customers/Details but your action is /Customers/AddDetails. You can tell your form to specifically post to /Customers/AddDetails or you can rename your AddDetails action to Details. It's fine to have both a GET and POST action called Details. When you issue a post request, the action with the HttpPost attribute will be hit. When you issue a get request, the action with no attribute or a HttpGet attribute will be hit.
If you want to keep the name 'AddDetails', your form should look like this (I'm using Razor instead of plain HTML):
#using (Html.BeginForm("AddDetails", "Home", FormMethod.Post) {
<h2>Please type in Item Name and price paid</h2>
<input type="text" class="form-control" placeholder="Name" required autofocus />
<input type="number" pattern="[0-9]+([\.|,][0-9]+)?" step="0.01" min="0" class="form-control" placeholder="Price" required autofocus />
<input type="submit" class="btn btn-lg btn-primary btn-block" value="Add" />
}
I guess, I don't type fast enough... As Neil mentioned, use the Html Helper, though I recommend using named parameters so that you don't confuse which is the Action and Controller:
#using (Html.BeginForm(
actionName: "AddDetails",
controllerName: "Customer",
method: FormMethod.Post,
htmlAttributes: new { #class = "myclass" }))
Also, to help keep the routes correct (and not need to rely on "magic strings"), I highly recommend using T4MVC. Then your form will look like this:
#using (Html.BeginForm(MVC.Customer.AddDetails(), ...)
And the Razor view will not even render if the Action does not exist. It is also easier writing the form because Intellisense will help you find the Controller and Action you want.
Make These changes in your code.
namespace //not included
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult AddDetails()
{
Customer newCustomer = new Customer();
return View(newCustomer);
//right click this action and choose addview and use strongly typed
//view by choosing Customer class
}
[HttpPost]
public ActionResult AddDetails(Customer customer)
{
//whatever you want here
}
}
}
You will get a view with all the syntax for form and input textBoxes. Just few minor additions to meet your UI requirement will be needed.
Feel free to ask more doubts if any.
You need to give Action name in Html.BeginForm like this #using (Html.BeginForm("AddDetails", "Home", FormMethod.Post)
Or
change you controller action name to Details instead of AddDetails

How to pass a value from TextBox as parameter to Action

This is what I've had in mind but of course it doesn't work.
#{
var textBoxData = form.find('input[name="textboxList"]').val();
}
<input type="button" value="Add" title="Add" onclick="location.href='#Url.Action("Create_Add", "Controller", new { textboxList = textBoxData })'" />
How should I pass this? Controller action name and parameter are correct. Just that I don't know how to get the value entered in textbox...
I have trouble with saving a form within a form, so someone suggested this solution. Proxy code would be:
<firstForm>
textboxfor Name
dropdownfor DType
If DTypeDDL value is "List" then
<secondForm>
textboxfor nameOfItem
submitSecondForm (using that method i mentioned above)
</secondForm>
End If
submitFirstForm
</firstForm>
I've been trying to save 2 forms for quite a while now but no luck. This is basically my last resort.
First of all, you should go with a viewmodel oriented html file since you are using MVC (Model, View, Controller):
Create a viewModel:
public class ExampleViewModel
{
public ExampleViewModel()
{
}
public virtual string TextBoxData { get; set; }
}
After, code your html using the viewmodel as model:
#model Models.Views.ExampleViewModel
#using (Html.BeginForm())
{
<div class="editor-row">
<div class="editor-label">
#Html.LabelFor(model => model.TextBoxData)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TextBoxData)
</div>
</div>
<input type="submit" value="submit" />
}
and your controller:
public ActionResult Example()
{
ExampleViewModel model = new ExampleViewModel();
return This.View(model);
}
[HttpPost]
public ActionResult Example(ExampleViewModel model)
{
string infoEntered = model.TextBoxData;
// Do something with infoEntered
}
Hope this will help you!
If you're using view models, check out this answer: MVC sending data from View to Controller
If you're only interested in sending the data from an input to the action method without view models, you can do that as well:
View:
#using (Html.BeginForm("Edit", "Some", FormMethod.Post))
{
<input type="text" id="myTextBox" name="myTextBox" />
<input type="submit" value="Submit" />
}
Notice the BeginForm line. The first parameter is the Action I want the data to go to, which I named Edit. The next parameter is the Controller I am using, which I named SomeController. You don't add the Controller bit to the name when you're referencing the Controller in BeginForm. The third parameter is telling the form to use the POST method when sending the data to the server.
Controller:
public class SomeController
{
[HttpPost]
public ActionResult Edit(string myTextBox)
{
// Do what you want with your data here.
}
}
If you added more inputs (again, without a view model here), you can add them as parameters to the Edit method. This isn't really the preferred method, though. Look into using a view model. ScottGu has a nice blog post on doing what you need, using view models:
http://weblogs.asp.net/scottgu/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx

Save and cancel button posting the form

I am having 2 buttons namely Save and Cancel on my Profile.cshtml. Now my method is something like:
[HttpPost]
public ActionResult Profile()
{
//code..
}
This save the records into database. But problem is because Cancel also posts here, the record is saved in database anyway. How do I handle this?
In the view you could have 2 forms (one for each button), where the action of each form posts to the relevant action method in the controller. For example, in the view:
#using (Html.BeginForm("Save", "Profile"))
{
<input type="submit" value="Save"/>
}
#using (Html.BeginForm("Cancel", "Profile"))
{
<input type="submit" value="Cancel"/>
}
Then in the controller:
[HttpPost]
public ActionResult Save()
{
...
}
[HttpPost]
public ActionResult Cancel()
{
...
}
I often use a viewmodel class passed to a view, and in that viewmodel I have:
public string submit { get; set; }
public bool SaveButtonClicked()
{
return this.submit.Equals("save");
}
public bool CancelButtonClicked()
{
return this.submit.Equals("cancel");
}
Then in the controller
[HttpPost]
public ActionResult Save(MyViewModel inputViewModel)
{
if(inputViewModel.SaveButtonClicked()) {}
blah.......
}
My view looks like this
#using (Html.BeginForm())
{
// Other stuff.
<input type="submit" name="submit" value="Save" />
<input type="submit" name="submit" value="Cancel" />
}
This works really well for me. By having the submit buttons use the same name, when either one is pressed, that name is passed to the submit property of the viewmodel. Not sure if having duplicate names is a bad idea, but for my needs it's fine.
This sounds like your cancel button has the type attribute set to submit. This will make the form submit as though you were pressing a save button.
The question you should ask yourself is what do you want the 'Cancel' button to do? It may be better to make a button that goes to another page if it's just a case of not finishing the form, otherwise if you need to point it towards an ActionResult then you will have to do a redirect for that to work.
Feel free to ask any questions if you need.
normaly you have the Save-button to submit your form and the cancel-"button" is just an ActionLink pointing back to your index or whatever.

Categories