RSS

Tag Archives: ASP.NET

How to hide WebMethods unhandled exception stacktrace using Response.Filter

If you’re interested in the code shown in this post, right-click here, click Save As and rename to zip.

Here’s a nice trick how to manipulate outgoing ASP.NET Response. Specifically, in situations that you would like to provide global handling of errors, removing the stacktrace.

In case you are wondering why you would remove the stacktrace – it is considered a security issue for attackers to be able to review the stacktrace (https://www.owasp.org/index.php/Missing_Error_Handling).

Consider this code, a web method raising an exception.

[WebMethod]
public static void RaiseException()
{
	throw new ApplicationException("test");
}

And the page code:

<asp:ScriptManager runat="server" EnablePageMethods="true" />
<input type="button" value="Go" id="btn" />
<script>
	window.onload = function () {
		var btn = document.getElementById('btn');
		btn.onclick = function () {
			PageMethods.RaiseException(function () { }, function (error) {
				alert(error.get_stackTrace());
			});
		}
	}
</script>

Running this code as is and clicking the button would result with a JSON response that includes the error message, exception type and stacktrace:

exception1

To remove the stacktrace it is possible to use ASP.NET’s Response.Filter. The Filter property is actually a Stream that allows us to override the default stream behavior. In this particular case the custom stream is simply a wrapper. All the stream methods except the method Write() will simply invoke the original stream’s methods.

public class ExceptionFilterStream : Stream
{
    private Stream stream;
    private HttpResponse response;
    public ExceptionFilterStream(HttpResponse response)
    {
        this.response = response;
        this.stream = this.response.Filter;
    }

    public override bool CanRead { get { return this.stream.CanRead; } }
    public override bool CanSeek { get { return this.stream.CanSeek; } }
    public override bool CanWrite { get { return this.stream.CanWrite; } }
    public override long Length { get { return this.stream.Length; } }
    public override long Position { get { return this.stream.Position; } set { this.stream.Position = value; } }
    public override void Flush() { this.stream.Flush(); }
    public override int Read(byte[] buffer, int offset, int count) { return this.stream.Read(buffer, offset, count); }
    public override long Seek(long offset, SeekOrigin origin) { return this.stream.Seek(offset, origin); }
    public override void SetLength(long value) { this.stream.SetLength(value); }

    public override void Write(byte[] buffer, int offset, int count)
    {
        if (this.response.StatusCode == 500 && this.response.ContentType.StartsWith("application/json"))
        {
            string response = System.Text.Encoding.UTF8.GetString(buffer);

            var serializer = new JavaScriptSerializer();
            var map = serializer.Deserialize<Dictionary<string, object>>(response);
            if (map != null && map.ContainsKey("StackTrace"))
            {
                map["StackTrace"] = "Forbidden";

                response = serializer.Serialize(map);
                buffer = System.Text.Encoding.UTF8.GetBytes(response);
                this.stream.Write(buffer, 0, buffer.Length);

                return;
            }
        }

        this.stream.Write(buffer, offset, count);
    }
}

Note that all the methods simply call the original stream’s methods. Points of interest:

  • Line 8: Apparently if you don’t call the original Filter property “on time” you will run into an HttpException: “Exception Details: System.Web.HttpException: Response filter is not valid”.
  • Line 23: From this point on the customization of the Write() is up to you. As can be seen, I selected to intercept error codes of 500 (“internal server errors”) where the response are application/json. The code simply reads the text, deserializes it and if the key “StackTrace” appears, replace its value with the word “Forbidden”. Note that there’s a return statement following this replacement so the code does not proceed to line 41. In all other cases, the original stream’s Write() kicks-in (Line 41).

Finally, we need to actually use the custom Stream. I used Application_BeginRequest in Global.asax like so:

<script runat="server">

    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        Response.Filter = new ExceptionFilterStream(Response);
    }

</script>

The result:
exception2

Obviously you can use this technique to manipulate outgoing responses for totally different scenarios.

 
Leave a comment

Posted by on 29/11/2015 in Software Development

 

Tags: , , ,

Taking a [passport] photo using your camera and HTML 5 and uploading the result with ASP.NET Ajax

If you just want the sample, right click this link, save as, rename to zip and extract.

7

You can use your HTML 5 browser to capture video and photos. That is if your browser supports this feature (at the time of this writing, this example works well on Firefox and Chrome but not IE11).
I have followed some good references on the internet on how to do that, but I also needed some implementation on how to take a “passport” photo and upload the result. This is the intention of this post.

Steps:

  1. Capture video and take snapshot.
  2. Display target area.
  3. Crop the photo to a desired size.
  4. Upload the result to the server.

Step 1: Capture video and take snapshot.
This step relies mainly on the Eric Bidelman’s excellent article. After consideration I decided not to repeat the necessary steps for taking a snapshot using HTML 5, so if you require detailed explanation please read his good article. However the minimum code for this is pretty much straight forward so consider reading on. What you basically need is a browser that supports the video element and getUserMedia(). Also required is a canvas element for showing a snapshot of the video source.

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <video autoplay width="320" height="240"></video>
    <canvas width='320' height='240' style="border:1px solid #d3d3d3;"></canvas>
    <div>
        <input type="button" value="start capture" onclick="startCapture()" />
        <input type="button" value="take snapshot" onclick="takePhoto()" />
        <input type="button" value="stop capture" onclick="stopCapture()" />
    </div>
    <script type="text/javascript">
        var localMediaStream = null;
        var video = document.querySelector('video');
        var canvas = document.querySelector('canvas');

        function takePhoto() {
            if (localMediaStream) {
                var ctx = canvas.getContext('2d');
                ctx.drawImage(video, 0, 0, 320, 240);
            }
        }

        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
        window.URL = window.URL || window.webkitURL;

        function startCapture() {
            navigator.getUserMedia({ video: true }, function (stream) {
                video.src = window.URL.createObjectURL(stream);
                localMediaStream = stream;
            }, function (e) {
                console.log(e);
            });
        }

        function stopCapture() {
            video.pause();
            localMediaStream.stop();
        }
    </script>
</body>
</html>

Several points of interest:

  • Line 7: Video element for showing the captured stream. My camera seems to show a default of 640×480 but here this is set to 320×240 so it will take less space on the browser. Bear this in mind, it’ll be important for later.
  • Line 8: Canvas element for the snapshots. Upon clicking ‘take photo’, the captured stream is rendered to this canvas. Note the canvas size.
  • Line 22: Drawing the snapshot image onto the canvas.
  • Line 26: Consider testing support for getUserMedia.
  • Line 30: Capture video.

