Model Binding

What's Model Binding? (strong type view)

Model bindingis the process of creating .NET objects using the data sent by the browser in an HTTP request. We have been relying on the model binding process each time we have defined an action method that takes a parameter—the parameter objects are created by model binding.

 

 

Notes:

1. about naming, the action method's parameter name(e.g. id) should be well-defined. because model binder will search to generate the parameter based on the parameter name and the pre-defined search list. otherwise, it may cause call fail, as binder could not find a proper name.

1. Request.Form["id"]
2. RouteData.Values["id"]
3. Request.QueryString["id"]
4. Request.Files["id"]

 2. be careful about the culture's impact on some data type, like datatime.

Binding to Simple Types 

be careful about the actionmethod's parameter and check

e.g.

method is action(int id)

1. if the URL is /Home/Controllor/Apple, then it could not convert to int type automatically, so it will return a failed page

2. if we change the method to action(int? id), but if in action method, we use id parameter, then it will return a failed page either.

3. if we give the parameter a default value, like action(int id=3), then it's ok.

4. if the URL is /Home/Controller/-1, it also pass the convertion, but it may not follow the business rules. so need to check in this case.

 

 

Binding to Complex Types (e.g. ActionResult ActionMethod(complex type parameter))

When the action method parameter is a complex type(i.e., any type which cannot be converted using the TypeConverterclass), then the DefaultModelBinderclass uses reflection to obtain the set of public properties and then binds to each of them in turn.

 

That is only property could be bound as property name is something like LastName, while varaiable name could not be like this.

 

in order for ModelBinder working, the property names have to be shown in the View file, while if there is nest complex type, e.g. one person class has another class object, Address, the address's properties also need to be shown. as the binder will search the property name from "FORM" to generate the proper complex object.

 

We have used the strongly-typed EditorForhelper method, and specified the properties we want to edit from the HomeAddress property. The helper

automatically sets the name attributes of the input elements to match the format that the default model binder uses, as follows: 

@Html.EditorFor(m=> m.HomeAddress.City

...
<input class="text-box single-line" id="HomeAddress_Country" name="HomeAddress.Country"
type="text" value="" />
...

 

Specifying Custom Prefixes 

Notes:

1. BeginFormhelper method could make the form is submitted back to the new DisplaySummaryaction method

@using(Html.BeginForm("DisplaySummary", "Home"))

 

if in the view file, we have something like summary.lastname property, and if we specify postback to new action method whose parameter is summary type, then by default, it will not generate summary type data. Because model binder will search the lastname in the form("lastname"), while in the form data, we only have form("summary.lastname"), so we need to add prefix in the new atcion method. like 

public ActionResult DisplaySummary([Bind(Prefix="HomeAddress")]AddressSummary summary) {
return View(summary);
}

 

Selectively Binding Properties

1.the country property value will not be generated, this is only applied for this action method.

public ActionResult DisplaySummary(
[Bind(Prefix="HomeAddress", Exclude="Country")]AddressSummary summary) {
return View(summary);
}

2. this indicates only city property will be generated, this is applied for anything using this class.

namespace MvcModels.Models {
[Bind(Include="City")]
public class AddressSummary {
public string City { get; set; }
public string Country { get; set; }
}
}

 

Manually Invoking Model Binding

The model binding process is performed automatically when an action method defines parameters, but we can take direct control of the process if we want to. This gives us more explicit control over how model objects are instantiated, where data values are obtained from, and how data parsing errors are handled(when disable client side validation, it may cause bind error).

public ActionResult Address(FormCollection formData//where data from, in this case, it is form) {
IList<AddressSummary> addresses = new List<AddressSummary>();  
try { //Handle error
UpdateModel(addresses, formData);
} catch (InvalidOperationException ex) {
// provide feedback to user
}
return View(addresses);
}

 

When model binding is invoked automatically, binding errors are not signaled with exceptions. Instead, we must check the result through the ModelState.IsValidproperty.

 

 

 Customizing the Model Binding System

----

 

 

MVC Model Binding Vulnerability

MVC Model Binding Vulnerability
Reference Link:
For example User Entity
public class User
{
    public string FirstName { get; set; }
    public bool IsAdmin { get; set; }
}
When you want to let a regular user change their first name, you give them the following form.
@using (Html.BeginForm()) {
   
     @Html.EditorFor(model => model.FirstName)
        
    
}
There is no input in the form to let a user set the IsAdmin flag, but this won't stop someone from crafting an HTTP request with IsAdmin in the query string or request body. Maybe they saw the "IsAdmin" name somewhere in a request displaying account details, or maybe they just got lucky and guessed the name.
composing the attack
If you use the MVC model binder with the above request and the previous model, then the model binder will happily move the IsAdmin value into the IsAdmin property of the model. Assuming you save the model values into a database, then any user can become an administrator by sending the right request. It's not enough to leave an IsAdmin input out of the edit form.
Fortunately, there are at least 6 different approaches you can use to remove the vulnerability. Some approaches are architectural, others just involve adding some metadata or using the right API.

Weakly Typed Approaches

The [Bind] attribute will let you specify the exact properties a model binder should include in binding (a whitelist).
[HttpPost]
public ViewResult Edit([Bind(Include = "FirstName")] User user)
{
    // ...
}
Alternatively, you could use a blacklist approach by setting the Exclude parameter on the attribute.
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
    // ...
}
If you prefer explicit binding with the UpdateModel and TryUpdateModel API, then these methods also support whitelist and blacklist parameters.
[HttpPost]
public ViewResult Edit()
{
    var user = new User();
    TryUpdateModel(user, includeProperties: new[] { "FirstName" });
    // ...
}

Strongly Typed Approaches

TryUpdateModel will take a generic type parameter.  You can use the generic type parameter and an interface definition to restrict the model binder to a subset of properties.
[HttpPost]
public ViewResult Edit()
{
    var user = new User();
    TryUpdateModel<IUserInputModel>(user);

    return View("detail", user);
}
This assumes your interface definition looks like the following.
public interface IUserInputModel
{
    string FirstName { get; set; }
}
Of course, the model will also have to implement the interface.
public class User : IUserInputModel
{
    public string FirstName { get; set; }
    public bool IsAdmin { get; set; }
}
There is also a [ReadOnly] attribute the model binder will respect. ReadOnly metadata might be want you want to use if you never want to bind the IsAdmin property. (Note: I remember ReadOnly not working in MVC 2 or MVC 1, but it is working in 3 & 4 (beta)).
public class User 
{
    public string FirstName { get; set; }

    [ReadOnly(true)]
    public bool IsAdmin { get; set; }
}

An Architectural Approach

Put user input into a model designed for user input only.
public class UserInputViewModel
{
    public string FirstName { get; set; }
}
In this approach you'll never bind against business objects or entities, and you'll only have properties available for the input you expect. Once the model is validated you can move values from the input model to the object you use in the next layer of software.
Based upon our convenience we can choose the approach.

 

posted @ 2014-12-26 05:20  ruri  阅读(108)  评论(0)    收藏  举报