Updating to ASP.NET MVC 2 Preview 2
New Features
Some of the new features of preview 2 have been discussed elsewhere, so I won't rehash them. But I've also noticed that there is a new attribute,
[RequireHttps]
, which does what you would expect, when added to an action, and a new HTML helper, Html.HttpMethodOverride
, which makes it easy to take a POST request and code as if the request were actually PUT or DELETE, by adding a hidden input containing a special value on the HTML form. This allows you to write your server in a more RESTful style, which will be suitable for user agents which know about the PUT and DELETE verbs, while maintaining compatibility with those (like browsers) which do not.MvcHtmlString
After installing the new assembly into the GAC, I attempted to compile our existing projects. I got a compilation error on some of our custom HTML helpers, as the MVC extension methods like
Html.RouteLink
have been changed to return an instance of type MvcHtmlString
instead of String
. In many cases, I could just change the return type. However, the clear intention of the framework designers is that any HTML helpers which return HTML (with angle brackets) instead of "plain" text should return a MvcHtmlString
instead of a String
. So, in addition to changing the return types of methods where necessary to get those methods to compile, I also wanted to change the return types of any custom HTML helper which returned HTML containing angle brackets. It makes no difference today, but in ASP.NET 4, there will be a new syntax to guard against XSS attacks, and it makes sense to get ready for this by returning the correct type.The trick here is that
MvcHtmlString
does not have a public constructor, so it's not immediately obvious how to create one of these things. Perusing the MVC source code, I noticed that it does have a static Create
method, and comments elsewhere in the source code indicate that this method should be used instead of the detected constructor. So you can now write a helper like this:
public static MvcHtmlString Fud(this HtmlHelper helper)
{
return MvcHtmlString.Create("<acronym title=\"Fear, Uncertainty, and Doubt\">FUD</acronym>");
}
JsonRequestBehavior
ASP.NET MVC will now, by default, throw an exception when an action attempts to return JSON in response to a GET request. Unless you read the release notes carefully, you won't discover this until runtime. This is in order to proactively defend against a particular cross-site attack. It is good to be safe by default, but you are only vulnerable to this attack under the following combination of circumstances:
- The data you're returning is worth stealing.
- The root data object in the JSON response is an array.
- The requesting browser is not IE 8, or some other browser which doesn't allow
__defineSetter__
It turns out that in our application we almost never return JSON results with the root object as an array in a GET. So the best fix was generally just to tell the framework that we have examined the risk and determined it returning JSON in this case, by changing code like:
return Json(model);
...to:
return Json(model, JsonRequestBehavior.AllowGet);
Unfortunately, I had to make this change in a lot of places. Still, I agree with the framework designers: Better safe than sorry.
JavaScript Files
The MicrosoftAjax.js, MicrosoftAjax.debug.js, MicrosoftMvcAjax.debug.js, and MicrosoftMvcAjax.debug.js files have all changed for ASP.NET MVC 2. Existing ASP.NET MVC 1.0 projects will have old versions of these files.
ModelBindingContext Changes
One of the new features of preview 2 is client-side validation and custom metadata providers for validations. In order to implement this feature, some of the implementation details of model binding have changed. This won't affect most people, but the fix was a little tricky, and required a good bit of examination of the MVC source code, so I'll list it anyway. Prior to preview 2, I had code in a unit test for a custom model binder like this:
internal static T Bind<T>(string prefix, FormCollection collection, ModelStateDictionary modelState) where T:class
{
var mbc = new ModelBindingContext()
{
ModelName = prefix,
ModelState = modelState,
ModelType = typeof(T),
ValueProvider = collection.ToValueProvider()
};
IModelBinder binder = new MyModelBinder();
var cc = new ControllerContext();
return binder.BindModel(cc, mbc) as T;
}
With Preview 2, on the other hand, I have to do this:
internal static T Bind<T>(string prefix, FormCollection collection, ModelStateDictionary modelState) where T:class
{
var mbc = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(T)),
ModelName = prefix,
ModelState = modelState,
ValueProvider = collection.ToValueProvider()
};
IModelBinder binder = new MyModelBinder();
var cc = new ControllerContext();
return binder.BindModel(cc, mbc) as T;
}
Not hard once you know the trick.

Comments
-
Dick Norman Wednesday, 18 November 2009
Craig,
First, Kudos on your blog. I found it about the first day you started your series on MVC/JSON/JqGrid and you are one of the best of the best.
The only thing I didn't like about your solution was the label redundancy in JSON:
{"Total":1,"Page":1,"Records":2,"Rows":[{"ID":3,"BC_LASTNAME":"Ajax","BC_FIRSTNAME":"Mike","BC_MIDDLEINITIAL"
:null},{"ID":1,"BC_LASTNAME":"Gutierrez","BC_FIRSTNAME":"Sandy","BC_MIDDLEINITIAL":null}],"UserData"
:null}
I've been very effective with essentially my own formatted JSON using the following, based upon your original guidance:
JsonResult jres = JsonHelper.ToJsonResult(contacts, totalPages, page, totalRecords, "ID", new string[] { "ID", "BC_LASTNAME", "BC_FIRSTNAME", "BC_MIDDLEINITIAL" });
return jres;
My solution generates the column names only once and works great. Then comes MVC 2 with JsonRequestBehavior.AllowGet. Since it isn't built technically by Json, there doesn't appear to be a way to use the JsonRequestBehavior, yet it still fails with the JsonRequestBehavior error on VS2010.
I haven't found a way to marry the two approaches. Any ideas? -
Dick Norman Thursday, 19 November 2009
Craig,
Yet another twist. The pre-2.0 data looks like this:
{"total":1,"page":1,"records":2,"rows":[{"id":"3","cell":["3","Ajax","Mike"," "]},{"id":"1","cell":["1","Gutierrez","Sandy"," "]}]}
Now jqGrid chokes - presumably because of the ContentEncoding and ContentType, though the JsonRequestBehaviour passed as expected:
{"ContentEncoding":null,"ContentType":null,"Data":{"total":1,"page":1,"records":2,"rows":[{"id":"3","cell"
:["3","Ajax","Mike"," "]},{"id":"1","cell":["1","Gutierrez","Sandy"," "]}]},"JsonRequestBehavior"
:1} -
firefly Monday, 18 January 2010
Craig,
Just to verify the Json data that you showed on your jqgrid demo is an array right?
Response: No, it's one object which contains an array as a property. Did you look at it?
In another word it might be vulnerable to the cross site attack if I leave it as it is?
Response: No, only
GET
s which return an array as the "root" object are vulnerable to http://haacked.com/archive/2009/06/25/json-hijacking.aspx" rel="nofollow">this kind of attack. -
Please login first in order for you to submit comments
- Page :
- 1
Hey Craig,
Have you tried out the new client side validation features yet? I've been having some trouble getting them to work. Just wanted to know your experiences.