The result, after starting a capture and taking a snapshot (video stream is on the left, canvas with snapshot is to the right):
2

Step 2: Display target area.
As the camera takes pictures in “landscape”, we will attempt to crop the image to the desired portrait dimensions. Therefore the idea is to place a div on top of the video element to mark the target area, where the head is to be placed.

4

The code:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <style type="text/css">
        .container {
            width: 320px;
            height: 240px;
            position: relative;
            border: 1px solid #d3d3d3;
            float: left;
        }

        .container video {
            width: 100%;
            height: 100%;
            position: absolute;
        }

        .container .photoArea {
            border: 2px dashed white;
            width: 140px;
            height: 190px;
            position: relative;
            margin: 0 auto;
            top: 40px;
        }

        canvas {
            float: left;
        }

        .controls {
            clear: both;
        }
    </style>
</head>
<body>
    <div class="container">
        <video autoplay></video>
        <div class="photoArea"></div>
    </div>
    <canvas width='320' height='240' style="border: 1px solid #d3d3d3;"></canvas>
    <div class="controls">
        <input type="button" value="start capture" onclick="startCapture()" />
        <input type="button" value="take snapshot" onclick="takePhoto()" />
        <input type="button" value="stop capture" onclick="stopCapture()" />
    </div>
    <script type="text/javascript">
        var localMediaStream = null;
        var video = document.querySelector('video');
        var canvas = document.querySelector('canvas');

        function takePhoto() {
            if (localMediaStream) {
                var ctx = canvas.getContext('2d');
                ctx.drawImage(video, 0, 0, 320, 240);
            }
        }

        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
        window.URL = window.URL || window.webkitURL;

        function startCapture() {
            navigator.getUserMedia({ video: true }, function (stream) {
                video.src = window.URL.createObjectURL(stream);
                localMediaStream = stream;
            }, function (e) {
                console.log(e);
            });
        }

        function stopCapture() {
            video.pause();
            localMediaStream.stop();
        }
    </script>
</body>
</html>

As you can see, the code was modified to place the dashed area on top of the video. Points of interest:

  • Lines 20-27: note the dimensions of the target area. Also note that the target area is positioned horizontally automatically using ‘margin’.
  • Line 41: The dashed area.

Step 3: Crop picture to desired size.
Luckily the drawImage() method can not only resize a picture but also crop it. A good reference on drawImage is here, and the very good example is here. Still, this is tricky as this isn’t an existing image as shown in the example, but a captured video source which is originally not 320×240 but 640×480. It took me some time to understand that and figure out that it means that the x,y,width and height of the source arguments should be doubled (and if this understanding is incorrect I would appreciate if someone can comment and provide the correct explanation).

As cropping might be a confusing business, my suggestion is to first “crop without cropping”. This means invoking drawImage() to crop, but ensuring that the target is identical to the source in dimensions.

function takePhoto() {
    if (localMediaStream) {
        var ctx = canvas.getContext('2d');
        // original draw image
        //ctx.drawImage(video, 0, 0, 320, 240); 

        // crop without cropping: source args are doubled; 
        // target args are the expected dimensions
        // the result is identical to the previous drawImage
        ctx.drawImage(video, 0, 0, 640, 480, 0, 0, 320, 240);
    }
}

The result:
5

Let’s review the arguments (skipping the first ‘video’ argument):

  • The first pair are the x,y of the starting points of the source.
  • The second pair are the width and height of the source.
  • The third pair are the x,y of the starting points of the target canvas (these can be greater than zero, for example if you would like to have some padding).
  • The fourth pair are the width and height of the target canvas, effectively allowing you also to resize the picture.

Now let’s review the dimensions in our case:
6

In this example the target area is 140×190 and starts at y=40. As the width of the capture area is 320 and the target area is 140, each margin is 90. So basically we should start cropping at x=90.

But since in the source picture everything is doubled as explained before, the drawImage looks different as the first four arguments are doubled:

function takePhoto() {
    if (localMediaStream) {
        var ctx = canvas.getContext('2d');
        //ctx.drawImage(video, 0, 0, 320, 240); // original draw image
        //ctx.drawImage(video, 0, 0, 640, 480, 0, 0, 320, 240); // entire image

        //instead of using the requested dimensions "as is"
        //ctx.drawImage(video, 90, 40, 140, 190, 0, 0, 140, 190);

        // we double the source args but not the target args
        ctx.drawImage(video, 180, 80, 280, 380, 0, 0, 140, 190);
    }
}

The result:
7

Step 4: Upload the result to the server.
Finally we would like to upload the cropped result to the server. For this purpose we will take the image from the canvas and set it as a source of an img tag.

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <style type="text/css">
        .container {
            width: 320px;
            height: 240px;
            position: relative;
            border: 1px solid #d3d3d3;
            float: left;
        }

        .container video {
            width: 100%;
            height: 100%;
            position: absolute;
        }

        .container .photoArea {
            border: 2px dashed white;
            width: 140px;
            height: 190px;
            position: relative;
            margin: 0 auto;
            top: 40px;
        }

        canvas, img {
            float: left;
        }

        .controls {
            clear: both;
        }
    </style>
</head>
<body>
    <div class="container">
        <video autoplay></video>
        <div class="photoArea"></div>
    </div>
    <canvas width='140' height='190' style="border: 1px solid #d3d3d3;"></canvas>
    <img width="140" height="190" />
    <div class="controls">
        <input type="button" value="start capture" onclick="startCapture()" />
        <input type="button" value="take snapshot" onclick="takePhoto()" />
        <input type="button" value="stop capture" onclick="stopCapture()" />
    </div>
    <script type="text/javascript">
        var localMediaStream = null;
        var video = document.querySelector('video');
        var canvas = document.querySelector('canvas');

        function takePhoto() {
            if (localMediaStream) {
                var ctx = canvas.getContext('2d');
                //ctx.drawImage(video, 0, 0, 320, 240); // original draw image
                //ctx.drawImage(video, 0, 0, 640, 480, 0, 0, 320, 240); // entire image

                //instead of
                //ctx.drawImage(video, 90, 40, 140, 190, 0, 0, 140, 190);

                // we double the source coordinates
                ctx.drawImage(video, 180, 80, 280, 380, 0, 0, 140, 190);
                document.querySelector('img').src = canvas.toDataURL('image/jpeg');
            }
        }

        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
        window.URL = window.URL || window.webkitURL;

        function startCapture() {
            navigator.getUserMedia({ video: true }, function (stream) {
                video.src = window.URL.createObjectURL(stream);
                localMediaStream = stream;
            }, function (e) {
                console.log(e);
            });
        }

        function stopCapture() {
            video.pause();
            localMediaStream.stop();
        }
    </script>
