April 2010 Blog Posts
One annoyance that some developers have run into with ASP.NET MVC is that certain reserved filenames are not allowed in URLs. Often, this is manifested as a Bad Request error or a File Not Found (404) error.
The specifics of this restriction are accounted for in an interesting blog post entitled Zombie Operating Systems and ASP.NET MVC. This actually wasn’t a restriction on ASP.NET MVC but was built into the core of ASP.NET itself.
Fortunately, ASP.NET 4 fixes this issue with a new setting. In web.config, simply add <httpRuntime relaxedUrlToFileSystemMapping="true"/> to the system.web node. Here’s a snippet from my web.config.
<configuration>
<system.web>
<httpRuntime relaxedUrlToFileSystemMapping="true"/>
<!-- ... your other settings ... -->
</system.web>
</configuration>
Here is a screenshot of it working on my machine.
Now you are free to use COM1-9, LPT1-9, AUX, PRT, NUL, CON in your URLs. I know you were dying to do so. :)
What about web.config?
So the question comes up from time to time, “what if I want to have web.config in my URL?” Why would you want that? Well if you are StackOverflow.com, this makes sense because of the tagging system which places a tag (such as the “web.config” tag, into the URL. I’m not sure why anyone else would want it. ;)
The answer is yes, it works.
Please note, that you still can’t request /web.config because that would try to request web.config in the root of your web application and ASP.NET won’t allow that for good reason!
In fact, any request for a *.config file that doesn’t match a route will fail.
While I think the vast majority of developers really won’t encounter this issue, it’s a really improvement included in ASP.NET 4 for those that do care.
Keep in mind that this isn’t restricted to just these special names. For example, a URL segment ending with a dot such as the following URL http://example.com/version/1.0./something will not work unless you set the this web.config value.
Like the well disciplined secure developer that you are, when you built your ASP.NET MVC 1.0 application, you remembered to call Html.Encode every time you output a value that came from user input. Didn’t you?
Well, in ASP.NET MVC 2 running on ASP.NET 4, those calls can be replaced with the new HTML encoding syntax (aka code nugget). I’ve written a three part series on the topic.
But dang, going through all your source files cleaning up these calls is a pretty big pain. Don’t worry, I have your back. Just bring up the Find an Replace dialog (CTRL + SHIFT + H) and expand the Find options section and check the checkbox labeled Use and make sure Regular expressions is selected.
Then enter the following in the Find what textbox.
\<\%:b*=:b*Html.Encode\({[^%]*}\):b*\%\>
And enter the following in the Replace with textbox.
<%: \1 %>
Here’s a screenshot of what the dialog should look like (though yours won’t have the red box :P).
Note that this regular expression I’m giving you is not foolproof. There are some very rare edge cases where it might not work, but for the vast majority of cases, it should work fine. At least, it works on my machine!
Now that I’m finally done with updates to Professional ASP.NET MVC 2, I hope to get back to my regular blogging schedule. This will be only my third blog post this month, a new record low! And I love to blog! It’s been a busy past few months.
UPDATE: The JsonValueProviderFactory is now registered by default in ASP.NET MVC 3. So if you’re using ASP.NET MVC 3, you can ignore that part of this blog post.
Javier “G Money” Lozano, one of the good folks involved with C4MVC, recently wrote a blog post on posting JSON (JavaScript Object Notation) encoded data to an MVC controller action. In his post, he describes an interesting approach of using a custom model binder to bind sent JSON data to an argument of an action method. Unfortunately, his sample left out the custom model binder and only demonstrates how to retrieve JSON data sent from a controller action, not how to send the JSON to the action method. Honest mistake. :)
His post reminds me of how remiss I’ve been in blogging recently because a while back, we added something to our ASP.NET MVC 2 Futures library that handles sending JSON to an action method but I just never found time to blog about it.
There’s one key problem with using a model binder to accept JSON. By writing a custom model binder, you miss out on validation. Using his example, if you type “abc” for the Age field, you will get a serialization failure when attempting to serialize the JSON into the PersonInputModel object because Age is an Int32 and the serialization will fail.
Value Providers to the rescue!
This is where value providers, a new feature of ASP.NET MVC 2, enters to save the day. Whereas model binders are used to bind incoming data to an object model, value providers provide an abstraction for the incoming data itself.
When the ASP.NET MVC feature team first implemented value providers, Jonathan Carter and I were working on a client templating sample which sent JSON to an action method. Rather than write a custom model binder which was the approach I took, Jonathan had the unique insight to write a custom value provider which received JSON data and serialized it to a dictionary rather than the target object. The beauty of his approach is that this dictionary data is then passed to the default model binder which binds it to the final object with validation!
I took is his prototype and added the JsonValueProviderFactory to our ASP.NET MVC 2 Futures library and then totally didn’t write about it. Yes, I suck.
Setting it up
To get started, download the ASP.NET MVC 2 Futures Library and reference the Microsoft.Web.Mvc.dll assembly. Then, in your Global.asax.cs file, add the following call to register the JsonValueProviderFactory.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
}
That’s it! You’re done!
This value provider will handle requests that are encoded as application/json. There’s no need to specify a model binder on classes that accept JSON input.
See it in action
I took the liberty of updating Javier’s sample to use this new value provider and to actually post JSON to the action method.
It turns out, sending JSON encoded data to an action method with jQuery was not as straightforward as I hoped. If you know of a more straightforward way, let me know. I ended up using a JSON plug-in for jQuery I found on the Internets. This provides a $.toJSON method I could use to serialize an object into a JSON encoded string. Here’s the updated client script code.
UPDATE: Per Dave Ward’s comment here I should be using json2.js and its JSON.stringify(...) method instead because it matches an API that some browsers implement and will use the native implementation if it exists. Nice! I’ll update this blog post later when I have a moment.
$(function () {
$("#personCreate").click(function () {
var person = getPerson();
// poor man's validation
if (person == null) {
alert("Specify a name please!");
return;
}
var json = $.toJSON(person);
$.ajax({
url: '/home/save',
type: 'POST',
dataType: 'json',
data: json,
contentType: 'application/json; charset=utf-8',
success: function (data) {
// get the result and do some magic with it
var message = data.Message;
$("#resultMessage").html(message);
}
});
});
});
function getPerson() {
var name = $("#Name").val();
var age = $("#Age").val();
// poor man's validation
return (name == "") ? null : { Name: name, Age: age };
}
Notice that we use the $.ajax method to specify both the JSON data and the JSON content type for the request.
A quick check in Fiddler confirms that the data in the POST request is properly JSON encoded.
Now, within my action method, I can actually check to see if the model state is valid and if not, return an error message.
[HttpPost]
public ActionResult Save(PersonInputModel inputModel) {
if (ModelState.IsValid)
{
string message = string.Format("Created user '{0}' aged '{1}' in the system."
, inputModel.Name, inputModel.Age);
return Json(new PersonViewModel { Message = message });
}
else {
string errorMessage = "<div class=\"validation-summary-errors\">"
+ "The following errors occurred:<ul>";
foreach (var key in ModelState.Keys) {
var error = ModelState[key].Errors.FirstOrDefault();
if (error != null) {
errorMessage += "<li class=\"field-validation-error\">"
+ error.ErrorMessage + "</li>";
}
}
errorMessage += "</ul>";
return Json(new PersonViewModel { Message = errorMessage });
}
}
And as you can see in the Fiddler screenshot, I sent an invalid Age to the server and yet, it all still works.
Whew! I can finally cross this off of my immense blog backlog. :) Hopefully soon, I’ll blog a more detailed write-up of value providers.
We have plans to add the JsonValueProviderFactory to ASP.NET MVC 3 so that it’s a built-in feature. I hope you find this useful and as always, let me know if there are ways we can improve it!
Oh, and here’s Javier’s updated sample with the value provider.
This is the third in a three part series related to HTML encoding blocks, aka the <%: ... %> syntax.
Scott Guthrie recently wrote about the new <%: %> syntax for HTML encoding output in ASP.NET 4. I also covered the topic of HTML encoding code nuggets in the past as well providing some insight into our design choices for the approach we took.
A commenter to Scott’s blog post asked,
Will it be possible to extend this so that is uses libraries like AntiXSS instead? See: http://antixss.codeplex.com/
The answer is yes!
ASP.NET 4 includes a new extensibility point which allows you to replace the default encoding logic with your own anywhere ASP.NET does encoding.
All it requires is to write a class which derives from System.Web.Util.HttpEncoder and register that class in Web.config via the encoderType attribute of the httpRuntime element.
Walkthrough
In the following section, I’ll walk you through setting this up. First, you’re going to need to download the AntiXSS library which is at version 3.1 at the time of this writing. On my machine, that dropped the AntiXSSLibrary.dll file at the following location: C:\Program Files (x86)\Microsoft Information Security\Microsoft Anti-Cross Site Scripting Library v3.1\Library
Create a new ASP.NET MVC application (note, this works for *any* ASP.NET application). Copy the assembly into the project directory somewhere where you’ll be able to find it. I typically have a “lib” folder or a “Dependencies” folder for this purpose. Right clicke on the References node of the project to add a reference to the assembly.
The next step is to write a class that derives from HttpEncoder. Note that in the following listing, some methods were excluded which are included in the project.
using System;
using System.IO;
using System.Web.Util;
using Microsoft.Security.Application;
/// <summary>
/// Summary description for AntiXss
/// </summary>
public class AntiXssEncoder : HttpEncoder
{
public AntiXssEncoder() { }
protected override void HtmlEncode(string value, TextWriter output)
{
output.Write(AntiXss.HtmlEncode(value));
}
protected override void HtmlAttributeEncode(string value, TextWriter output)
{
output.Write(AntiXss.HtmlAttributeEncode(value));
}
protected override void HtmlDecode(string value, TextWriter output)
{
base.HtmlDecode(value, output);
}
// Some code omitted but included in the sample
}
Finally, register the type in web.config.
...
<system.web>
<httpRuntime encoderType="AntiXssEncoder, AssemblyName"/>
...
Note that you’ll need to replace AssemblyName with the actual name of your assembly. Also, in the sample included with this blog post, AntiXssEncoder is not in any namespace. If you put your encoder in a namespace, you’ll need to make sure to provide the fully qualified type name.
To prove that this is working, run the project in the debugger and set a breakpoint in the encoding method.
With that, you are all set to take full control over how strings are encoded in your application.
Note that Scott Hanselman and I gave a live demonstration of setting this up at Mix 10 this year as part of our security talk if you’re interested in watching it.
As usual, I’ve provided a sample ASP.NET MVC 2 project for Visual Studio 2010 which you can look at to see this in action.