Although ASP.NET MVC continues to grow in popularity, Microsoft estimates that between 80% and 90% of their customer base is still using ASP.NET Web Forms as their development platform of choice (Damian Edwards, ASP.NET program manager, Build Conference 2012).
It should thus come as no surprise that Microsoft continues to invest in the improvement of the ASP.NET Web Forms framework. ASP.NET MVC is, of course, built on top of ASP.NET Web Forms. As a result, many of these enhancements will also affect ASP.NET MVC developers—so please don’t skip this chapter!
ASP.NET 4.5 Web Forms has been influenced by its newer MVC brother, and it introduces support for strongly typed data controls and model binding. Other shiny new features include better async support, built-in minification and bundling of JavaScript and CSS files, and a new WebSockets API.
Previously when working with template controls such as GridView, you would declare the item you wanted to bind to with a syntax similar to the following:
<%# DataBinder.Eval (Container.DataItem, "Price") %>
With this type of syntax, it’s very easy to make a mistake, such as a typo, since the IDE doesn’t know the type you are binding to until runtime. Additionally, if any properties in the bound class are renamed or changed, you must remember to update the binding code. Otherwise, the error will only appear at runtime.
ASP.NET 4.5 solves these issues by allowing you to specify the type that is bound to a control with the new ItemType
property. Let’s see this in action:
System.ComponentModel.DataAnnotations
assembly. (We will need this later to demonstrate cool declarative validation features.)Person
:
<asp:FormView ID="FormView1" ItemType="WebApplication7.Person" UpdateMethod="UpdatePeople"
AllowPaging="true" runat="server" SelectMethod="GetPeople" DataKeyNames="ID"
DefaultMode="Edit">
<EditItemTemplate>
<ol>
<li>
<%#Item.ID%>
</li>
<li>
<asp:TextBox ID="textFirstName" runat="server" Text='<%# BindItem.FirstName
%>' />
</li>
<li>
<asp:TextBox ID="txtLastName" runat="server" Text='<%# BindItem.LastName%>'/>
</li>
</ol>
<asp:linkbutton id="UpdateButton"
text="Update"
commandname="Update"
runat="server"/>
<asp:ValidationSummary ID="ValidationSummary1" runat="server"/>
</EditItemTemplate>
</asp:FormView>
ItemType
property. You might also have noticed we set two new properties: SelectMethod
and UpdateMethod
. SelectMethod
and UpdateMethod
are new methods that unsurprisingly allow us to specify the methods to bind and update data. We will complete our binding example by creating these select and update methods.IQueryable
list of our person class (you can think of IQueryable
as a query that hasn’t been run yet and can be modified on the fly—this is needed to enable the FormView control’s paging functionality). To do this, open default.aspx.cs and add the following code:
public IQueryable<Person> GetPeople()
{
IQueryable<Person> people = new List<Person>(){
new Person()
{
ID=0,
Age=33,
FirstName="Belinda",
LastName="Lord"
},
new Person()
{
ID=1,
Age=62,
FirstName="Rhonda",
LastName="Lord"
},
new Person()
{
ID=2,
Age=64,
FirstName="Gary",
LastName="Lord"
},
new Person()
{
ID=4,
Age=1,
FirstName="Darcy",
LastName="Lord"
},
}.AsQueryable();
return people;
}
public void UpdatePeople(Person model)
{
var success = TryUpdateModel<Person>(model);
if (success)
{
//TODOwritebackupdate
};
}
BindItem
is similar to Item
but should be used if you want to persist changes that the user has made. To see the difference between Item
and BindItem, follow these steps
:
<asp:TextBox ID="textFirstName" runat="server" Text='<%# Item.FirstName %>' /></asp:TextBox>
<asp:TextBox ID="txtLastName" runat="server" Text='<%# BindItem.LastName %>' /></asp:TextBox>
UpdatePeople
method and run the application.You will see only the LastName
property in the submitted model has been populated with changes.
In the example above, we added a special validation attribute to our Person
class:
[Required(ErrorMessage="First name is required")]
public string FirstName { get; set; }
This attribute, of course, specifies that the FirstName
field is required. To see the effect of this attribute, run the example, remove any entry in the first name textbox, and click the Update button. ASP.NET validation will then kick in and you should then see the error message “First name is required.” This feature should look familiar to MVC users.
There is a number of different validation attributes that you can apply to your classes; ASP.NET 4.5 introduces some new ones such as e-mail address, telephone number, and credit card number. Refer to the source at http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations(v=vs.110).aspx
for more detail.
These simple binding improvements bring us a number of advantages:
These new binding features give you some of the benefits ASP.NET MVC users have been enjoying. They could be a good starting point for upgrading existing applications to a cleaner, happier way of doing things.
While we are on the subject of databinding, I want to mention two new databinding-related features in ASP.NET 4.5:
AllowCustomPaging
and VirtualItemCount
New to ASP.NET 4.5 is the ability to HTML encode databinding expressions by appending “:
” to the end of the <%#
block. For example:
<%#: BindItem.LastName %>
Normally when you page data with a control such as DataGrid, a data source containing every row of the data set is loaded, which can take time with large data sets.
ASP.NET 4.5 introduces a new mode that is enabled by setting AllowCustomPaging
to "true"
. Once this is set, it tells ASP.NET that the currently bound data source doesn’t contain every row of data, and it should instead refer to the VirtualItemCount
to determine the number of items to display. The user would then use the OnPageIndexChanged
event to set the current page index and calculate the block of data to display.
Microsoft has an example of this at http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.datagrid.allowcustompaging.aspx
.
It is common in web applications to need to read from a QueryString
or other posted value. ASP.NET 4.5 introduces new attributes to do this declaratively.
Let’s modify our GetPeople
method to filter the list of people based on a value from the QueryString
using this new approach.
using System.Web.ModelBinding;
GetPeople
method to the following code. Note the use of the new [QueryString]
attribute on the method signature:
public IQueryable<Person> GetPeople([QueryString] string firstName)
{
var people = new List<Person>(){
new Person(){
ID=1,
Age=31,
FirstName="Alex",
LastName="Mackey"
},
new Person(){
ID=2,
Age=33,
FirstName="Belinda",
LastName="Lord"
}
}.AsQueryable();
if (!String.IsNullOrWhiteSpace(firstName))
{
return people.Where(person => person.FirstName == firstName);
}
else
{
return people;
}
}
firstname
to the URL e.g. ?firstName=Belinda.
When applying the value type attribute, ASP.NET will match values to parameters based on name (in this case firstName
from QueryString
is matched to firstName
in the method parameters). If this isn’t what you want, you can of course specify the value to bind to as below:
GetPeople([QueryString("nameIWantToFilterOn")] string firstName)
As well as binding to QueryString
values, you can bind the variable to a number of other types of variables:
… and if none of these serve your purposes, you can even create your own value providers.
The example below demonstrates how to create your own binder. Our binder won’t do much apart from append the string echo to the requested value. (Note: This is very similar to how you would do this in ASP.NET MVC with the IValueProvider
interface.)
using System.Globalization;
using System.Web.ModelBinding;
public class TestBinderAttribute : ValueProviderSourceAttribute
{
private readonly string _key;
public TestBinderAttribute()
{
}
public TestBinderAttribute(string key)
{
_key = key;
}
public string Key
{
get
{
return _key;
}
}
public bool ValidateInput { get; set; }
public override string GetModelName()
{
return "";
}
public override IValueProvider GetValueProvider(ModelBindingExecutionContext
modelBindingExecutionContext)
{
return new TestBinderAttributeProvider();
}
}
public class TestBinderAttributeProvider : IValueProvider
{
public bool ContainsPrefix(string prefix)
{
return false;
}
public ValueProviderResult GetValue(string key)
{
var result = new ValueProviderResult("Echo " + key ,key , CultureInfo.InvariantCulture);
return result;
}
}
That’s it! You can then simply apply the new value provider with the attribute [TestBinder]
:
public IQueryable<Person> GetPeople([TestBinder] string firstName)
{
…
}
HTML5 introduces a huge number of changes, some of which have necessitated ASP.NET server controls being updated (please see the IDE chapter for a number of changes):
The file upload control now supports the multiple file upload feature (for browsers that support this).
To enable, simply set the AllowMultiple
property to "true".
For example,
<asp:FileUpload ID="uploadFile" runat="server" AllowMultiple="true" />
You can then iterate through the files uploaded with the PostedFiles
property:
foreach (var file in uploadFile.PostedFiles) {
file.SaveAs("");
}
During development, it is common to separate JavaScript and CSS files for maintainability and ease of development. Unfortunately, this division isn’t the best way to deliver the files to your users, which is where bundling and minification come in.
Bundling is the combining of files to reduce the number of requests a browser has to make. Minification is the removal of unnecessary characters (e.g., comments and spaces) to produce as tiny a file as possible.
Bundling and minification give you the following advantages:
Prior to VS2012, bundling and minification would have to be implemented using one of many third-party tools and libraries such as ClientDependency and JSMin.
VS2010 users can enjoy these new features by downloading and applying the NuGet package ASP.NET Optimization (see Figure 6-1).
So, how do we configure the files to bundled and minified? This configuration is done in BundleConfig.cs
, which is located in a new directory called App_Start. App_Start is used to contain programmatic configuration for ASP.NET applications (by default bundles/minification in ASP.NET and other items such as Routing in ASP.NET MVC).
BundleConfig
has one static method called RegisterBundles
, which lists all the files to be minified/bundled. This is called in the global.asax.cs App_Start method. There is no reason you have to stick with this convention, but I can’t see an immediate problem with it so I suggest using it.
Let’s take a look at what is going on in the RegisterBundles
method. The following example (code removed for brevity) shows the creation of two types of bundles: a ScriptBundle
containing two Microsoft AJAX JavaScript files and a StyleBundle
containing site.css
.
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include(
"~/Scripts/WebForms/MsAjax/MicrosoftAjax.js",
"~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"));
bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));
}
I think it’s pretty self-explanatory how this API works—the only parameters you may be wondering about are the "~/bundles/MsAjaxJs"
and "~/Content/css"
values. These provide a path that your bundle should be referenced at. When you want to use your bundles, you simply reference the bundles as in the following (note MVC has a similar syntax):
<%: Styles.Render("~/Content/css", "~/Content/moreCss") %>
<%: Scripts.Render("~/bundles/MsAjaxJs") %>
But there is one more thing to be aware of—the use of minified and bundled files can make debugging harder, so files will only be bundled and minified when you are not running in debug mode. This can be modified in web.config as shown here:
<compilation debug="true"></compilation>
Or you can also use the BundleTable.EnableOptmizations API if you need programmatic control.
It would be tedious to add each file in a directory, so the Bundles API also has an IncludeDirectory
method. But you may be wondering in what order ASP.NET will bundle your scripts and CSS files? By default, files are bundled alphabetically. ASP.NET does, however, understand the dependencies for some popular libraries—the bundler is smart enough to know that jQuery needs to be bundled before jQueryUI.
The bundler even understands common naming conventions and will always place reset.css and normalize.css first when combining CSS files.
If you want to use your own bundling or minification mechanism, the Optimization
extensions allow you to do this too by implementing the IBundleTransform
interface.
Let’s create a simple custom bundler that replaces a string in JavaScript files to demonstrate how to do this:
TestTransform
and add the following code:
var testBundle = new Bundle("~/bundles/testMinify").Include("~/scripts/test.js");
testBundle.Transforms.Add(new TestTranform());
bundles.Add(testBundle);
script
tag in our aspx or master file as shown below:
<%: Scripts.Render("~/bundles/testMinify") %>
alert('replace me'),
Custom bundlers and minifiers can be used for any pre-processing you might want to do on JavaScript or CSS files. For example, at the Build 2012 conference, Mads Kristensen (Microsoft Web Platform Program Manager) showed a creative example of overriding the minification process to compile CoffeeScript to JavaScript. For further information on this, please refer to http://channel9.msdn.com/Events/BUILD/BUILD2012/SAC-837T
.
XSS (cross-site scripting) is a well-known attack where unvalidated user input is directly output onto a web page, unencoded, and run. For example, imagine if the following script were submitted to a forum site and then output unencoded (it would be run for each user that visited the site!):
<script>alert('do something bad')</script>
Or even the much worse:
<script>window.location='http://www.justinbiebermusic.com/'</script>
XSS attacks can, however, be much more serious as they can be used to hijack another user’s session cookie, and as a result, allow the attacker to make requests in the context of another user.
For more information on XSS, please refer to www.owasp.org/index.php/Cross-site_Scripting_(XSS)
and www.blackhat.com/presentations/bh-usa-09/VELANAVA/BHUSA09-VelaNava-FavoriteXSS-SLIDES.pdf
.
By default, ASP.NET will try to validate submitted requests for potential attacks such as XSS. At some point, you have probably seen this kick in and a screen similar to the one shown in Figure 6-2.
You could be forgiven for thinking that ASP.NET will prevent all XSS attacks. Unfortunately, this isn’t the case.
Warning Attempting to circumvent ASP.NET protection on live web sites is very probably illegal, so don’t do it!
Unfortunately, ASP.NET’s protection mechanisms are fairly easily circumvented (see www.procheckup.com/vulnerability_manager/documents/document_1258758664/bypassing-dot-NET-ValidateRequest.pdf
), and there is a mind-blowing number of ways to format input and produce an XSS attack. (See http://ha.ckers.org/xss.html
.) Thus, it is vital that you never trust any input from a user and always make sure you encode any output.
ASP.NET validation can also sometimes get in the way if users want to be able to enter certain characters such as “<
” or “>
”. As an example, I worked on a medical system where staff would often use characters such as “<
” and “>
” to denote ranges of laboratory values. In certain combinations, the characters “<
” and “>
” will cause ASP.NET’s validation checks to fail, which led to some unhappy medical staff not being able to enter data how they wanted!
Prior to ASP.NET 4.5, if you wanted to ensure staff could always enter data in this format, you had little choice but to turn validation off at a page or site level.
So now we that we know the issues with the existing model, let’s see how ASP.NET 4.5 improves things.
ASP.NET 4.5 validation mode is enabled by default in ASP.NET 4.5 sites and in web.config with the following key:
<httpRuntime requestValidationMode="4.5" />
When operating in 4.5 validation mode, a number of new features are enabled such as lazy validation, the ability to access raw unvalidated data without triggering the validation mechanisms, and new integrated AntiXSS functionality!
When the validation mode is set to 4.5, submitted data using Server Controls (standard HTML form elements will still trigger failure) will only be validated when and if it’s actually used. Consequently, if a submitted data contains potentially dangerous values but you never actually use it, then ASP.NET will not throw a nasty, potentially dangerous, request error.
ASP.NET 4.5 provides the ability for you to access the raw form, cookies, QueryString
, and URL data without triggering validation with the new unvalidated
collections:
Request.Unvalidated.Cookies["myValue"]
Request.Unvalidated.QueryString["myValue"]
Request.Unvalidated.Form["myValue"]
Warning When you use this method, you are on your own and must remember to perform your own checks!
Validation can now be disabled at control level with the ValidateRequestMode
property:
<asp:TextBox ValidateRequestMode="Disabled" runat="server"></asp:TextBox>
ASP.NET 4.5 has beefed up protection against XSS attacks by incorporating a more advanced protection mechanism based on the open source AntiXSS libraries. The AntiXSS libraries provide superior encoding and protection by using a whitelist-based model (specifying the values that are allowed rather than those that are not allowed), and they contain additional security and encoding features.
ASP.NET 4.5 sites and applications use AntiXSS encoding by default by specifying the following option in web.config:
<httpRuntime ...
encoderType="System.Web.Security.AntiXss.AntiXssEncoder, System.Web,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
AntiXSS routines are now used by default for the following methods in ASP.NET 4.5:
CssEncode
HtmlEncode
HtmlFormUrlEncode
HtmlAttributeEncode
XmlAttributeEncode
XmlEncode
UrlEncode
UrlPathEncode
(new)You can also, of course, use the various AntiXSS methods individually if you need more control. For example:
<%=System.Web.Security.AntiXss.AntiXssEncoder.HtmlEncode("<script>alert('I wont run as im
encoded!'),</script>", false)%>
Let’s talk to Barry Dorrans, the original developer of AntiXSS and author of Wrox’s Beginning ASP.NET Security, about AntiXSS and XSS attacks.
AntiXSS was originally developed as a separate open source project, wasn’t it?
Yes. And it still is. The open source project provides more encoding options—the core encoders were taken into the framework (Alex: Barry also mentioned AntiXSS has additional VBScript, Javascript, and the two LDAP encoders that are not present in the framework version). AntiXSS will be updated and enhancements pulled from .NET 4.5, and .NET will take enhancements from AntiXSS as well for the code encoders.
Any general advice on how to protect against XSS attacks?
Don’t trust input. This doesn’t just mean data from forms, but data from your database, as there’s a risk it may have been compromised, or data from a request such as cookies or headers. All of these can be changed by an attacker, and all should be validated and encoded before output. Additionally, encode correctly. If you’re outputting to JavaScript, use the JavaScript encoder. If your JavaScript is going to insert HTML, then you need to HTML encode first, then JavaScript encode, before outputting it into your page.
Any new features in AntiXSS that you would like to highlight to readers?
AntiXSS now supports .NET 4.0’s encoder swapping, so you can switch between the original .NET encoder and the AntiXSS encoder (either the built-in 4.5 version or the stand-alone version) with the flick of a config setting.
You are known to have some strong opinions on individuals with red hair—any comment?
All male gingers should be rounded up and sent to Tasmania, then the island surrounded with mines and other deterrents to stop them making an escape.
Hmm … time to move on to unobtrusive validation …
ASP.NET 4.5’s unobtrusive validation significantly reduces the amount of JavaScript that is generated inline when using ASP.NET’s validation controls.
Let’s see this in action by comparing the two modes. First, add a textbox and validation control to a page:
<asp:TextBox ID="txtFirstname" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator runat="server" ControlToValidate="txtFirstname"
ErrorMessage="Required"></asp:RequiredFieldValidator>
Check out all the funky validation code this small addition generates:
<script type="text/javascript">
//<![CDATA[
var Page_Validators = new Array(document.getElementById("ctl02"));
//]]>
</script>
<script type="text/javascript">
//<![CDATA[
var ctl02 = document.all ? document.all["ctl02"] : document.getElementById("ctl02");
ctl02.controltovalidate = "txtFirstname";
ctl02.errormessage = "Required";
ctl02.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
ctl02.initialvalue = "";
//]]>
</script>
<script type="text/javascript">
//<![CDATA[
var Page_ValidationActive = false;
if (typeof(ValidatorOnLoad) == "function") {
ValidatorOnLoad();
}
function ValidatorOnSubmit() {
if (Page_ValidationActive) {
return ValidatorCommonOnSubmit();
}
else {
return true;
}
}
//]]>
</script>
And
<script type="text/javascript">
//<![CDATA[
function WebForm_OnSubmit() {
if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false) return false;
return true;
}
//]]>
</script>
Ugh—and this is the slightly cut-down version! Luckily, we can reduce this in ASP.NET 4.5 by enabling the new unobtrusive validation mode (enabled by default in 4.5 sites and applications). When unobtrusive validation is enabled, additional HTML5 data attributes are added to controls to hold the meta information about how validation should work and the generated script is much reduced:
<input name="txtFirstname" type="text" id="txtFirstname" />
<span data-val-controltovalidate="txtFirstname" data-val-errormessage="Required"
id="ctl02" data-val="true" data-val-evaluationfunction="RequiredFieldValidatorEvaluateIsValid"
data-val-initialvalue="" style="visibility:hidden;">Required</span>
If for some reason you don’t want to use the new validation mode, you can turn it off in a number of places.
<add name="ValidationSettings:UnobtrusiveValidationMode" value="None" />
Page.UnobtrusiveValidationMode = System.Web.UI.UnobtrusiveValidationMode.WebForms;
One of the big themes in ASP.NET 4.5 is better support for asynchronous scenarios with the introduction of Await
and Async
keywords. It’s worth noting that previous versions of ASP.NET contain async functionality such as async pages and interfaces for writing async handlers and modules (although their examples and documentation are a bit lacking).
For more information, please refer to http://msdn.microsoft.com/en-us/magazine/cc163463.aspx
.
ASP.NET 4.5 contains several new APIs and changes that allow you to utilize async functionality in your web applications. But first, some of you may be wondering why you might want async support at all.
By default, ASP.NET has access to a (configurable) number of threads to services site requests. If all of these threads are doing something (e.g., awaiting a response from a remote server), then your web server won’t be able to respond to new requests and probably will start throwing Server Unavailable messages, leaving your users to look up amusing cat videos on YouTube.
Many ASP.NET applications function marvelously until they hit a certain number of requests, and then performance can degrade very quickly. Apart from throwing more hardware at the problem or optimizing code, we can try to be more efficient in utilizing resources.
If you have certain types of requests that have to wait on an external resource, you can essentially give back the thread to service other requests (until a response is received).
Of course, you could still end up with a situation where you tie up all the threads on a machine waiting for requests. In which case, you may want to consider other possibilities such as a non-blocking message/queue-based system.
There are no hard and fast rules for determining when async functionality should be used, and it is vital that when making any changes, you measure their effect by benchmarking your application before and after changes are made. As a rough guide, you can consider implementing async functionality when the following situations are true:
So now that we have a (simplified) view of why you might want to do this, let’s see how ASP.NET 4.5 can help us out.
It was possible in previous versions of ASP.NET to develop async modules and handlers, but documentation and examples were very scarce. ASP.NET 4.5 makes this much easier.
The example below shows how to create an asynchronous HttpModule
that will call the Digg REST API (http://developers.digg.com/version2/digg-getall
) asynchronously and output a string of JSON onto each web page the user requests:
using System.Threading.Tasks;
using System.Net;
public class TestHttpModule:IHttpModule
{
public void Init(HttpApplication context)
{
EventHandlerTaskAsyncHelper helper = new
EventHandlerTaskAsyncHelper(ScrapeHtmlPage);
context.AddOnPostAuthorizeRequestAsync(helper.BeginEventHandler,
helper.EndEventHandler);
}
private async Task ScrapeHtmlPage(object caller, EventArgs e)
{
WebClient client = new WebClient();
var result = await
client.DownloadStringTaskAsync("http://services.digg.com/2.0/digg.getAll");
//process here
System.Web.HttpContext.Current.Response.Write(result);
}
public void Dispose()
{
}
}
Note how we used the new EventHandlerTaskAsyncHelper
class to wrap the Begin
and End
methods out of a task object, which avoided the need to worry about any implementation detail.
If you are playing with this example, remember to register the module in web.config:
<system.webServer>
<modules>
<add name="WebApplication4.TestHttpModule" type="WebApplication4.TestHttpModule"/>
</modules>
</system.webServer>
Previously if you wanted to develop an async handler, you would have to implement the IHttpAsyncHandler
interface (see http://madskristensen.net/post/How-to-use-the-IHttpAsyncHandler-in-ASPNET.aspx
). ASP.NET 4.5 introduces a new abstract class called HttpTaskAsyncHandler
that hides a lot of the complexity from you.
If you are sending a very large response to a user, it is necessary to periodically call the HttpResponse.Flush
to stop memory usage becoming too high. ASP.NET 4.5 augments the HttpResponse
class with new BeginFlush
and EndFlush
methods to allow you to perform blocks of flushing and not overuse OS resources.
ASP.NET 4 introduced a method called GetBufferlessInputStream
to retrieve a stream object from a request. (See http://george2giga.com/tag/file-upload/
for an example.) This method, however, was synchronous and would occupy a thread for the entire request. ASP.NET 4.5 adds BeginRead
and EndRead
methods to the GetBufferlessInputStream
method to allow chunked reading and releasing of resources between read calls.
New to ASP.NET 4.5 is the GetBufferedInputStream,
which is similar to GetBufferlessInputStream
but also saves a copy of the request internally (the same place that Form, Files data is kept). This is cool as it allows you to use results in downstream pages, modules, and handlers.
Web applications are becoming increasingly sophisticated, and it is common to need to communicate with various services.
There are a number of options to accomplish this task with, probably the most popular being to continually poll a server with XHR requests. Other alternatives exist that delay disconnections. These can be tricky to implement and don’t scale well (sometimes worse than polling as they keep a connection open), so they aren’t used as much.
HTTP isn’t really an ideal protocol for performing frequent requests for the following reasons:
http://remysharp.com/2007/10/08/what-is-jsonp/
).WebSockets is a new technology that attempts to resolve some of these limitations by:
When would WebSockets be a suitable protocol for your application? You might want to consider using WebSockets in the following scenarios:
There is a nice set of demos at www.html5rocks.com/en/tutorials/websockets/basics/
and an interesting article that compares a WebSockets and polling solution in terms of latency and throughput at http://websocket.org/quantum.html
.
WebSockets is a relatively new protocol that has already undergone a number of versions as various issues are addressed. Addressing these problems is important as support across browsers varies.
At the time of writing, WebSockets (in some form) can be used by the following browsers (check http://caniuse.com
for the most up to date information):
Earlier implementations of WebSockets had some security issues, so your connections may work but they are not secure. (Firefox disabled support in Firefox 4 and 5 for this reason.)
The other issue that you may encounter is that some older proxy servers don’t support the HTTP upgrade system that WebSockets uses to connect, so some clients may be unable to connect.
ASP.NET 4.5 introduces a number of APIs for working with WebSockets. If you find you need more control than the ASP.NET API’s offers, then look into WCF because it has also been updated.
Before we begin, there are a couple of requirements for using ASP.NET WebSockets API:
Currently, Microsoft has no plans to release WebSockets support for earlier versions of IIS. Consequently, if you plan to run it on Windows Server 2008, you are going to have to look at other options such as http://superwebsocket.codeplex.com/
. Alternatively you could look at the SignalR library from Microsoft, which is designed for developing async applications and providing WebSockets (and fallback) support: https://github.com/SignalR/SignalR/wiki/WebSockets
.
I am going to assume that you are already working with some version of Windows 8 that has IIS and ASP.NET 4.5 installed. The other thing we are going to need to do is make sure IIS has the WebSockets protocol feature installed (this is in the add/remove programs bit):
index.htm
and enter the following code:
<!doctype html>
<head>
<script src="Scripts/jquery-1.7.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
var name = prompt('what is your name?:'),
var url = 'ws://' + window.location.hostname + window.location.pathname.replace('index.htm',
'ws.ashx') + '?name=' + name;
alert('Connecting to: ' + url);
ws = new WebSocket(url);
ws.onopen = function () {
$('#messages').prepend('Connected <br/>'),
$('#cmdSend').click(function () {
ws.send($('#txtMessage').val());
$('#txtMessage').val(''),
});
};
ws.onmessage = function (e) {
$('#chatMessages').prepend(e.data + '<br/>'),
};
$('#cmdLeave').click(function () {
ws.close();
});
ws.onclose = function () {
$('#chatMessages').prepend('Closed <br/>'),
};
ws.onerror = function (e) {
$('#chatMessages').prepend('Oops something went wront <br/>'),
};
});
</script>
</head>
<body>
<input id="txtMessage" />
<input id="cmdSend" type="button" value="Send" />
<input id="cmdLeave" type="button" value="Leave" />
<br />
<div id="chatMessages" />
</body>
</html>
HttpHandler
, add a new generic handler to the project called ws.ashx
and enter the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Web.WebSockets;
namespace WebSockets
{
public class ws : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
context.AcceptWebSocketRequest(new TestWebSocketHandler());
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
TestWebSocketHandler
, that is created in the AcceptWebSocketRequest
method). Create a new class called TestWebSocketHandler
and enter the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using Microsoft.Web.WebSockets;
namespace WebSockets
{
public class TestWebSocketHandler : WebSocketHandler
{
private static WebSocketCollection clients = new WebSocketCollection();
private string name;
public override void OnOpen()
{
this.name = this.WebSocketContext.QueryString["name"];
clients.Add(this);
clients.Broadcast(name + " has connected.");
}
public override void OnMessage(string message)
{
clients.Broadcast(string.Format("{0} said: {1}", name, message));
}
public override void OnClose()
{
clients.Remove(this);
clients.Broadcast(string.Format("{0} has gone away.", name));
}
}
}
Microsoft says that combined use of ASP.NET 4.5 and Windows 8 can improve web application startup time by up to 35 percent (www.asp.net/vnext/overview/whitepapers/whats-new#_Toc_perf
).
A number of performance enhancements have been made that will affect ASP.NET applications performance:
If you have a number of web sites hosted on a server, it’s quite likely that many of them also use the same libraries. For example, several sites may reference NHibernate assemblies. When a web server first starts up, having many copies of the same libraries slows everything down as each one must be individually processed, read, and loaded.
VS2012 introduces a new command line tool that replaces individual assemblies with a link to a single copy of an assembly, making this process much quicker. If a site references a specific version of an assembly, the symbolic link will be replaced by the actual assembly.
To use the shared assemblies, you must use the command-line tool aspnet_intern.exe,
which comes as part of the VS2012 SDK, to process assemblies on the server (you can also use this feature within ASP.NET 4 if you apply the following update: http://support.microsoft.com/kb/2468871
).
When aspnet_intern
is run, it will, by default, process assemblies that appear in more than three places. (This is configurable using the minrefcount option.)
Let’s see this in action now:
C:Program Files (x86)Microsoft SDKsWindowsv8.0AinNETFX 4.0 Tools.
aspnet_interns
options: aspnet_intern /?
aspnet_intern -mode analyze -sourcedir
"C:WindowsMicrosoft.NETFrameworkv4.0.30319Temporary ASP.NET Files" > c:internReport.txt
C:CommonASP
:
aspnet_intern -mode exec -sourcedir
"C:WindowsMicrosoft.NETFrameworkv4.0.30319Temporary ASP.NET
Files" -interndir C:CommonASP
Microsoft suggests setting up a weekly scheduled task to do this.
When a site first starts up, individual assemblies must be JIT compiled by the framework. ASP.NET 4.5 speeds up this work by dividing compilation across multiple cores. This feature is enabled by default in ASP.NET 4.5, but it may be turned off in web.config as shown below:
<system.web>
<compilation profileGuidedOptimizations="None" />
</system.web>
To a certain extent, previous versions of ASP.NET allowed you to configure and tweak how the garbage collector worked (http://learn.iis.net/page.aspx/50/aspnet-20-35-shared-hosting-configuration/
).
ASP.NET 4.5 introduces a new profile setting for those running servers, hosting many sites that groups these tweaks together. To enable this setting, copy the following setting to the aspnet.config file at WindowsMicrosoft.NETFrameworkv4.0.30319aspnet.config
:
<configuration>
<runtime>
<performanceScenario value="HighDensityWebHosting" />
Recent versions of Windows include a technology known as Prefetcher (http://en.wikipedia.org/wiki/Prefetcher
), which reduces disk-read time by maintaining an index of recently used files.
Until now, Prefetcher was not available in server editions of Windows because it was mainly aimed at client applications. Prefetching, however, can reduce launch time for individual sites. To enable Prefetching for individual sites, run the following command from a Visual Studio command prompt:
sc config sysmain start=auto
reg add "HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerMemory
ManagementPrefetchParameters" /v EnablePrefetcher /t REG_DWORD /d 2 /f
reg add "HKEY_LOCAL_MACHINESoftwareMicrosoftWindows NTCurrentVersionPrefetcher" /v
MaxPrefetchFiles /t REG_DWORD /d 8192 /f
net start sysmain
You must then add the following switch to your web site:
<configuration>
<system.web>
<compilation enablePrefetchOptimization="true" />
</system.web>
</configuration>
VS2012 allows you to precompile a web site before deployment or publishing to reduce startup time. To enable this option, right-click on Project, select Properties, and then go to the Package/Publish Web tab and select the checkbox that reads “Precompile this application before publishing.”
You should also be aware of the following changes in VS2012 affecting ASP.NET users.
In VS2012, IIS Express is now the default web server for testing your applications. IIS Express has all the main features of its bigger brother, but it doesn’t require administrator rights for most tasks, it is not run a service, and it can be used by multiple users on the same machine. Refer to http://learn.iis.net/page.aspx/868/iis-express-overview/
for more information.
The Script Manager control will now fall back to a local version of referenced scripts if they are not loaded successfully from the CDN. For more information on the EnableCDN feature, please refer to http://weblogs.asp.net/infinitiesloop/archive/2009/11/23/asp-net-4-0-scriptmanager-improvements.aspx
.
An update was made to IIS 7 to natively enable routing functionality on Windows 7 SP1. Previously it was necessary to add a setting called runAllManagedModulesForAllRequests to your web.config file to enable this. The new web site templates in Visual Studio 12 do not include this setting, which means that it is false by default. Thus, if you run one of the new project templates on a machine without the aforementioned update applied, then routing won’t work. To resolve this problem, simply add the setting to web.config:
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
</modules>
</system.webServer>
</configuration>
HttpRequest.Abort
is a new method that forcibly terminates an HTTP connection.
A number of new APIs have been introduced for better Async support such as HttpResponse.ClientDisconnectedToken
, HttpRequest.TimedOutToken
, and HttpContext.ThreadAbortOnTimeout
. These APIs allow you to be notified of client disconnection or timeout and control the time before ASP.NET aborts a request. Unfortunately there is no information on these at the time of writing.
Microsoft has also introduced new APIs for customizing how ASP.NET works. Again, very little information is available on these APIs, but I wanted to make you aware of them. A new class called ControlBuilderInterceptor
has been created that should allow you to customize Web Forms output to some degree, and a new method called TemplateParser.ParseTemplate
will allow the creation of a template instance from ASPX markup.
ASP.NET 4.5 contains a number of useful features. Probably the biggest game changer is support for model and declarative binding. New async support and improvements to validation are also very welcome and will benefit all users.