</body>
</html>
  • Lines 43-44: Note that the canvas has been resized to the desired image size, and the new img element is also resized to those dimensions. If we don’t match them we might see the cropped image stretched or resized not according to the desired dimensions.
  • Line 66: We instruct the canvas to return a jpeg as a source for the image (other image formats are also possible, but this is off topic).

This is how it looks like. The video is on the left, the canvas is in the middle and the new img is to the right (it is masked with blue because of the debugger inspection). It is important to notice the debugger, which shows that the source image is a base64 string.
8

Now we can add a button to upload the base64 string to the server. The example uses ASP.NET PageMethods but obviously you can pick whatever is convenient for yourself. The client code:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <style type="text/css">
        .container {
            width: 320px;
            height: 240px;
            position: relative;
            border: 1px solid #d3d3d3;
            float: left;
        }

        .container video {
            width: 100%;
            height: 100%;
            position: absolute;
        }

        .container .photoArea {
            border: 2px dashed white;
            width: 140px;
            height: 190px;
            position: relative;
            margin: 0 auto;
            top: 40px;
        }

        canvas, img {
            float: left;
        }

        .controls {
            clear: both;
        }
    </style>
</head>
<body>
    <form runat="server">
        <asp:ScriptManager runat="server" EnablePageMethods="true"></asp:ScriptManager>
    </form>
    <div class="container">
        <video autoplay></video>
        <div class="photoArea"></div>
    </div>
    <canvas width='140' height='190' style="border: 1px solid #d3d3d3;"></canvas>
    <img width="140" height="190" />
    <div class="controls">
        <input type="button" value="start capture" onclick="startCapture()" />
        <input type="button" value="take snapshot" onclick="takePhoto()" />
        <input type="button" value="stop capture" onclick="stopCapture()" />
        <input type="button" value="upload" onclick="upload()" />
    </div>
    <script type="text/javascript">
        var localMediaStream = null;
        var video = document.querySelector('video');
        var canvas = document.querySelector('canvas');

        function upload() {
            var base64 = document.querySelector('img').src;
            PageMethods.Upload(base64,
                function () { /* TODO: do something for success */ },
                function (e) { console.log(e); }
            );
        }

        function takePhoto() {
            if (localMediaStream) {
                var ctx = canvas.getContext('2d');
                //ctx.drawImage(video, 0, 0, 320, 240); // original draw image
                //ctx.drawImage(video, 0, 0, 640, 480, 0, 0, 320, 240); // entire image

                //instead of
                //ctx.drawImage(video, 90, 40, 140, 190, 0, 0, 140, 190);

                // we double the source coordinates
                ctx.drawImage(video, 180, 80, 280, 380, 0, 0, 140, 190);
                document.querySelector('img').src = canvas.toDataURL('image/jpeg');
            }
        }

        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
        window.URL = window.URL || window.webkitURL;

        function startCapture() {
            navigator.getUserMedia({ video: true }, function (stream) {
                video.src = window.URL.createObjectURL(stream);
                localMediaStream = stream;
            }, function (e) {
                console.log(e);
            });
        }

        function stopCapture() {
            video.pause();
            localMediaStream.stop();
        }
    </script>
</body>
</html>
  • Line 40: PageMethods support.
  • Line 60-61: Get the base64 string from the image and call the proxy Upload method.

The server side:

public partial class _Default : System.Web.UI.Page
{
    [WebMethod]
    public static void Upload(string base64)
    {
        var parts = base64.Split(new char[] { ',' }, 2);
        var bytes = Convert.FromBase64String(parts[1]);
        var path = HttpContext.Current.Server.MapPath(string.Format("~/{0}.jpg", DateTime.Now.Ticks));
        System.IO.File.WriteAllBytes(path, bytes);
    }
}
  • Line 6: As can be seen in the client debugger above, the base64 has a prefix. So we parse the string on the server side into two sections, separating the prefix metadata from the image data.
  • Line 7: Into bytes.
  • Lines 8-9: Save to a local photo. Replace with whatever you need, such as storing in the DB.

Addendum
There are several considerations you should think of:

  • What happens if the camera provides a source of different dimensions?
  • Browsers that do not support these capabilities.
  • The quality of the image. You can use other formats and get a better photo quality (at the price of a larger byte size).
  • You might be required to clear the ‘src’ attribute of the video and or img elements, if you need to reset them towards taking a new photo and ensuring a “fresh state” of these elements.
 
3 Comments

Posted by on 01/06/2014 in Software Development

 

Tags: , , , , , ,

MVC 4 Code Enhancements

Here are 2 minor nice enhancements for MVC 4.

Conditional Attribute Enhancements
This enhancement is something I really like. If you are accustomed to write server-side code embedded with HTML, you probably ran into ugly spaghetti code like this:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    @{
        string css = "myDiv";
    }
    <div class='@{ if (css != null) {<text>@css</text>} }'></div>
</body>
</html>

In MVC 4, one enhancement allows you to save quite a lot of spaghetti confusing code, by interpreting the Code Nugget for you, like so:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    @{
        string css = "myDiv";
    }
    <div class='@css'></div>
</body>
</html>

Even more, you can really shorten things by inserting lengthy strings and render less HTML:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    @{
        string css = "class=myDiv";
    }
    <div @css></div>
</body>
</html>

Note that if you would have used apostrophes, they would have been HTML encoded. So this:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    @{
        string css = "class='myDiv'";
    }
    <div @css></div>
</body>
</html>

is rendered like this:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
 <div class=&#39;myDiv&#39;></div>
</body>
</html>

If you would like to avoid encoding, just use Html.Raw( ) as usual:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    @{
        var css = @Html.Raw("class='myDiv'");
    }
    <div @css></div>
</body>
</html>

Note that Html.Raw returns an IHtmlString, and this works just as well. It seems like the Code Nugget simply performs a ToString( ) with HTML encoding on the given variable. This can be tested easily. Consider this code:

using System.Web.Mvc;

namespace MVCEnhancements.Controllers
{
    public class MyModel
    {
        public override string ToString()
        {
            return "t'is my code";
        }
    }
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new MyModel());
        }
    }
}

and the corresponding cshtml (note the @Model in line 9):

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    @{
        var css = @Html.Raw("class='myDiv'");
    }
    <div @css>@Model</div>
</body>
</html>

