MVC ASP.NET MVC3 AllowHtml Attribute Not Working? - asp.net-mvc-3

The question is very simple:
Say you have a model called Person
public class Person
{
public int PersonID {get; set;}
public string Name {get; set;}
[AllowHtml] // Allow html in Intro property
public string Intro {get; set;}
[ScaffoldColumn(false)]
public string ComplicatedValue {get; set;}
}
In controller's Create action
[HttpPost]
public ActionResult Create(Person o, FormCollection collection)
{
// whatever code here;
}
If you run it,
input plain text for Intro, no
problem happens.
input html content for Intro, no matter how you set
your configuration file, it will
tells "A potential dangerous ..."
I DO find the reason of this problem.
If you change the function to
public ActionResult Create(Person o) // Get rid of the *FormCollection collection*
{
// whatever code here;
}
This will eliminate the "potential dangerous" error.
But my problem is that for my application, I have to use the secondary parameter FormCollection collection in the Create Action method, because I need to use some other control values and server variable to assign a calculated value to the ComplicatedValue property.
If any expert of ASP.NET MVC3 have met the same problem as me, and found a solution, please kindly let me know.

This forum at this link discusses this issue at length and gives some workarounds.
http://forums.asp.net/p/1621677/4161637.aspx
Here is one solution from that thread that may or may not work for you:
public ActionResult Create(Person o) // Get rid of the *FormCollection collection*
{
FormCollection form = new FormCollection(Request.Unvalidated().Form);
// whatever code here;
}
or my own recommendation:
public ActionResult Create(Person o, int otherControlValue1, int otherControlValue2, ...)
{
o.ComplicatedValue = CalculateComplicatedValue(otherControlValue1, otherControlValue2, ...);
// whatever code here.
}
In my case, I am not using the FormCollection, but it was there so that I had a different footprint on my [HttpPost] method. I did this hack and put in a bogus parameter:
public virtual ActionResult Edit(int id)
{
return View(this.repository.GetById(id));
}
[HttpPost]
public virtual ActionResult Edit(int id, int? bogusID)
{
var d = repository.GetById(id);
if (TryUpdateModel(d))
{
repository.Save();
return RedirectToAction("Index");
}
return View();
}

Might I suggest using a custom model binder instead of pulling the complex data from a FormCollection. Scott Hanselman has a blog post on creating a custom model binder that would serve as a good template. In his post he puts together a DateTimeModelBinder that allows a DateTime property to be set either by a single input containing the date or a pair of inputs containing a date and a time.

have you tried
[Bind(Exclude="ComplicatedValue")]
:
[HttpPost]
public ActionResult Create([Bind(Exclude="ComplicatedValue")]Person o)
{
}
?
with that it allows you to exclude setting ComplicatedValue property on the form and still submit the object as a Person class.
hope that helps

Related

ASP.NET Web API - how to pass unknown number of form-encoded POST values

The front-end of my application can send unknown number of POST values inside a form. Fro example in some cases there will be 3 values coming from certain textboxes, in some cases there will be 6 values coming from textboxes, dropdowns etc. The backend is ASP.NET Web API. I know that a simple .NET value can be passed in URI parameter to a "POST Action" using FromURI attribute and a complex type can be passed in body and fetched using FromBody attribute, in any POST Action. But in my case the number of form data values will NOT be constant rather variable and I can't use a pre-defined class to hold values using 'FromBody' attribute.
How can I tackle this situation?
You can use the FormDataCollection from the System.Net.Http.Formatting namespace.
public class ApiFormsController : ApiController
{
[HttpPost]
public IHttpActionResult PostForm(FormDataCollection form)
{
NameValueCollection items = form.ReadAsNameValueCollection();
foreach (string key in items.AllKeys)
{
string name = key;
string val = items[key];
}
return Ok();
}
}
Try to send this properties as list of properties. Make model something like this:
public class PostModel
{
public IEnumerable<PropertyModel> Properties { get; set; }
}
public class PropertyModel
{
public string Value { get; set; }
public string Source { get; set; }
// etc.
}
And action:
public IHttpActionResult Post(PostModel model)
{
//Omited
return Ok();
}

Preventing tampering of form fields in ASP.NET MVC EF

