RSS

Tag Archives: Ajax

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: , , , , , ,

Posting complex types using ASP.NET Ajax, WebForms

This is a minor post, which is some sort of a completion to the previous post dealing with the same topic only for using MVC. So, if you require background, you may want to consider reading that post first.

Fortunately, ASP.NET Ajax makes it really easy to send JSON back and forth. Using jQuery, there is some tweaking that had to be done in order to get this to work. However in WebForms usnig ASP.NET Ajax PageMethods or WebMethods – ASP.NET takes care about the technical details, leaving you to implement only the logic. Note that the same technique and guidelines are used just like in MVC: the data is sent from the Client browser to the Server using POST and in JSON format, with a Content-Type of application/json. All properties must match in data-type and names.

This is the client code:

    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" EnablePageMethods="true" />
    <input type="button" value='click me to test' onclick='Test();' />
    <script type="text/javascript">
        function Test() {
            var data =
            {
                people: [
                    {name: 'Joe', age: 20},
                    {name: 'Jane', age: 30}
                ]
            };

            PageMethods.Test(data, function (result){
                alert("Done");
            });
        }
    </script>
    </form>

And the server side, containing classes matching the client type:

    [WebMethod]
    public static void Test(MyPeople data)
    {

    }

    public class MyPeople
    {
        public IEnumerable<Person> People { get; set; }
    }

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }

And the traffic:

As you can see for yourself, this is pretty much straight forward. ASP.NET Ajax client infrastructure takes care to POST the data and send it in JSON format and the appropriate application/json content-type, which is great.

 
Leave a comment

Posted by on 27/07/2012 in Software Development

 

Tags: , , , ,

Posting complex JavaScript types to MVC, using Ajax

This post demonstrates how to send Complex client types to MVC using JSON. If you require the same information but for ASP.NET WebForms, you can go and read this post instead.

Background
Sending data to MVC using jQuery is something that I wrote about in several blog posts. Just as a quick reminder, this post discusses the basics of sending data, including simple-typed arrays. The problem is that I had to send over an array of complex-typed objects. Originally, this was intended to be a post which performs some sort of a work around by sending a JSON string to the server, and uses a JavaScriptSerializer & dynamics to retrieve the objects. But, after Googling some more it turns out by sending and receiving data in a certain way, MVC will perform this process out of the box, by binding the sent data to your custom server side classes. For simple types, sending data to the server is very “forgiving” and easy. But if you require to send complex types which involve JSON strings – you have to be more strict and follow “the rules”.

If you’re just into the solution, click here. If you would like to see some basic examples and the problem explained, read on.

Basic implementation
Here’s a reminder on how to send simple types, slightly complex types and simple typed arrays. First, this is the server side code:

    public class HomeController : Controller
    {
        public ActionResult Index() { return View("Test"); }

        public ActionResult GotSimple(string name, int age)
        {
            return Content(string.Format("name: {0}; age: {1}", name, age));
        }

        public ActionResult GotArrays(string[] names, int[] ages)
        {
            return Content(string.Format("names: {0}; ages: {1}",
                string.Join(",", names),
                string.Join(",", ages)));
        }

        public ActionResult GotComplexType(string name, int age)
        {
            return Content(string.Format("name: {0}; age: {1}", name, age));
        }
    }

And this is the client code (excluding the buttons html):

<script type="text/javascript">// <![CDATA[
        $(function () {
            $('#sendSimple').click(function () {
                 $.get("@Url.Action("GotSimple")",
                     {
                        name: "Joe" ,
                        age: 20
                     },
                     function (result){
                         alert("Server replied: " + result);
                     }
                 );
            });

            $('#sendArray').click(function () {
                 $.get("@Url.Action("GotArrays")",
                     $.param({
                        names: ["Joe", "Jane"],
                        ages: [20,30]
                     }, true),
                     function (result){
                         alert("Server replied: " + result);
                     }
                 );
            });

            $('#sendComplexType').click(function () {
                var Joe = { name: 'Joe', age: 20};
                $.get("@Url.Action("GotComplexType")",
                    Joe,
                    function (result){
                        alert("Server replied: " + result);
                    }
                );
            });
        });
// ]]></script>