This renders the following:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <div class=&#39;myDiv&#39;>t&#39;is my code</div>
</body>
</html>

As you can well see, ToString( ) was called and it was also Html Encoded.

URL Resolution Enhancements
Instead of using Url.Content with a tilde,

<!DOCTYPE html>
<html>
<head>
    <script src='@Url.Content("~/Scripts/jquery-1.6.2.js")'></script>
</head>
<body>
    <div>
    </div>
</body>
</html>

You can just use the tilde like so:

<!DOCTYPE html>
<html>
<head>
    <script src='~/Scripts/jquery-1.6.2.js'></script>
</head>
<body>
    <div>
    </div>
</body>
</html>
 
2 Comments

Posted by on 20/05/2012 in Software Development

 

Tags: , ,

Long Poll Sally – ASP.NET MVC AsyncController

Just like the rest of us, I use polling occasionally. When I learned of WebSockets, I came across my reading about a term I wasn’t acquainted with: Long Polling (or “Comet“). It can be viewed schematically here. To put it simply, while polling can be explained as a series or recurring requests to the server for updates, Long Polling is more or less the same, only technically, the client opens a request to the server, but the server doesn’t return an immediate response and keeps the connection open. When the server has a response, it will use that open connection to notify the client. The client then has to perform another request to reopen a connection for the next update.

At first, when I realized what Long Polling is, I didn’t like the idea much. I mean, if you end up reopening a request to the server like regular polling, why bother? This comes in handy only for less-frequent notifications from the server side, otherwise its just like Polling. Besides, with the new WebSockets in place – who needs it? But then I did some more reading and decided that it might come in handy after all. The main problem I see for using WebSockets is IE9. The major browsers (Chrome, Firefox and the soon to be released IE10) support WebSockets. Cool. But IE9 does not support it. “Fine” you say, have your users upgrade their browsers to IE10. Ahhhh, but MS has decided to limit IE10 to Windows 7 or above. MS is taking several steps to force us to upgrade to their latest releases: No IE10 for Vista, just like WebSockets isn’t planned for IIS7. Funny enough, Google’s Chrome supports XP and Firefox is also compatible with older Windows (in fact, a short visit to Wikipedia reveals that: “Windows XP SP2, XP SP3 and above is now the minimum requirement for Firefox 13.0 and above”…) I find it quite amazing, that MS’ latest browsers cannot be installed on their own operating systems, while their biggest competitors can. It’s as if Microsoft believes that users will upgrade to their latest operating systems just to get their better browser, when they have major competition which is compatible with legacy Windows. Gregg Keizer rightfully points to an anonymous comment on IE’s blog: “IE9 is the new IE6.” Microsoft is working so hard on renewing IE and kicking out IE6, but it performs such a strategic mistake by leaving IE8 and IE9 for a long long time.

Anyway, that’s a major issue as far as I’m concerned. IE6 to IE9 will probably remain with us for quite sometime, as many users still use Windows XP or Vista. All of these users will not enjoy WebSockets support, and we developers will have to continue to develop alternatives to MS’ browser policy of not allowing their own users to upgrade. I guess that this more or less explains why Polling and Long Polling are still relevant: we simply can’t rely on WebSockets alone in the upcoming years.

“Hello World”

Following several blog posts, especially this one by Clay Lenhart, I ended up with the following “Hello World”-like example. Here is the client. It’s fairly simple and self explanatory:

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Simple</title>
</head>
<body>
    <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js"></script>
    <script type="text/javascript">
        $(function () {
            openConnection();
        });

        function openConnection() {
            $.post("@Url.Action("Simple", "MyAsync")", function (results) {
                if (results.Data) {
                    $('#log').text(results.Data);
                }

                openConnection();
            });
        }
    </script>
    <div id='log'>
    </div>
</body>
</html>

Here is the server side (explanation below):

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public void SimpleAsync()
        {
            AsyncManager.OutstandingOperations.Increment();

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(10));
                AsyncManager.Parameters["contents"] = DateTime.Now.TimeOfDay.ToString();
                AsyncManager.OutstandingOperations.Decrement();
            });
        }

        public JsonResult SimpleCompleted(string contents)
        {
            return Json(new { Data = contents });
        }
    }
}

The server code works similarly to the Web Services Async pattern: There is a ‘Begin’ (SimpleAsync), and an ‘End’ (SimpleCompleted). Here is what we see here:

  • Line 8: A would-be async controller must derive from AsyncController. This is what gives us the AsyncManager property through which we accomplish async behavior in MVC.
  • Line 10 is where the async begins. Note, that while the client called the “Simple” Action, our method is called SimpleAsync. In Line 22 you will find it’s counterpart callback method which will be called upon completion (“complete” callback).
  • Lines 12 & 18 are completing operations: In line 12 you announce how many “outstanding operations” you have, which is simply a counter you set at the start of the async operation. When the async operation ends you decrement this counter. When the counter reaches zero, the “completed” callback is called and the response returns to the client. This resembles a C++ reference counter for a smart pointer.
  • Lines 14-19 simulate an async task call. This is where you call time consuming logic, or where you register to receive notifications for two-way communication, simulating WebSockets behavior.
  • Line 17 is how you may pass arguments to the “completed” callback (Line 22).

Here’s a screenshot from IE9 running as IE8:

  1. Response from the server.
  2. Interval of the 10 second requests.
  3. Current pending request.
  4. Browser running in IE8 mode – after all, this is what this effort is all about.

There you have it – quite a simple mechanism to perform async MVC operation, which can evolve into a WebSocket-like operation (see below).

Multiple “outstanding operations”

The OutstandingOperations property which returns an OperationCounter class which is a sort of a reference counter for “pending async operations”. This means that if you have multiple async operations scheduled per a single async request, you may set the counter to the number of those operations, and have each operation decrement the counter when it completes. When the counter reaches zero, the “completed” callback will be returned. Here’s an example:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public void MultipleAsync()
        {
            AsyncManager.OutstandingOperations.Increment(3);

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                AsyncManager.Parameters["param1"] = DateTime.Now.ToString();
                AsyncManager.OutstandingOperations.Decrement();
            });

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(2000);
                AsyncManager.Parameters["param2"] = DateTime.Now.ToString();
                AsyncManager.OutstandingOperations.Decrement();
            });

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(3000);
                AsyncManager.Parameters["param3"] = DateTime.Now.ToString();
                AsyncManager.OutstandingOperations.Decrement();
            });
        }

        public JsonResult MultipleCompleted(string param1, string param2, string param3)
        {
            return Json(new { Data = string.Format("{0} {1} {2}", param1, param2, param3) });
        }
    }
}
  • Line 12 states that there are three async pending operations to be completed before returning a response.
  • Three new threads simulate those async operations. Each completes in a Decrement being called, to signal an async operation completion (lines 18,25,32). Each also provides a different argument with the result of the pending operation, which will be passed on to the “completed” callback.
  • Line 36: the “completed” callback receives the results of the pending operations and returns a response.