The default strongly-typed Edit page in ASP.NET MVC 3 generally exposes all fields for the Entity. While this is often workable, some fields are a security risk. For example a simplified magazine subscription entity might look like:
public void Subscription() {
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public DateTime SubscribedThru { get; set; }
}
If I provide an Edit page to let users change their own address, for example, it's a security risk to include the SubscribedThru field because a knowledgeable and malicious user could give themselves a free 10-year subscription by faking the date (even if I use #Html.HiddenFor(model => model.SubscribedThru). So I am not including that field in any way on the Edit page html (via razor).
I thought the answer might be to prevent binding attempts on SubscribedThru on the Edit method in the controller using something like:
[HttpPost]
public ActionResult Edit([Bind(Exclude="SubscribedThru")] Subscription subscription) {
if (ModelState.IsValid) {
db.Entry(subscription).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
return View(subscription);
}
When I get to the SaveChanges(); line, it throws the error The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value. I believe that the SubscribedThru date (properly?) doesn't exist, and the empty value is less than SQL Server can handle. What surprises me is that it's even trying to update that field when I have Binding excluded for it.
So far my best solution seems to be to create a custom ViewModel that omits the SubscribedThru date, but that seems a lot of duplication of fields, validation, etc.; if possible I'd like to just make the one field SubscribedThru safe from user editing.
I can't say I fully understand the UpdateModel and TryUpdateModel methods and wonder if that's a direction to head? I played with them and EF throws errors for having duplicate objects (same key) which is perplexing.
Also, I'm not clear if the subscription data is preserved from the initial load in public ActionResult Edit(int id) in the controller all the way to the final [HttpPost]
public ActionResult Edit(Subscription subscription)... method, or does the line db.Entry(subscription).State = EntityState.Modified; try and set all the data (I thought it was just setting a flag indicating "edited-so-EF-should-save-this").
I'm a long-time .NET developer, just jumping in to my first ASP.NET MVC project, so I'm probably overlooking something painfully obvious. Thanks for any help!
So far my best solution seems to be to create a custom ViewModel that omits the SubscribedThru date, but that seems a lot of duplication of fields, validation, etc.;
That is exactly what you should do to keep things neat & tidy. AutoMapper eases the ViewModel variation headache.
This page contains an example of updating a model using TryUpdateModel (Listing 4):
http://www.asp.net/mvc/tutorials/older-versions/models-(data)/creating-model-classes-with-the-entity-framework-cs
You can whitelist only the fields that you allow to be edited, which removes the security risk.

Ignoring properties when serializing

I'm pulling my hair out on this one.
I am trying to implement a multi-step wizard, and i'm using the Html.Serialize html helper in MVC3 Futures. This works well, except one of the properties in my model is a SelectList. I don't want this property serialized (and it blows up when it tries anyways).
I can't use [NonSerialized] because that only works on fields, not properties. I've even tried some of the other normal ways such as [XmlIgnore] (which I didn't think would work anyways).
Can anyone suggest an attribute that will ignore a property in a model when using Html.Serialize?
EDIT:
The error I get when I try to serialize is a InvalidDataContractException. There is this message:
Type 'System.Web.Mvc.SelectList' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
However, if I do this then I have to mark all the members with [DataMember] just to exclude 1 property, which seems kind of stupid.
UPDATE:
A quick example of this is this bit of code (make sure to add reference to System.Runtime.Serialization.dll):
Test.cs
[Serializable]
public class Test
{
public int ID { get; set; }
[IgnoreDataMember]
public SelectList TestList { get; set; }
}
HomeController.cs
public ActionResult About()
{
return View(new Test() { ID = 0, TestList = new SelectList(new [] {""})});
}
Home/About.cshtml
#using Microsoft.Web.Mvc
#model MvcApplication3.Models.Test
#Html.Serialize("Test", Model)
This generates the InvalidDataContractException
public class MyViewModel
{
[IgnoreDataMember]
public SelectList Items { get; set; }
...
}
or simply:
public class MyViewModel
{
public IEnumerable<SelectListItem> Items { get; set; }
...
}

Is there a way to pass back the Request.Files in the view model on post (ASP.NET MVC 3)?

I've successfully removed references to Request.Form in my code, but I haven't seen any ASP.NET MVC 3 support for binding Request.Files to my view model. Would best practice be to just pass the Request.Files object (HttpFileCollectionBase) to whatever method processes my attachments? E.g.,
[HttpPost]
public ActionResult UpdateStatus(StatusViewModel vm)
{
bool updated = HandleUpdate(Request.Files, vm);
...
return View("Updated");
}
You can simply declare Action parameters of type HttpPostedFileBase, like so:
public ActionResult Method(HttpPostedFileBase fileUploaded)
You can bind to arrays of like-named instances, as well.
public ActionResult Method(HttpPostedFileBase[] filesUploaded)
and, HttpPostedFileBase can be a member of your view model.
public class MyModel{
public string Name{get; set;}
public HttpPostedFileBase FileUploaded{get; set;}
}

using MVC Model Binder, how to prevent binding inner complex object properties?

i have the following model
public class Person
{
public int Id {get;set;}
[Required()]
public string Name {get;set;}
[Required()]
public Address Address {get;set;}
}
public class Address
{
public int Id {get;set;}
[Required()]
public string City {get;set;}
[Required()]
public string Street {get;set;}
}
in the controller:
[HttpPost]
public ActionResult Create(Person entity)
{
if (ViewData.ModelState.IsValid)
{
///Some code
return this.RedirectToAction("Browse");
}
else
{
return View("Edit", ViewModel);
}
}
the problem is that the binder try to validate even the inner address class, but all i care for, is the AddressID
but the ModelBinder insist to validate even the City and Street properties.
how can i simply override the original ModelBinder just to validate the ID of the inner object (which is in my situation is AddressID)??
is there a simple way ?
It sounds like your entity and your model have two different requirements. If that is the case, then they should be two different classes. Write a separate Person and address class for MVC to bind to and don't have city or street be required.
Another possible, but less elegant solution, is to not rely on MVC doing the model binding. If you only have a handful of values that may be acceptable, but if you have a lot, then I would use my first suggestion.

Resources