I have a TextArea in my strongly typed View defined with
#Html.TextAreaFor(x => x.Text)
My controller Action originally looked similar to this:
[HttpPost]
public ViewResult Index(MyViewModel vm)
{
using (var db = new MyEntities())
{
Post p = new Post();
p.Text = vm.Text;
db.Posts.AddObject(p);
db.SaveChanges();
}
return View();
}
This worked fine. The text the user entered into the TextArea was passed into the controller and saved into the Post table in SQL Server via Entity Framework. The data type for this field is varchar(1000). (There is also Model validation on the Text field using MetadataType with [StringLength(1000)] validation.)
I noticed that if I tried to copy some HTML source and paste it into the TextArea and submit it, I received the following error:
"A potentially dangerous Request.Form value was detected from the client"
The error led me to this question, and the take away from there was that I could simply add
[HttpPost, ValidateInput(false)]
to my Action to stop that type of validation. This worked wonderfully, but to my surpise, no matter what I tried putting into the TextArea, I couldn't cause any problems. If I paste in javascript, html or T-SQL statements riddled with quote characters, it still works fine. I see the exact characters I entered into the TextArea appearing in the SQL table, and if I display the text back to the View, I see in the source each character is converted to the HTML counterpart, and the display on the screen looks just like it did when I entered it in. I did not do any sort of text conversion to accomplish this. It seems by default everything is working exactly as I want it to. Of course I'm glad for this, but when I read about disabling the validation, it is often followed with a warning that you should understand the consequences of doing this, and I don't think I do. So I wonder, what are the consequences? Is there anything someone could possibly type into my TextArea that could mess things up as a result of disabling the input validation?
In case it's relevant, my particular setup is MVC4, .NET 4.0, Entity Framework 4.4, SQL Server 2012 Express.
If you are using razor any text that you output will automatically be encoded which will appear as text in the browser but not be interpreted as javascript etc.
If you are turning validation off you have to be very careful to ensure that you are encoding all user input whereever you display it so that you dont inadvertently run some javascript on your page due to somethign a user entered (look at XSS for some examples).
You can test quickly (althought not an exhaustive search) by adding some kind of javascript alert('hello') call to various varchar fields in your database and see if it gets called when you visit the page.
Also even if you arent displaying the user data it may have implications depending on how you do your data access.
Even if you use something like entity framework you are not protected from sql injection for instance if you used stored procedures and did not do validation checking on the input. See Troy Hunt's article on this
Related
I'm a just starting to learn Play, so this is a basic question, but I've searched every term I can think of and can't find the answer.
All I want to do is have a page that on submit takes an ID from a text field and puts it in the URL directly (e.g. /myservice/person/123). Instead, the URL being generated contains the ID as a parameter (e.g. /myservice/person?id=123).
I know my controller is being invoked correctly if I type the URL in by hand, so I'm inclined to think my routes file is correct. This is what my entry looks like:
GET /person/:id controllers.PersonActions.getPerson(id: String)
So I'm assuming something is going wrong in my template, which looks like this:
#form(routes.PersonActions.getPerson(personID)) {
#* #inputText(personForm("id")) *#
<input type="text" name="id" value="#personID">
<input type="submit" value="Get">
}
You can see that I've commented out another way using #inputText also, but it behaves identically for me. I'm not tied to either method.
I've also tried this using POST, which removes the ID from the URL entirely, which I don't understand either, but since I'm just doing a simple query, I'd rather use GET.
Can anyone help me understand what's going on here? I feel like there's something fundamental about how routing/URLgeneration works that I'm not understanding, but I've gone through the tutorial and the docs so many times today I'm at a loss.
Thanks in advance.
Oh, and I'm using Java 7, Play 2.1, and Eclipse Kepler
I think you are trying to skip a step and map form request data from a view directly to a controller method. You will need to submit the form, have a controller parse the data and then render the next appropriate view based on the data you parsed from the form.
Remember that the personId in your view is a parameter that is bound server-side when the view is rendered. In this case, the submit url of the form is hard coded to whatever personId is passed in to the view at render time -- it doesn't change dynamically when the input box changes.
To fix, add a controller method to accept requests from /person (I'm guessing this based on the part in your question that says the form is being submitted to /person?id=123, in any case it should be the URL of the form you've shown above)
e.g. if you want to use a GET method for the form add:
GET /person controllers.PersonActions.findPerson(id: String)
and in your PersonActions controller (I'm assuming you're using Java, if scala I'm sure you can adapt it from the docs)
public static Result findPerson(String id){
/*
* I'm assuming this method exists and works because you say
* when you type in /person/123 manually the routing works
* and your router says getPerson is the method name for that URL.
*/
return getPerson(id);
}
I've understood that a viewmodel in MVC is supposed to reflect data on a single page rather than objects in the model. But should the viewmodel correspond to the data you want to show on that page or to the data you want back from that page? If we for example look at a login page then I just want username and password back in the post, but I might need more variables than that when displaying the login page (previous error messages etc).
Should the viewmodel then just contain username and password as parameters and the rest of the variables end up in viewbags. Or should the viewmodel contain all values I want to show even though I'm only interested in a few of them in the response.
What is best practice when using viewmodels?
All data that somehow interacts between the html and your server should be in a ViewModel.
This allows you to perform formatting and such outside your html and inside your ViewModel properties.
However, if your page contains a lot of controls or data, you may want to split it into multiple ViewModels (example one for the Get and one for the Post).
The post model may contain only data that you entered and needs to be validated.
I think it's best to put everything in the view-model. This keeps the code cleaner and makes discovery and maintenance easier as well. The view-model should be your primary mechanism here.
I would say only properties you need, in your case username and password. If you want to display error messages then that's what ModelState is for. You can always append any error messages to your ModelState:
ModelState.AddModelError("PropertyName", "Error Text")
Beyond that let's say you have a form that contains a list of categories that you need to pick one category from a drop down. In this case I usually attach that list to my model even though the only thing being submitted is the actual selected value. But this is a matter of preference, meaning I could also set a ViewBag to contain this SelectList of categories and then bind that to your DropDownList. I suppose it's better to place this in a model because ViewBag is dynamic and you will have to cast anything in the ViewBag into it's underlying type on your views.
I'm trying to use Infragistics' grid to display a list of items from my Database. I'm using code-first method with Entity Framework in an MVC application with Razor engine. Every thing is working fine in the view except the Infragistics grid.
Here is my home view:
#using Infragistics.Web.Mvc
#model IEnumerable<BusinessModel.Models.TestPlan>
#{
ViewBag.Title = "Home";
}
#( Html.Infragistics().Grid<BusinessModel.Models.TestPlan>(Model)
.AutoGenerateColumns(true)
.DataSourceUrl(Url.Action("igListTestPlan"))
.DataBind()
.Render())
Here is my controller:
[GridDataSourceAction]
public ActionResult igListTestPlan()
{
return View(service.getListTestPlan());
}
Using firebug I can clearly see that the request is sent with a status code "200 OK", but the response tab is empty. It also causes an error in the console (in infragistics.js):
Uncaught TypeError: Cannot read property 'length' of undefined
I guess it's because of the empty response.
What I tried:
Debugging my controller showed me that return View(service.getListTestPlan()); doesn't return an empty list: I have 3 valid items in.
I also tried Html.Infragistics().Grid<BusinessModel.Models.TestPlan>(Model__.ToList()) but nothing changed. Also Html.Infragistics().Grid(Model) tells me I've got invalid parameters
Thanks in advance.
I think I have a pretty good idea why you are getting this, happened to me as well.
The MVC wrappers provide defaults for the way the grid model handles data on the server (serializing the data source to an object with 'Records' of your data and supportive 'Metadata'). However, if you do that yourself since you don't define a key of your own you are stuck with a default key 'Records' that is used to filter the response and since it's not there..well you get 'undefined' data fed to the grid :)
So solutions:
1) Wrap your response and define matching key using the "ResponseDataKey" property of the grid. I am suggesting this because as far as I recall it's a good practice to wrap responses in a single object - think there were some security implications.
2) If you don't feel like doing this and just want to get it working now then set the "ResponseDataKey" to empty string ("" will do) so your response will be properly filtered(or rather not).
On the second part of binding the grid directly to model data in the view - you are correctly getting the error as far as I see. The DataSource property explicitly states the source must implement IQueryable instead of IEnumerable. Slap a .AsQueryable() in there and that should work fine as well.
Let me know if this helps :)
Can you give me any general advice on how to debug ASP.NET MVC Binding?
When everything works as expected, ASP.NET MVC is great. But if something does not, like something doesn't bind for some unknown reason, I find it hard to trace down the problem and find myself spending hours tracking down a seemingly simple problem.
Let's imagine you land in a controller method like this:
[HttpPost]
public ActionResult ShipmentDetails(Order order)
{
//do stuff
}
Let's further imagine that the Order class looks like this:
public class Order
{
public decimal Total {get; set;}
public Customer Customer {get; set;}
}
public class Customer
{
public string Name {get; set;}
public string Phone {get; set;}
}
What are good places to start when Order in the controller method is not bound correctly? What are good places to start when only parts of the Order are bound correctly?
Although #russ's answer is useful and will sometimes be necessary, both options seem a little low level when the main question is more about the big picture. Thus, I'd recommend Glimpse.
From its about page:
… Glimpse allows you to debug your web site or web service right in the browser. Glimpse allows you to "Glimpse" into what's going on in your web server. In other words what Firebug is to debugging your client side code, Glimpse is to debugging your server within the client.
And since you've specifically asked about data binding, you'll want to look at the binding tab documentation. You'll be able to see, again from the docs:
Ordinal: Order in which the MVC Model Binding infrastructure attempted to bind the available data
Model Binder: Model Binder that was used in a given scenario
Property/Parameter: Name of the thing that the Binder was trying to bind
Type: Type of the thing that the Binder was trying to bind
Attempted Value Providers: Providers that the Binder attempted to use to get a given value (and whether it was successful)
Attempted Value: The actual value that the provider has to work with (post type conversation, etc.)
Culture: The culture that was used to parse the raw value
Raw Value: The raw value that the provider has to work with (pre type conversation, etc.)
See the quick start. Briefly:
Install the glimpse.mvc3 package
Go to http://yourhost/yourapp/Glimpse.axd and "turn it on."
Click on the glimpse icon on the bottom right of any view in your app for details.
As Darin has suggested, start with inspecting what is being sent from the client to the server using something like Firebug, Fiddler, or other web debugging proxy tool.
Failing that, you might want to step through the source code to see what's happening during binding.
Two ways that I can recommend doing this are
Include the System.Web.Mvc source code project in your application and reference this. This is good for learning but probably not recommended for a commerical application.
Download the symbols for System.Web.Mvc from the Microsoft Symbol servers, change your settings to be able to debug framework source code and set a break point appropriately to step through.
In my case looking at the ModelState property in the controller method provided the answers why the modelbinding was failing.
A good place to start is download and install FireBug and see what gets posted from the client to the server. Then you will see what's missing, incorrect, ... Blog posts such as Model Binding to a List are good reads as well to get acquainted with the proper syntax that the default model binder uses.
From Visual Studio side:
Set a breakpoint on entry to your endpoint.
Open the Immediate via the Debug menu option.
Type in ModelState.Keys.ToList()
This will show the binding errors by name/key.
Better yet type in ModelState.Values.ToList()...
Put a breakpoint in your controller-method and make a watch for Request.Params to see what is actually coming in to the controller in terms och parameters.
Let's say you have simple object like this :
public class MyObject {
public int Test { get; set; }
}
And you count on the default model binder to make sure the user does not leave the "Test" field empty when posting a form like below :
<form method="post" action="/test">
<p>
<%=Html.TextBox("Test") %>
<%=Html.ValidationMessage("Test") %>
</p>
<input id="Submit1" type="submit" value="submit" />
</form>
And this action :
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Test(MyObject o) {
return View();
}
This all works as expected when the form data contains a key for "Test" (like "Test=val" or "Test=")
But if the key is not in the form data, then the validation doesn't occur. So in case of an empty post request or a request with a data like AnotherField=foo the property on the model object defaults to the type's default value (in this case 0). And ModelState.IsValid returns true.
This is, IMO, not the behaviour one would expect.
So what do you suggest to change this behaviour?
Edit :
Keep in mind that a malicious user can just tamper the form data easily with FireBug or Tamper Data plugin to pass the default model binder's validations, which could cause some security problems.
It is not validating the fields by design, unfortunately. Please take a look at this Codeplex issue that I've found and commented on.
It is really unfortunate, that MS has decided this is the correct behaviour for their model binder.
I believe the default model binder should validate the whole entity instead of just the posted data.
You might consider using xVal to perform the necessary mix of client- and server-side validation. With it, you can add an attribute to your Test property dictating what sort of validation rules (required, regex validation, etc.) apply to it and then, with a little more work, get it to generate JavaScript rules for you as well as pretty easily perform model validation.
You're already aware that you're violating a rule of development - never trust client input. There's really no way around it.
Client-side validation (preventing a round trip to find out that there's an error) is nice-to-have, server-side validation verifying that things truly are in order is a must-have.
i hope its by design because I am about to start relying on this behavior!
in one instance i have two controls which post back the following fields :
ShippingAddress.FirstName
ShippingAddress.LastName
ShippingAddress.Line1
ShippingAddress.Line2
BillingAddress.Email
BillingAddress.FirstName
BillingAddress.LastName
BillingAddress.Line1
BillingAddress.Line2
Note: Billing Address does not have email displayed, even though the underlying model of course still has it
If I have Email as a [Required] data annotation in my model then it will only complain when it is missing from Billing Address.
But I definitely see your point! I'm just relying on it for this one scenario here!
After researching all the different ways to get the form values into my controller methods, I discovered that the "safest" was to explicitly define each form field as a parameter to the controller. A side effect is that if a form does NOT have one of the fields, it will throw an exception - which would solve your problem.