WebSocket-like implementation

UPDATE: Before you dig into this code, you might be interested in SignalR. Turns out that this is a broadcasting solution from guys at MS. It supposed to auto-detect and use WebSockets, Long Polling or whichever technology is available on the client. I have not tried this yet, but it looks very promising. You can see Scott Guthrie talk about this here (1:09:38).

Now it comes down to the “main event”: Implementing a WebSocket like implementation. Instead of opening a WebSocket from the browser to the server for two-way communication, the idea is to open a request which the server will maintain asynchronously but not so much for the purpose of supporting a lengthy operation, as much as to maintain the connection for a future response from some business logic component. Here’s an example for accomplishing this. First, the publisher (i.e. class which publishes business logic events to subscribers):

using System;
using System.Timers;

namespace LongPolling.Controllers
{
    public static class MyPublisher
    {
        public class MyEventArgs : EventArgs
        {
            public string Contents { get; set; }
        }

        public static event EventHandler<MyEventArgs> Notify;

        private static Timer timer;
        static MyPublisher()
        {
            timer = new Timer();
            timer.Interval = 10000;
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.AutoReset = false;
            timer.Start();
        }

        static void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                timer.Stop();

                if (Notify != null)
                {
                    Notify(null, new MyEventArgs { Contents = DateTime.Now.ToString() });
                }
            }
            finally
            {
                timer.Start();
            }
        }
    }
}

Not much here to tell. The MyPublisher class simulates receiving notifications from the business logic using a simple timer, that every 10 seconds publishes to subscribers the current DateTime. Next comes the AsyncController:

using System;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public void LogicAsync()
        {
            AsyncManager.OutstandingOperations.Increment();

            MyPublisher.Notify += MyPublisher_Notify;
        }

        private void MyPublisher_Notify(object sender, MyPublisher.MyEventArgs e)
        {
            MyPublisher.Notify -= MyPublisher_Notify;
            AsyncManager.Parameters["contents"] = e.Contents;
            AsyncManager.OutstandingOperations.Decrement();
        }

        public JsonResult LogicCompleted(string contents)
        {
            return Json(new { Data = contents });
        }
    }
}
  • Line 12: Register to receive an event from the business logic class (“MyPublisher”). MyPublisher will raise an event each time it attempts to notify whatever message to registered clients.
  • Line 15 is the event handler which is raised by MyPublisher. Here we set the argument to be passed to the “completed” callback and decrement the Outstanding Operations reference counter.
  • Line 17 is probably the most important here as it’s the easiest part to forget: we have to release the event handler from MyPublisher, or MyPublisher will keep a reference to our AsyncController instance and it will never be Garbage Collected. In other words: a managed code memory leak. This is just like in Winforms: a form registers with a business class to receive events in order to display a progress bar or some other UI feedback. When you are done you call a Close( ) on that form. You mistakenly believe that because you called Close and don’t see the form anymore – it is disposed of and eligible for Garbage Collection: not remotely. The business class maintains a reference to the form instance and it is not removed at all. After a while, you begin to witness your memory growing more and more although you’re certain that you have cleaned up everything, and you end-up “windebugging” with SOS to detect that leak. In the screenshot below you can see how the event’s invocation list gets populated with just several requests from the same client browser. The event handlers are never released, which cause the MyAsyncController instances generated per request to remain in memory. By issuing a ‘-=’ we unregister correctly from the event, and the invocation list is kept clean.

Regardless of the potential memory leak described above, the major difference in this implementation from the previous sample is conceptual, and not necessarily technological: In the previous sample, the async method was used to start an async operation, which actually opened new threads. When the threads complete, each decrements the reference counter thus signaling the end of the lengthy async operation. The entire process was started as a browser initiative and simply freed ASP.NET’s threads for the time of the lengthy operation. However, in the current example, the browser opened a request for no particular operation but in order to wait for a callback from the server. The server on an entirely different logical class (“MyPublisher”) will decide when and what to return to the client. A server may return different operations, such as “display notification” about a new email, or “redirect to another page” and so forth.

Timeout

Moving on to implementing a WebSocket like behavior, one issue that was bothering is the Timout. It’s one thing to use an async server operation which is due in a reasonable time; it’s another thing when you attempt to implement a WebSocket-like behavior that may time-out due to less-frequent updates. For example, if you would like to implement a chat, updates might be frequent, but if you implement a mechanism which is most of the time idle, and just prompts notifications every now and then to the end user, we could be discussing here relatively long interval updates. Consider Gmail. When you are positioned on an email correspondence, if suddenly you receive an email related to that correspondence – a notification appears informing you that there’s a new message and you may click on “show” or “hide” to proceed. This is a good example for when a website may be open for a lengthy time without any operation, but a server may “push” a message when relevant. Therefore I tried to test whether a Timeout occurs on the client or the server, by a simple Thread.Sleep() to lengthy time intervals on the server side. It seems that an AsyncController (MVC 4) defaults to 45 seconds. After that, the client is expected to receive the following timeout exception:

“System.TimeoutException: The operation has timed out.”

I have no idea why, but settings the Timeout property seems to be completely ignored. For example, setting the Timeout to “infinite” (Timeout.Infinite) or to a simple 60000 ms (and extending the server’s Sleep to a much lengthier operation), returned a Timeout exception after 45 seconds. Luckily, I read here that you could set a NoAsyncTimeoutAttribute on the async method to state that there should not be a timeout, and in fact – this actually works (viewing the Timeout property in a debug watch shows -1, which is the value of Timeout.Infinite, but it actually works.)

Now that I have server Timeout set to infinite, I tested different clients’ behavior. On Chrome and Firefox – the browsers did not timeout even when I set the server to respond after 6 hours. This was a good behavior as far what I expected. However, in IE it timed out. This was quite frustrating, because IE is the target of this Long Polling alternative in the first place. If it times out, it means that now I have to detect how long it takes before the client times out, and that I have to implement a mechanism for having the server return a response no longer than that timeout value or I’m risking losing the connection with the client. Testing this several times shows that IE times out at approx 60 minutes (see pics below).