There are three examples here:

  • ‘Simple’ demonstrates sending two simple types and the server receives them “as-is”.
  • ‘Array’ demonstrates that you can send simple typed arrays using $.param with the traditional flag set to true. The server receives them as expected.
  • ‘ComplexType’ shows an even cooler example, that you can send an simple object and receive it’s properties on the server side similarly to the ‘Simple’ example.

Note the traffic sent over to the server: ‘Simple’ (1) and ‘Complex’ (3) send the data exactly the same way, so clearly the server handles the sent data in a similar way. ‘Array’ (2) sends the data in duplicate keys which is received and interpreted by MVC as arrays.

Moreover: MVC also supports receiving the arguments directly to a custom class (e.g. Person), which is really cool:


public ActionResult GotComplexType(Person person)
{
   return Content(string.Format("name: {0}; age: {1}", person.name, person.age));
}

public class Person
{
   public string name { get; set; }
   public int age { get; set; }
}

This actually works (You can read about it here, under “JavaScript and AJAX Improvements”.

Complex typed arrays

But… when I wanted to send an array of complex types, this was more of a problem. First, lets review the client code:

            $('#sendComplexTypeArrays').click(function () {
                var Joe = { name: 'Joe', age: 20};
                var Jane = { name: 'Jane', age: 30};
                $.get("@Url.Action("GotComplexTypeArrays")",
                    $.param({
                        people: [Joe,Jane]
                    }, true),
                    function (result){
                        alert("Server replied: " + result);
                    }
                );
            });

And here is how it is sent over by the browser:

Clearly, this is not what I wanted. OK, let’s try to remove the traditional flag from $.param:

Looks more promising. Let’s review the server side code this time:

public ActionResult GotComplexTypeArrays(dynamic data)
{
    return Content("");
}

Unfortunately “data” is just an object, nothing more, and I did not get what I wanted.

Solution
I Googled and found this post which gave me a good direction. Turns out that MVC will know how to parse your sent data from the client on the server side, but there are a few catches I had to overcome before this properly worked:

  1. (Client) You must send a JSON content-type in the form of: ‘application/json’.
  2. (Client) The data must be POSTed and in JSON format (use JSON.stringify).
  3. (Server) The server data type must match that of the client:
    • Property names must match (although it seems like case-sensitivity is not an issue);
    • Client arrays must match a server IEnumerable type (array, list etc.);
    • Client data type must match the server side data type to a certain extent or you’re risking losing data (e.g. can’t send a client string and receive it as an int on the server side.)
  4. (Server) All properties must be, well, Properties (e.g. get;set; implemented). Can’t use global fields. They should also be public.
  5. (Server) Apparently the name of the argument cannot be identical to one of the properties. I’m not sure why, but this seem to confuse the binder and results in ‘null’. So you cannot call your argument ‘people’, and have a ‘people’ property in that class at the same time. At least not in the immediate class of the argument.

So after making a few changes to my code, here is the client side:

$.ajaxSetup({
    dataType: 'json',
    contentType: 'application/json, charset=utf-8'
});

The above code ensures that Ajax calls sent to the server are with a JSON content-type, and that JSON is to be received from the server (you may choose to add more global jQuery Ajax settings here such as error handling, or POST method etc.).

Important: if you don’t need all of your Ajax calls to be in JSON format, you can simply use the dataType and contentType in explicit Ajax calls. Just replace $.post with $.ajax and use POST.

$('#sendComplexTypes').click(function () {
    var Joe = { name: 'Joe', age: 20};
    var Jane = { name: 'Jane', age: 30};

    $.post("@Url.Action("GotComplexTypes")",
        JSON.stringify({ people: [ Joe,Jane ]}),
        function (result){
            alert("Server replied: " + result);
        }
    );
});

The code above converts the data into JSON format, and will require a ‘people’ property on the server side for binding purposes.

Below is the modified server side code. It receives a ‘data’ (not ‘people’ – remember?), performs binding to the different attributes, matching name to name and data type to data type. Properties and not global fields.

public ActionResult GotComplexTypes(MyPeople data)
{
    return Json(string.Format("People: {0}", data.People.Count()), JsonRequestBehavior.AllowGet);
}

public class MyPeople
{
    public IEnumerable<Person> People { get; set; }
}

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Here’s the traffic shown which can help understand whether you’re POSTing JSON strings correctly:

Summary
Apart for being a really cool feature, to be able to bind JSON strings to server side objects, it can save a lot of effort if you thought of doing so yourself. However, this was no picnic. If you don’t follow “the rules” specified above, expect a hard-time and lots of frustration. But don’t despair – it’s worthwhile once it works, and after the first time, its supposed to get much easier.

 
4 Comments

Posted by on 22/07/2012 in Software Development

 

Tags: , , ,

ASP.NET MVC Partial View and Ajax “real world” example

If you would like this sample code, right click here and “save as” to download. Then rename to a zip file and open.

In my previous post I have discussed and demonstrated how you can easily use jQuery and a ASP.NET MVC’s Partial View to accomplish an UpdatePanel-like solution. In this post I’d like to demonstrate a “real world” usage of this technique.

I wanted a cool looking Login dialog to popup on request when a user hits a simple “Log On” button, without having to refresh the entire page or redirecting the client to a Login page. In other words, there is no Login page, just a Login dialog. Naturally, I wanted this Log On button to be available across all the pages in the site. This could be easily be implemented using a layout (“Master Page”). But it’s more generic and nice to use a PartialView, to represent that button. More over, the basic code for this already exists in the MVC application template. Simply create a new MVC application and see for yourself. So there’s nothing new so far. However, the code for the login dialog is something new which is required and here I had basically 2 options:

  1. I could render the login dialog as a hidden dialog (“display:none”) in the Partial View (or layout). This is possible, will save a round trip to the server when the user requests to login, but is also quite “ugly” as it is inefficient to render a hidden dialog every time.
  2. I could have an UpdatePanel like implementation. Using a placeholder div, I could request another PartialView from the server on demand. The html rendered will be then “injected” into the placeholder div. I figure that this is way more efficient than the first option and can be considered, well, “nice”.

I decided to go with option 2. It felt more cool and correct, and also provided me with a great opportunity to practice this technique. Here’s the basics:

  1. We’ll create (or rather re-use MVC’s template code) a Partial View to represent a Log On/Log Off button. This will be rendered from the layout onto all pages. This view will also contain a place holder div, which will be injected with the log-in dialog.
  2. We’ll create a Partial View for the login dialog. This is basically a simple div which wraps the username/password textboxes. This will not be rendered anywhere at design time.
  3. We’ll use jQuery’s ‘load’ method to perform an Ajax call to the server, and return the Partial View of the log-in dialog. The dialog’s html will be injected into the place holder described in stage 1.
  4. We’ll create server side code to support the logon/logoff scenarios.

“Strategy”: The login button’s partial view is going to have a wrapping div, which will change according to the whether a user is logged in or not. This will be rendered at first according to the authenticity state, but will change at run-time according to the user’s operations. This will be described in 4 stages below.

Stage 1: Login button (Partial View). I reused MVC’s template code to do this. The entire code is wrapped with a div (‘divLoginButton’), whose contents will dynamically change according to Log on/log off operations performed by the user.

  • Line 1 is the wrapping div.
  • Line 2 is the server side code which decides whether a Log On or Log Off button will be rendered from the server.
  • Lines 4-9 represents a Log Off button. The user is logged in, authenticated, and has not requested to logoff (line 2 makes that decision).
  • Line 7 contains an Ajax call to actually perform the log off, and to replace the div’s entire content with a new state. This really resembles a Web Form’s UpdatePanel’s behavior, only cleaner and more efficient.
  • Lines 13-28 represent a situation that the user is not authenticated and a Log On button is displayed.
  • In line 17 we query whether the login dialog has already rendered from the server. This can happen if the user has clicked the Log On button, the dialog has rendered from the server (line 19), but has not logged in (e.g. clicked the Cancel button of the dialog). In this case, there’s no point in reloading the dialog from the server, so we reset the username/password fields (line 22) and reopen the dialog (line 25).
  • Lines 29-30 is the place holder for the login dialog.

Stage 2: Login dialog (Partial View). I created the new LoginDialog.cshtml PartialView file. This looks like this:

  • Lines 1-13 represent the “dialog”. This is just a basic div with username/password textboxes. This will be displayed as a modal dialog using jQuery UI’s Dialog. Because the dialog is set to autoopen, this will be automatically opened when rendered for the first time.
  • Lines 15-43 contains the JavaScript which will display the window as a dialog. If you’d rather have the JavaScript in a different JS file because you believe that rendering a JavaScript ajax response from the server is a bad thing, it’s also possible.
  • Line 23 indicates a POST ajax call. POST is done for security as I’d rather have the username/password (lines 25/26) not sent in the query string. The Ajax call is for the Login action method in the Home Controller.
  • Line 32 is executed when the result of the Login method is “Success”. This is what does the trick of replacing the “Log On” button with a “Log Off”, similar to the signout process discussed in Stage 1 above. It is possible to return the PartialView directly from the Login action (line 23), but I wanted to render a Success boolean in order to have more control, especially in case the Login fails.

Stage 3: Server side code to support the Log On / Log Off situations.

The comments are probably sufficient, but just in case:

  • Lines 15-18 return the Login Dialog partial view.
  • Lines 21-26 supports a Log Off scenario. Line 23 performs the actual Sign Out. Line 24 instructs the LoginButton partial view that although the user is still authenticated at this point, a Log On button should be rendered and not a Log Off button.
  • Lines 30-33 return a Log On or Log Off button, depending on the authenticity state of the user. Basically this is used after a successful login operation, when we would like to render a Log Off button.
  • Lines 36-42 represents a Login process. Line 38 should be replaced with actual Membership user login validation check. If true, line 40 sets the authentication cookie. Line 41 returns if a successful login was performed. As you can see, in this example the login is always a success.

Stage 4: Render the Login button in the layout (“Master Page”), so it’s visible throughout all the pages.

  • Line 11 represents the Login Button partial view (Log On / Log Off button).
  • Lines 5 and 8 are jQuery UI (required for the modal dialog).

Here’s how it looks. At first, the page is loaded and the Login button is rendered as a Log On button.

Now we click the Log On button. jQuery’s load method performs an Ajax request to the server, and the Login Dialog Partial View is rendered back. Because the JavaScript code which creates the dialog has an autoopen set to true, it automatically opens:

Now we type-in our credentials and click Login. A POST Ajax call to the server performs credential validation and returns a JSON with Success, meaning that the Login went ok and the user has logged in. Now another Ajax call takes place to “refresh” the state of the Login Button to that of a Log Off button. This looks like this:

Finally, when we click the Log Off button, another “refresh” takes place as the server’s LogOff button returns an updated Login Button.

That’s about it. As stated above, you could take the JavaScripts to a different JS file if you feel bad about rendering back JavaScript, or if you think that it’s more efficient either because the network load is reduced or because the JS file will be cached by the browser. It’s also possible to render both a Log On and a Log Off buttons at once and toggle their visibility on the client side, settling just for Ajax calls only to perform the credential validation and to set the cookies. In either case, this isn’t the point of this blog post. The point is to show multiple examples of Partial Views used as Web Forms Update Panels, using jQuery.

 
123 Comments

Posted by on 26/04/2011 in Software Development

 

Tags: , , , , ,

ASP.NET MVC – PartialView with Ajax

In this post I will briefly discuss how to use a PartialView either as a Web Form’s “UserControl” or as an “UpdatePanel”. You can click on the links to navigate accordingly, or just read on. UPDATE: If you’d rather have a “real world” example, you can read this post: https://evolpin.wordpress.com/2011/04/26/asp-net-mvc-partial-view-and-ajax-real-world-example/.

I’ve wanted to use a PartialView for quite some time now. A PartialView provides two functionalities which aren’t necessarily overlapping although have much in common. In comparison with Web Forms, a PartialView is a kind of a UserControl, but it also provides a kind of UpdatePanel.

What’s great about a PartialView is that it’s very straight forward – you simply place your HTML and/or JavaScript in a separate View file, probably placed in your Shared views folder so it can be used easily across views. Then you can either use it as a control, by placing Html.Partial(…) calls on your View as desired, or you can render it in run-time as a response to an Ajax call.

UserControl like implementation:

Let’s go quickly over the first usage, that of a “UserControl”: we right-click on the Shared folder to add a new View, give it a name, and select the “Create as a partial view” checkbox:

Our solution explorer looks as follows (Index.cshtml will be using the newly created MyPartialView.cshtml):

MyPartialView contains a simple dummy html place holder which will display a “My View” header; whereas Index.cshtml in this example will now include 2 references to MyPartialView:

1: <table border='1'> 
2:     <tr>
3:         <td>
4:             @Html.Partial("MyPartialView")
5:         </td>
6:         <td>
7:             @Html.Partial("MyPartialView")
8:         </td>
9:     </tr>
10: </table>

That’s about it, and this is how it looks:

One more thing worth mentioning, is that the ViewData dictionary which can be populated in the Controller, is available both to the View and to it’s Partial Views.

Ajax and html injection – “UpdatePanel”:

As I mentioned earlier, Partial Views can also provide a functionality resembling Web Form’s UpdatePanel. Those familiar with UpdatePanel usually either appreciate the (relatively) ease of use, or despise it for being so heavy and inefficient. Almost an Ajax “wannabe”. Personally, I think that UpdatePanel has its place in Ajax side by side with PageMethods/WebMethods. I consider it the better alternative for rendering a “mass” of html onto the client, especially when paging or sorting grids. In MVC, a PartialView could perform this exact functionality, and could prove to be the better solution for rendering plenty of html to the client.

Basically there are 4 steps to achieve this:

  1. Create a PartialView.
  2. Create a place holder html control.
  3. Use jQuery’s load method to fetch the partial view from the server and inject it into the place holder.
  4. Create a server side Action in a Controller that will return the partial view.

Here’s a quick example how this can be done. We’ll use the same partial view created earlier (i.e. MyPartialView.cshtml). So now we have to prepare a place holder div, and use jQuery to load and inject the response:

1: <a href='javascript:getView();'>Get Partial View</a>
2: <script type="text/javascript">
3:     function getView() {
4:         $('#divResult').load("@Url.Action("GetView" , "Home" )");
5:     }
6: </script>
7: <div id='divResult'>
8: </div>
  • Line 1 is a simple anchor which will invoke the JavaScript containing jQuery’s load.
  • Line 4 is the simple one-line code which performs an ajax call to the server’s GetView action in the Home Controller (will be done shortly), and injects the result.
  • Line 7-8 is where the result will be injected to.

And now for the server side – pretty self-explanatory:

1: public ActionResult GetView()
2: {
3:    return PartialView("MyPartialView");
4: }

When we run the sample, prior to the Ajax call this looks like this:

After clicking the link, jQuery’s load method performs the Ajax call as expected, and the partial view’s HTML is injected into the place holder. All this can be viewed in the picture below:

  1. The load method performs a GET operation to the server’s GetView.
  2. Response is returned with html.
  3. jQuery injects the result in the place holder div.
  4. The result is rendered in the browser.

That’s it! No doubt this is real simple to achieve. From here on, the possibilities are quite remarkable. You see, it turns out that when jQuery injects the code, it is also able to inject JavaScript. This means that you can actually render not only html, but JavaScript code as well. Naturally you can argue if this is a good thing or not, but it just gives you a hint on how extensible this can be.

As usual, credits are in order. This great post summarizes different jQuery Ajax approaches with ASP.NET MVC.

BTW: If you ask yourself why you should use jQuery instead of Microsoft Client libraries, I guess you should relate to Microsoft’s statements about “throwing its weight behind jQuery”. You can read about this in Stephen Walther’s blog. Stephen’s conclusion says it all: “Our plan is to focus on jQuery as the primary technology for building client-side Ajax applications moving forward. We want to adapt Microsoft technologies to work great with jQuery and we want to contribute features to jQuery that will make the web better for everyone. We are very excited to be working with the jQuery core team.” Although MVC 3 comes with Microsoft Ajax client scripts, it also includes jQuery and I guess that we’ll see more and more of jQuery in Microsoft’s VS templates.

 
22 Comments

Posted by on 12/04/2011 in Software Development

 

Tags: , , , ,

ASP.NET MVC Ajax using jQuery – Quick Start sample

Ajax is heavily used over the past few years to provide a great UI to the web site. In ASP.NET Web Forms there are several alternatives and my preferred choice is ASP.NET Ajax from Microsoft. In this post I’m excluding Partial Rendering (a.k.a. UpdatePanels), and I’m focusing on implementing a kind of PageMethods  / WebMethods solution in MVC. Partial Rendering is good for some cases (although many would disagree one way or another), but this is a “different discussion”.

Back to WebMethods: What I really like about them, is that the infrastructure lets you enjoy Ajax easily. In short, JavaScript proxy classes are generated for the WebMethods/PageMethods, and they easily used with JavaScript callback functions. JSON objects are handled completely automatically from and to the server.

In ASP.NET MVC things are different. No ScriptManager is there to provide the client proxies and I was expecting lots of work. However, to my surprise, Ajax in MVC was quite easy to perform using jQuery. Credits are in order: you can read a summary of the different methods available for Ajax here. The difference between this post and the referenced post, is that this post is designated to provide a quick start to performing PageMethod-like implementation of Ajax in MVC, and will not cover each and every possibility and case in which a more through reading is required.

Assuming you have an updated jQuery which supports Ajax, basically there are just 2 things to take care of in order to accomplish a successful Ajax implementation:

  1. jQuery client code which performs an Ajax request.
  2. Server-side method which will handle that request.

Starting at the Server-side, here’s a sample code (no [WebMethod] attribute is required):

1: public ActionResult GetGreeting(string name)
2: {
3:     return Content("Hello " + name);
4: }

Note that in Line 3, Content returns plain text response to the client. There are other alternatives such as Json(…), which will return, well, Json.

On the client:

1: <script type="text/javascript" src="../../Scripts/jquery-1.4.4.min.js"></script>
2: <script type="text/javascript">
3: function go() {
4:     $.get("@Url.Action("GetGreeting")" ,
5:         {
6:             name: "Joe"
7:         }, function (data){
8:             alert(data);
9:         }
10:     );
11: }
12: </script>
  • Lines 4-10 represents a simple GET Ajax call from the client, and a callback function upon success.
  • Line 4 specifies the ‘$.get’ for a GET operation (can be substituted for ‘post’ if required).
  • Also in line 4 you can see the server side code which renders an appropriate url for reaching the GetGreeting method. The Action method allows to specify a different controller than the View’s default (as assumed in this sample), and it is also possible to specify Route values.
  • Lines 5-7 represents the data sent to the server. The data is a map of keys and values. The keys have to match the server side argument names.
  • Lines 7-9 represent a callback function in the case of success (failure will be discussed below).

Basically, you can create a new MVC project, copy-paste the code sections to a Controller and a View, set the routing in the global.asax, and it’s supposed to work:

jQuery has different Ajax alternatives. These can be viewed in the documentation. Although this sample accomplishes the minimum required for a successful Ajax operation, one last thing that you might want to consider adding is error handling. There are several methods that error handling can be accomplished (especially if you decide to use $.ajax). One way to handle error handling is as follows:

1: $(document).ready(function (){ 
2:     $("#log" ).ajaxError(function (e, xhr, settings, exception) {
3:         $(this ).text(xhr.statusText);
4:     });
5: });
  • Line 1 isn’t really mandatory if you’re certain that #log has been loaded by the time ajaxError is called. You can also bind the ajaxError to $(document).
  • Line 3 simply injects the status of the error as text within #log (naturally, there are other properties that you can query and display).

Assuming that you want to send arrays to the server, this is a little ‘trick’ in jQuery 1.4 and above, as the default serialization of jQuery has changed. So, assuming that you add an int[] age array to your server side method, your client code should look something like this:

1: function go() {
2:     $.get("@Url.Action("GetGreeting")" ,
3:         $.param({
4:                     name: "Joe" , 
5:                     age: [50,30]
6:         }, true ),
7:         function (data){
8:             alert(data);
9:         }
10:     );
11: }

Line 3 uses jQuery’s param, which is what performs the serialization. The ‘true’ in line 6 specifies that you would like to use the traditional method (which serializes it so that MVC gets it correctly).

You can read more about sending arrays ‘traditionally’ here.

 
8 Comments

Posted by on 15/03/2011 in Software Development

 

Tags: , , , ,