Now comes the harder part, which is what to do when those 60 minutes time-out. Remember: the idea is to return a response to the client browser before reaching the client time-out, and have the client re-open a new connection. I thought I could use a simple Timer for this. The timer has to be set to a predefined interval (e.g. 30/45/60 minutes etc. – depends on how much risk you wish to take here…), and decrement the Outstanding Operations reference counter if it elapses. This way the server returns a response to the client, which the clients reads as “timeout”, meaning that there’s nothing to do but re-open a request.

Here’s the code:

using System;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        private System.Timers.Timer timer;

        [NoAsyncTimeout]
        public void LogicAsync()
        {
            this.timer = new System.Timers.Timer();
            this.timer.Interval = 4000;
            this.timer.AutoReset = false;
            this.timer.Elapsed += delegate
            {
                timer.Stop();
                MyPublisher.Notify -= new MyPublisher_Notify;
                AsyncManager.Parameters["contents"] = "timeout";
                AsyncManager.OutstandingOperations.Decrement();
            };

            this.timer.Start();

            AsyncManager.OutstandingOperations.Increment();

            MyPublisher.Notify += MyPublisher_Notify;
        }

        private void MyPublisher_Notify(object sender, MyPublisher.MyEventArgs e)
        {
            this.timer.Stop();
            MyPublisher.Notify -= MyPublisher_Notify;
            AsyncManager.Parameters["contents"] = e.Contents;
            AsyncManager.OutstandingOperations.Decrement();
        }

        public JsonResult LogicCompleted(string contents)
        {
            return Json(new { Data = contents });
        }
    }
}
  • Line 8 declares a Timer variable. This timer is started in the ‘Begin’ method (LogicAsync) and is set to 4 seconds (just for this example – it’s supposed to be set to a much lengthier interval but less than 60 minutes).
  • Lines 16-22: When this timer’s Elapsed event is raised, this means that the request is timing out and should be renewed, so we unregister from the publisher (we do not want to process the event handler during re-connection), and decrement the Outstanding Operations reference counter in order to immediately return a response to the browser. The browser is supposed to interpret this response as “do nothing” (NOP) and re-open a request.
  • Line 33: If the timer did not elapse but we did get an event from the Publisher, we immediately stop the timer, and execute the returning response as normal. This ensures that the timer won’t elapse till we finish implementing the response logic.

In the example above, assuming that a MyPublisher is set to return a response every 10 seconds, you are expected to see two “NOP” responses per one correct response of the handler being raised:

await/async

This here is just an extra to this post. I was thinking whether I could use the new async lib from C#. I came up with this:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public async Task<JsonResult> MyAction()
        {
            string contents = null;
            await Task.Factory.StartNew(() =>
            {
                Thread.Sleep(TimeSpan.FromMinutes(30));
                contents = DateTime.Now.TimeOfDay.ToString();
            });

            return Json(new { Data = contents });
        }
    }
}

The code above seems to be working well but I’m somewhat uncertain whether it’s the correct implementation or best practice for using await/async in MVC.

UPDATE: In Scott Guthrie’s lecture (1:15:25), you can view async/await support which is similar to the example above.

However, when I tried to rewrite the “MyPublisher pattern” using await/async (excluding the timer part), I ended up with something I wasn’t pleased with:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public async Task<JsonResult> MyAction()
        {
            ManualResetEvent ev = new ManualResetEvent(false);
            string contents = null;
            EventHandler<MyPublisher.MyEventArgs> d = null;
            d = delegate(object sender, MyPublisher.MyEventArgs e)
            {
                MyPublisher.Notify -= d;
                contents = e.Contents;
                ev.Set();
            };

            await Task.Factory.StartNew(() =>
            {
                MyPublisher.Notify += d;
                ev.WaitOne();
            });

            return Json(new { Data = contents });
        }
    }
}

Nope, I was clearly unhappy with this code.

Summary

To be honest – I’m not thrilled at all to have to use Long Polling. I didn’t like polling either, but polling was simple – the browser makes requests at (almost) regular intervals, checks for updates. End of story. But with IE10 limited to Windows 7 and above, it seems like Long Polling is a possible solution to achieve a “less frequent polling” but still with “real-time” updates. Having said that, one needs to remember that other solutions are available such as using Flash or Silverlight for achieving two-way communications, and to be honest, I’m uncertain how Google Docs document sharing was implemented. You might also be interested in pubsubhubbub which is based on Atom/RSS feeds (at least watch their really cool “what is PubSubHubbub” video). There are other utils which provide reverse-Ajax and server-pushing such as WebSync, but I haven’t used them myself.

UPDATE: As specified above, SignalR is a .NET based solution from guys at MS which you should also probably look up.

Although the majority of my blog posts relate to stuff that I am learning and documenting as I go along, in this particular case I feel like Long Polling is a technique that I have not experienced sufficiently in order to recommend it. Although this is also true for WebSockets, WebSockets are designed to provide a two-way communication so I was feeling a lot more comfortable recommending them. I see Long Polling not as a technology on its own, but rather as a technique which is designated to provide a solution to a problem using existing technology, which isn’t specifically targeting that same problem. However, I am planning on implementing this soon in order to learn whether it proves itself as reliable and comfortable.

UPDATE: I attempted to implement a Publisher which will be able to filter out the browser that initiates updates to the rest of them using the above technique. My solution had one open Long Polling Ajax request and another Ajax request posted to the server from the same browser representing a user’s update. This was supposed to be nothing too clever: just exclude the current user from the Publisher’s event notification list. I thought of using the Session’s ID to make that distinction, when I noticed that my async requests got stuck almost immediately. At first I thought that this was related to the number of requests I had open from several browsers over a Windows 7 workstation, but slowly and painfully I discovered that this was because I inserted something into the Session in order to make the Session ID sticky. Googling revealed a reason: seems like AsyncController locks the Session in order to make it thread-safe between open requests from the same session. In order to use the Session from an AsyncController you probably have to declare it as read-only using the SessionState attribute. I have not found an “official” explanation for that yet although I’m sure that there is, and the best I found so far was this, this and this blog post. If anyone finds a more documented or official blog from MS guys about this, please comment.

Last credits

Little Richard gets the credit for this blog post’s title.

 
2 Comments

Posted by on 19/03/2012 in Software Development

 

Tags: , , , , , , , ,

How to write a code based ValidationExpression using ExpressionBuilder

Here’s a pain: write an ASP.NET ExpressionBuilder. Here’s another: use a ValidationExpression for a RegularExpressionValidator which isn’t hard-coded, Resource based or App Settings based, but code based. This post will cover both pains, but will open a “world of options” for using custom ASP.NET Expressions.

ASP.NET Expressions

To those unfamiliar with ASP.NET Expressions, here’s a brief: you can set property values in a declarative way using Expressions. For example:

<asp:Label runat="server" Text="<%$Resources: Description %>"/>

Assuming that you have a corresponding resource file, the user will see the value set there for the item that has the Description key. I won’t get into this in this post, but I’m a big fan of using Resources in ASP.NET, so Expressions are something I use often.

ASP.NET comes with 3 out-of-the-box Expression “types”: Resources, Connection Strings and App Settings.

The RegularExpressionValidator Situation (Pain number 2):

I assume that most of us who develop using WebForms and ASP.NET Validators have used the RegularExpressionValidator at some point. Using the ValidationExpression property, we can easily set a Regular Expression to validate our users input for stuff like emails, phone numbers, zip codes etc. Very comfortable:

However, a ValidationExpression has to be either hard coded, or retrieved using an Expression. This means that if you need to use the same regular expression in different locations, you’ll either have to copy-paste it, or use an Expression based solution. Hard-coding and copy-pasting a ValidationExpression could prove to be a bad practice, especially for generic validations such as emails and the like, because you may want to change the tested expression, and in such a case you’ll have to find all those locations you now have to replace. Using Expressions is a better solution because by placing a regular expression in a Resource file or App Settings, you can at least avoid copy-pasting it and you have single location where changes can be made. However, both of these Expression-based solutions are somewhat an ugly workaround, because you’d usually want your regular expression in a Shared/Common dll, so other components may use it, and not only your ASP.NET validators. While AppSettings can still be used across your different assemblies, Resources cannot (well, you can use Reflection but that’s another story). Finally, your expression may reside in a Property and contain logic, so now you have to have a code based solution. For example, let’s assume that you need a regular expression validation which is culture oriented – you probably have to use thread culture code to accomplish that. Unfortunately, you cannot use server side script tags to fetch a static, const or property. For some reason I’m not familiar with, MS has not provided what should have been one of the most basic features in ASP.NET, and that is to use the <%= %> from within declarative syntax:

The above code compiles and runs, but literally renders the server tags themselves instead of resolving them. You can use <%= %> in your JavaScript code, or even inside a regular html block, but you can’t use it in declarative properties! This is beyond me, although I guess that there’s a good explanation to this.

Anyhow, if you do want to use a regular expression placed in a const, static field or a static property as written in the following code, you are now stuck, or you are forced to code-behind this in your page and give up the declarative ValidationExpression.

StaticExpression (Pain number 1):

I decided to overcome this situation using a custom Expression. I have written just a small number of expressions all these years, and each time I found myself wasting precious time on remembering how to do this, googling for sample code etc. There are several good resources on the web for writing custom Expressions. Here’s one and another from MS. To summarize this, I’ll just say that basically there are 4 steps to writing your own a custom expression:

  1. Write a custom Expression class by inheriting from ExpressionBuilder (e.g. StaticExpression class that will accomplish retrieving static, const or Property values).
  2. This class should have a static method which actually performs what you want to do and return the desired string.
  3. This class should also implement the abstract GetCodeExpression( ) method, which simply uses CodeDom objects to call the method you created in step 2. (Note, that you may choose to implement the code directly from this method and skip the static method in step 2, as shown in MS’ sample).
  4. Add the custom Expression to the web.config, so that ASP.NET will now how to resolve the declarative Expressions in your pages.

Here is the StaticExpression class, which supports returning a string from a const or static field/property using simple Reflection (explanation below). I placed this class in the App_Code:

  • Lines 9-18 is the CodeDom method which returns the custom method which actually does all the work (lines 20-71).
  • Lines 26-35 parse and determine whether the expression contains not only the class and field/property, but also an assembly name where to retrieve it from. Note, that you can alternatively change the code so that if no assembly is specified, some default assembly is used (in such a case you’ll have to insert a line of code adjacent to line 34, which states: assemblyName=”<your default assembly name>”.
  • Lines 29-30 assumes that the expression specifies an assembly, and that the assembly is specified prior to the const/property. This resembles working with Global Resources, but this doesn’t have to be the case. You may choose to specify the assembly after the const/property name, resembling how Reflection’s Type.GetType work (line 47 shows this).
  • Lines 38-47 resolves the type of the class to be used. The expression, without the assembly, is parsed so that we’ll have the class (including namespace if specified) separated from the field/property name.
  • Line 50 retrieves the actual class type.
  • Lines 54-58 attempt to detect if the name relates to a static field or const, and retrieve it’s value (the null in Line 57 implies a static field or a const).
  • Lines 59-64 relate to properties (the first null in Line 63 implies a static property and the second null means that there are no arguments to this property).
  • Lines 67-68 will throw an exception if the text could not be retrieved.

The declarative code in your Page:

  1. The Label’s Text demonstrates how to retrieve MyConst const from a Consts class residing in the App_Code (no assembly specified).
  2. The RegularExpressionValidator’s ValidationExpression demonstrates how to retrieve a static Property value from a class residing in a different assembly.

And the web.config, which states that when a “Static” keyword is used for an Expression, resolve it using the StaticExpression class:

Final notes

You may also choose to inherit ResourceExpressionBuilder instead of the abstract ExpressionBuilder. This could be handy if you need a custom Expression that will fallback to resolving a Resource Expression. For example, if your page is running in two different custom modes, X and Y, you could change the resource key stated in the declarative property and only then proceed to retrieving the resource, by calling the base class methods. Your code will require changes, but it’s certainly achievable.

 
Leave a comment

Posted by on 11/11/2011 in Software Development

 

Tags: , , , ,

Removing the IIS 7 Server header in a Classic application pool

There are quite a few blogs that describe how to remove the common headers sent to the browser by ASP.NET and IIS. Here’s an example from the iis.net website:

As you can see, the headers give away which web server and technology is running the website. To be honest, I’m not entirely sure why IIS or ASP.NET should send these headers in the first place, but it never bothered me all these years till now. Apparently, some security specialists consider this a threat, because potential hackers might exploit this knowledge to execute known security hacks. I was puzzled by this claim, because I think that removing these headers will be of little help to prevent such exploits. For example, in ASP.NET it’s enough to see the ‘.aspx’ in the url to know that it’s ASP.NET (and therefore, IIS). You’d have to use Routing to overcome this, which means that either you should have thought of this prior to development, or you have lots of work to do now (strange enough, the security specialists do not necessarily require this change.) Other examples for detecting which technology is used even without having those headers sent over, come from the actual content downloaded from the server, which can be easily viewed using browser development tools. Here are several examples: Microsoft uses a lot of ‘.axd’ files for web resources; the html rendered from ASP.NET usually has a __VIEWSTATE hidden field; web sites which use ASP.NET themes render style sheets from the App_Themes folder.

Having said that, I looked for information on how to remove those headers. It seems like those three headers, X-Powered-By, X-AspNet-Version and Server, are controlled in entirely different locations. As there are quite a few blogs about this topic, I’ll be happy to simply refer to an excellent blog post, which explains how to remove most headers. However, the reason I’m writing this blog is not because I wanted to provide this url, its because it took me some time to remove the Server header from an IIS 7 running an application pool in Classic mode. For Integrated mode, this can be done via code. But this doesn’t seem to work for Classic app pools. So how do we remove the Server tag in such cases?

I preferred a programmatical solution that would not force a change in IIS configuration. Unfortunately, all my coding attempts failed to remove the Server tag in IIS7 Classic mode. In order to accomplish this, I had to download and install a utility from MS, UrlScan. UrlScan version 3.1 supports IIS7 so it provided a working solution. Although it lacks a graphical interface, setup was quite easy:

  1. Start->Run->inetsrv and hit enter.
  2. After installation, there should be a UrlScan folder. Go there.
  3. Edit the urlscan.ini file. If there isn’t one, you can download one from here.
  4. Depending on your need:
    1. Set RemoveServerHeader=1 to remove the Server header completely.
    2. Set AlternateServerName to a string of your choice to change the header sent over.

One side effect I had was that all of a sudden, the browser requests for css files returned a 403 forbidden code. I wasn’t sure why this happened, but it was obviously the result of installing the UrlScan. I found that AllowDotInPath was set to 0 and that this was the cause of it. I changed it quickly to ‘1’ and the problem was solved. No other problems have arisen so far.

You can also read the full UrlScan reference here

 
2 Comments

Posted by on 25/08/2011 in Software Development

 

Tags: , , , ,

ASP.NET WebForms Routing

This post is more of a “technical completion” post for using Routing in WebForms, which covers some of the issues I have come across. If you require a quick start on Routing, or technical MVC stuff, please read this post first.

I was happy to learn that it’s quite easy to use MS’ built-in Url Routing solution for ASP.NET WebForms. This is quite easy to accomplish in IIS 7, and requires some tweaking in IIS 6. Read this on how to configure your web.config file to support routing according to your IIS configuration. You may want to check that Url Routing is installed.

One pit fall that I’ve encountered was when I needed to deploy a routing solution on a certain server. There seemed to be a slight configuration difference from my development machine, and routing did not work. What I eventually did was to perform a minor tweak in the configuration file, and added a runAllManagedModulesForAllRequests as described here. Note that there are those who consider this configuration tweak “inappropriate” as far as performance, resources and error prone, but their suggested solutions didn’t seem to solve my issue. I decided to perform a simple check, what ASP.NET MVC’s template is configured like – and runAllManagedModulesForAllRequests=true is exactly that. I reckon that if the guys at MS provide this configuration in ASP.NET MVC, it’s good enough for WebForms too.

Enough with the configuration, where are those pitfalls?

As with ASP.NET MVC, the routes are set in the Global.asax file. Here are some routes that will be used in this post:

get.aspx is the sample page in this post. It contains a single PageMethod and will be our test case:

PageMethods
Unlike Web Services (asmx) WebMethods, there is an issue with Url Routing when it comes to PageMethods. It seems like the client is unable to provide the correct url for the page, and the server seems to fail in figuring out which page and method should be invoked. The error received in the client is “405 Method Not Allowed“, and the response contains a somewhat confusing “The HTTP verb POST used to access path ‘…’ is not allowed.” message, which basically means that the Url isn’t resolved correctly and therefore does not reach the correct PageMethod.

Indeed, the Url the client sends by default to our get.aspx page is: “http://localhost:54885/WebSite2/get/1/Get&#8221;.

  • The first “get” is the route which routes to get.aspx;
  • The “1” is a simple {id}.
  • The second “Get” is the PageMethod.

Because we received the 405 error message, I used the Route Debugger, and as expected, the Route Debugger showed a “No Match” on that url. Initially I thought that it would be easy to write a RouteHandler and provide a generic solution for PageMethod urls, but after I started coding I noticed that it’s not as easy as it may seem. Possible, but not as easy as the workaround. In order to workaround, all you have to do is to set the path in the client JavaScript to the correct page and you’re done. This is done like so:

Note: You can read here how to call your PageMethods using jQuery.

HttpHandlers – ashx files

It seems like ASP.NET 4 has no support for routing to ashx handler files. When you attempt to route to an ashx you’ll get the following exception:

Type ‘MyHandler’ does not inherit from ‘System.Web.UI.Page’.

Fortunately, this is quite easy to get by, because coding a Route handler which will take care of ashx files is trivial enough. Note, that the solution suggested here is based on a Web site and not a Web application, so the route handler code resides in App_Code.

So, there are three steps to this:

  1. Refactor your ashx code to an App_Code class which inherits from IHttpHandler.
  2. Write a simple Route handler which will create that handler when requested to.
  3. Add the new route handler to the routes defined in Global.asax.

Step 1: Refactor the handler – basically, this is your starting point for your actual “business” implementation (although I encourage calling a Business Layer from here):

Step 2: Create a simple Route handler to return the IHttpHandler:

Step 3: Register the new route in Global.asax:

That’s all there is to it. Now you can use a route for targeting your handler, for example:

http://<server>/handler/123

UPDATE: For routing Web Services (asmx), click here.

Route Data

One last issue is in cases that you perform url rewriting or other processing, and you suddenly notice that your Route Data is gone. This is quite frustrating as you can plainly see that url in the browser’s address bar. Fortunately, the solution is quite simple and also uses a Route Handler. All we have to do, is copy the Route Data in a custom Route handler to HttpContext.Current.Items collection, and we have them for the entire http request. Two steps here:

Step 1: Write a simple custom Route handler which copies the route data:

Step 2: Configure Global.asax accordingly:

To sum up – it’s quite cool to be able to use Routing in WebForms. I just finished a small project where I used Routing and the result is quite awesome. It wasn’t just looking better, but it also allowed me to “white label” the website dynamically. For example, I could do the following: http://server/productA/login and http://server/productB/login, and it really felt like two different websites.

 
4 Comments

Posted by on 22/06/2011 in Software Development

 

Tags: , ,