RSS

Tag Archives: PageMethods

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

CSRF and PageMethods / WebMethods

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

In short, Cross-Site Request Forgery (CSRF) attack is one that uses a malicious website to send requests to a targeted website that the user is logged into. For example, the user is logged-in to a bank in one browser tab and uses a second tab to view a different (malicious) website, sent via email or social network. The malicious website invokes actions on the target website, using the fact that the user is logged into it in the first tab. Example for such attacks can be found on the internet (see Wikipedia).

CSRF prevention is quite demanding. If you follow the Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet you’ll noticed that the general recommendation is to use a “Synchronizer Token Pattern”. There are other prevention techniques listed but also specified are their disadvantages. The Synchronizer Token Pattern requires that request calls will have an anti-forgery token that will be tested on the server side. A lack of such token or an invalid one will result in a failure of the request.

ASP.NET WebForms are supposed to prevent attacks on two levels: “full postback” levels (which you can read here how to accomplish) and on Ajax calls. According to a post made by Scott Gu several years ago, ASP.NET Ajax web methods are CSRF-safe because they handle only POST by default (which is known today as an insufficient CSRF prevention technique) and because they require a content type header of application/json, which are not added by the browser when using html element tags for such an attack. It is more than possible that I am missing something but in my tests I found this claim to be incorrect. I found no problem invoking a web method from a different website using GET from a script tag. Unfortunately ASP.NET didn’t detect nor raise any problem doing so (as will be shown below).

Therefore I was looking into adding a Synchronizer Token Pattern onto the requests. In order not to add token to every server side methods’ arguments, one technique is to add the CSRF token to your request’s headers. There are several advantages to using this technique: you don’t need to specify a token argument in your server and client method calls, and more importantly: you do not need to modify your existing website web methods. If you’re using MVC and jQuery Ajax you can achieve this quite easily as can be shown here or you can follow this guide. However if you are using PageMethods/WebMethods, the Synchronizer Token Pattern can prove more difficult as you’ll need to intercept the http request to add that header.

Test websites
I set up a couple of websites for this solution. One website simulates the bank and the other simulates an attacker.
The bank website has PageMethods and WebMethods. The reasons I am setting up a PageMethod and a WebMethod are to demonstrate both and because the CSRF token is stored in session and for WebMethods session is not available by default (as opposed to PageMethods).

public partial class _Default : System.Web.UI.Page
{
    [WebMethod]
    public static bool TransferMoney(int fromAccount, int toAccount, int amount)
    {
        // logic

        return true;
    }
}
[System.Web.Script.Services.ScriptService]
public class MyWebService  : System.Web.Services.WebService {

    [WebMethod]
    public bool TransferMoney(int fromAccount, int toAccount, int amount)
    {
        // logic

        return true;
    }
}

A bank sample code for invoking both methods:

<asp:ScriptManager runat="server" EnablePageMethods="true">
    <Services>
        <asp:ServiceReference Path="~/MyWebService.asmx" />
    </Services>
</asp:ScriptManager>
<input type="button" value='WebMethod' onclick='useWebMethod()' />
<input type="button" value='PageMethod' onclick='usePageMethod()' />
<script type="text/javascript">
    function usePageMethod() {
        PageMethods.TransferMoney(123, 456, 789, function (result) {

        });
    }

    function useWebMethod() {
        MyWebService.TransferMoney(123, 456, 789, function (result) {

        });
    }
</script>

This is how it looks like:
1

The web.config allows invoking via GET (note: you don’t have to allow GET; I’m deliberately allowing a GET to demonstrate an easy CSRF attack and how this solution attempts to block such calls):

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
    <webServices>
      <protocols>
        <add name="HttpGet" />
        <add name="HttpPost" />
      </protocols>
    </webServices>
  </system.web>
</configuration>

The attacking website is quite simple and demonstrates a GET attack via the script tag:

<script src='http://localhost:55555/MyWebsite/MyWebService.asmx/TransferMoney?fromAccount=111&toAccount=222&amount=333'></script>
<script src='http://localhost:55555/MyWebsite/Default.aspx/TransferMoney?fromAccount=111&toAccount=222&amount=333'></script>

As you can see, running the attacking website easily calls the bank’s WebMethod:
2

Prevention
The prevention technique is as follows:

  1. When the page is rendered, generate a unique token which will be inserted into the session.
  2. On the client side, add the token to the request headers.
  3. On the server side, validate the token.

Step 1: Generate a CSRF validation token and store it in the session.

public static class Utils
{
    public static string GenerateToken()
    {
        var token = Guid.NewGuid().ToString();
        HttpContext.Current.Session["RequestVerificationToken"] = token;
        return token;
    }
}
  • Line 5: I used a Guid, but any unique token generation function can be used here.
  • Line 6: Insert token into the session. Note: you might consider ensuring having a Session.

Note: you might have to “smarten up” this method to handle error pages and internal redirects as you might want to skip token generation in certain circumstances. You may also check if a token already exists prior to generating one.

Step 2: Add the token to client requests.

<script type="text/javascript">
    // CSRF
    Sys.Net.WebRequestManager.add_invokingRequest(function (sender, networkRequestEventArgs) {
        var request = networkRequestEventArgs.get_webRequest();
        var headers = request.get_headers();
        headers['RequestVerificationToken'] = '<%= Utils.GenerateToken() %>';
    }); 

    function usePageMethod() {
        PageMethods.TransferMoney(123, 456, 789, function (result) {

        });
    }

    function useWebMethod() {
        MyWebService.TransferMoney(123, 456, 789, function (result) {

        });
    }
</script>
  • Line 3: Luckily ASP.NET provides a client side event to intercept the outgoing request.
  • Line 6: Add the token to the request headers. The server side call to Utils.GenerateToken() executes on the server side as shown above, and the token is rendered onto the client to be used here.

Step 3: Validate the token on the server side.

To analyze and validate the request on the server side, we can use the Global.asax file and the Application_AcquireRequestState event (which is supposed to have the Session object available by now). You may choose a different location to validate the request.

protected void Application_AcquireRequestState(object sender, EventArgs e)
{
    var context = HttpContext.Current;
    HttpRequest request = context.Request;

    // ensure path exists
    if (string.IsNullOrWhiteSpace(request.PathInfo))
        return;

    if (context.Session == null)
        return;
        
    // get session token
    var sessionToken = context.Session["RequestVerificationToken"] as string;

    // get header token
    var token = request.Headers["RequestVerificationToken"];

    // validate
    if (sessionToken == null || sessionToken != token)
    {
        context.Response.Clear();
        context.Response.StatusCode = 403;
        context.Response.End();
    }
}
  • Line 7: Ensure we’re operating on WebMethods/PageMethods. For urls such as: http://localhost:55555/MyWebsite/Default.aspx/TransferMoney, TransferMoney is the PathInfo.
  • Line 10: We must have the session available to retrieve the token from. You may want to add an exception here too if the session is missing.
  • Line 14: Retrieve the session token.
  • Line 17: Retrieve the client request’s token.
  • Line 20: Token validation.
  • Line 22-24: Decide what to do if the token is invalid. One option would be to return a 403 Forbidden (you can also customize the text or provide a subcode).

When running our bank website now invoking the PageMethods you can see the token (the asmx WebMethod at this point doesn’t have a Session so it can’t be properly validated). Note that the request ended successfully.
3

When running the attacking website, note that the PageMethod was blocked, but not the asmx WebMethod.

4

  • The first TransferMoney is the unblocked asmx WebMethod, as it lacks Session support vital to retrieve the cookie.
  • The second TransferMoney was blocked as desired.

Finally we need to add Session support to our asmx WebMethods. Instead of going thru all the website’s asmx WebMethods modifying them to require the Session, we can add the session from a single location in our Global.asax file:

private static readonly HashSet<string> allowedPathInfo = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { "/js", "/jsdebug" };
protected void Application_BeginRequest(Object sender, EventArgs e)
{
    if (".asmx".Equals(Context.Request.CurrentExecutionFilePathExtension) && !allowedPathInfo.Contains(Context.Request.PathInfo))
        Context.SetSessionStateBehavior(SessionStateBehavior.Required);
}
  • Line 1: Paths that we would like to exclude can be listed here. asmx js and jsdebug paths render the client side proxies and do not need to be validated.
  • Line 4-5: If asmx and not js/jsdebug, add the Session requirement so it becomes available for the token validation later on.

Now running the attacking website we can see that our asmx was also blocked:
5

Addendum

  • Naturally you can add additional hardening as you see fit.
  • Also important is to note that using the Session and the suggested technique has an issue when working in the same website with several tabs, as each time the Utils.GenerateToken is called it replaces the token in the session. So might consider also checking the referrer header to see whether to issue a warning instead of throwing an exception, or simply generating the token on the server side only once (i.e. checking if the token exists and only if not then generate it.)
  • Consider adding a “turn off” switch to CSRF in case you run into situations that you need cancel this validation.
  • Moreover: consider creating an attribute over methods or web services that will allow skipping the validation. You can never know when these might come in handy.
 
5 Comments

Posted by on 07/09/2014 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.
 
7 Comments

Posted by on 01/06/2014 in Software Development

 

Tags: , , , , , ,

First attempt at AngularJS and ASP.NET’s PageMethods & WebMethods

If you need the example, right click this link, Save As and rename to zip.

I made a first attempt at AngularJS and was particularly interested in how I can use it alongside ASP.NET Ajax. The reason is because I am very fond of PageMethods and WebMethods for Ajax, as they are extremely simple and trivial to use. I know that just by writing this short introduction some AngularJS religious fans are shaking their heads in disagreement. That’s OK.

AngularJS seems nowadays like one of the hottest frameworks so I thought I to try it out. I did some reading (the AngularJS homepage shows really helpful examples), saw some videos and experimented a bit. My purpose: see if AngularJS can be good as a templating solution. Comparing to other solutions, what I really liked about AngularJS was that it is embedded within the html itself. Note: AngularJS is much more than “just templates” (the html is not just referred to as a collection of DOM elements but as The “View” in MV* architectures) – but that’s off topic. So in comparison for example to jsrender, which I’ve been using lately, AngularJS just integrates into the html, allowing you to manipulate it using JavaScript controllers. This is very convenient because your favorite editor will colorize everything as usual. That’s just a short description of one of the advantages. To be fair I lack the experience to describe the gazillions of advantages of AngularJS. But again, this is off topic.

What I am interested in is something that I will be comfortable working with, that will also be reliable and long lasting. Without going too much into details, there are many libraries, 3rd party controls and frameworks that failed to stand up to some or all of these requirements. Sometimes when you figure out that a library is not living up to standards, it might be after lots of your work was already written using it and it’ll be very difficult or even impossible to back out. Then you have to start working around problems and this could be a pain. Which is why I would rather not become too dependent on things that might later prove to be a constraint.

Having said that, this might explain why I would rather try to integrate AngularJS with ASP.NET Ajax. The latter is something so convenient to me that I’d hate to give it up. Similarly, although jQuery is one major library that I’m working with daily and by far it is a huge success, I’m still using the good ol’ ASP.NET Ajax over jQuery’s Ajax wrappers. I manage to integrate them together with ease so that’s a comfy.

The problem was that after I experimented a little with AngularJS “hello world” samples, I tried to do the most trivial thing: make an Ajax call outside of AngularJS framework and then apply the results using AngularJS. In short, I wasn’t trying to code “in AngularJS” but to use it for templating only. Using jsrender, what I would do is perform a simple ajax call and in the callback use the results and templates, followed by injecting the html onto the page. Very simple. In AngularJS, initially, I couldn’t find a way to do that. That is, I could not find a way to change the $scope property from outside the controller. Perhaps there are ways to do that, but I failed to make a simple ajax call and set the results as a datasource.

After some time it occurred to me that I was working wrong with AngularJS. I figured that if I can’t call the AngularJS controller from outside, I need to change the datasource from the inside. Therefore I made the PageMethods ajax call from within the controller and then it worked as expected.

So the result is as follows. This is my ASP.NET Default.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html>
<html>
<head runat="server">
    <title></title>
</head>
<body ng-app>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" EnablePageMethods="true">
        <Scripts>
            <asp:ScriptReference Path="http://code.angularjs.org/1.0.8/angular.js" />
            <asp:ScriptReference Path="~/JScript.js" />
        </Scripts>
    </asp:ScriptManager>
    <div ng-controller="TestCtrl" ng-init="init()">
        {{source}}
    </div>
    </form>
</body>
</html>
  • Line 1: Well, this is a Default.aspx page because we’re using ASP.NET here.
  • Line 8: ng-app tag to indicate an AngularJS app.
  • Line 10: Enable PageMethods.
  • Lines 12-13: AngularJS framework and my own js file containing the AngularJS controller.
  • Line 16: Use my TestCtrl controller with this div element and activate the init() function when the page (view) loads.
  • Line 17: This is the name of the property that it’s value will be displayed and changed following the ajax call. It’ll be initialized in the controller later on.

And the JavaScript AngularJS controller I wrote (JScript.js):

function TestCtrl($scope, $timeout) {
    $scope.source = 10;
    $scope.init = function () {

        PageMethods.Inc($scope.source, function (result) {
            $scope.$apply(function () {
                $scope.source = result;
                $timeout(function () {
                    $scope.init();
                }, 100);
            });
        });

    }
}
  • Line 2: The source property. Initialized to a value of 10.
  • Line 3: The init() method. This is called by AngularJS because in the html I placed an ng-init in the div.
  • Line 5: The ASP.NET Ajax call. Can be any ajax call here: jQuery, ASP.NET Ajax or whatever you prefer. Note that I’m passing the current value of ‘source’ as an argument to the server which will increment this value.
  • Line 6: In AngularJS you have to call $apply to invoke AngularJS framework for calls outside the framework. Here’s a quote from the docs:

    “$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life cycle of exception handling, executing watches.”

    Without $apply this code doesn’t seem to refresh the updated binding onto the html.

  • Line 7: In the callback, change the source property of the controller.
  • Lines 8-10: Using the AngularJS $timeout function to schedule consecutive recursive calls to the init() method. The ‘source’ is expected to increment endlessly.

That’s it. If you run this code you’ll see that it endlessly calls the server using ASP.NET Ajax and updates the AngularJS ‘source’ property as expected.

“Philosophy”
Although AngularJS looks very tempting and sexy, what’s important to me is to have ease of use and not to be too dependent. Like so many frameworks that seemed so appealing at one time, AngularJS too has the potential to be looked at as an ancient dinosaur one day. That’s OK. I work with a dinosaur on a daily basis (ASP.NET WebForms). I believe that every single developer with several years of experience in any environment, can provide examples to libraries and frameworks that once were considered the pinnacle of technology. Not many survive as new technologies and ideas are being thought of everyday. Therefore I think that you should choose something that you’ll feel comfortable to work with and not just because it is now very hot. Just browse this article, and you’ll find the 10 hottest client side frameworks of today. While their fans argue between themselves which is better (Backbone.JS? AngularJS? Ember.js?), I think that it’s quite clear that not all ten will remain in that list for years to come. Something else will become “hot”. What then? you can’t just replace your website’s framework every couple of months just because some other technology became “hotter” than what you have selected. Therefore, do not pick the hottest. Pick what’s working for you and your team. If it’s AngularJS, great. If it is Backbone JS, great. If you rather keep a more classic approach by manipulating the DOM using jQuery or the amazing Vanilla JS – that’s great too. Just don’t bind yourself to something because of the wrong reasons.

Summary
In fairness, I think AngularJS looks very interesting. The concepts seems to be very good. Separation of concerns, testing and advocation of single page applications as true applications. In fact, I’m looking forward to using AngularJS in a real project. However, my main concern over AngularJS is that it seems to me like it is a somewhat “too-closed” of a framework and that you must and abide by “the rules”. While it has advantages as it conforms you to do things “properly, their way”, it is also a disadvantage because if eventually you’ll run into a situation that AngularJS doesn’t do what you expect it to – you’ll have a problem. Then you might want to revert to some non-AngularJS such as jQuery but this is strongly discouraged. You’re welcome to read this very helpful post and replies. You’ll get an idea why you shouldn’t mix the two.

In case you’re asking yourself, I consider both jQuery and ASP.NET Ajax as “open” libraries. They are not frameworks. You can work with them in your website and you can work without. You can decide to adapt newer patterns & plugins and even other libraries. You can use plain JavaScript to manipulate the DOM any way you require with or without them. In short, they are not constraining.

 
Leave a comment

Posted by on 27/10/2013 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: , , , ,

A quick guide to have jQuery call your ASP.NET PageMethods

I based most of my solution and understandings on several posts from Dave Ward. Although Dave’s excellent posts cover everything written here and more, it still took me quite a while to get this going. That is why I decided to sum up my current experience in this single post, which may be considered as a quick guide to having jQuery call WebMethods (PageMethods in particular).

Assuming that we have the following PageMethod, how do we use jQuery to call it?

There are several stages I had to go in order to use jQuery alongside PageMethods:

  1. Use the correct url for the PageMethod.
  2. Ensure that the client and server use json content types.
  3. Pass arguments to the PageMethod as JSON strings (see the ‘JSON, objects, and strings: oh my!‘).
  4. Interpret the result using the ‘d’ key (see the ‘Waiter, there’s a .d in my msg soup!‘).

Here’s the client code I ended up with (explanation below):

Correct url? No surprises here. In order to invoke the correct PageMethod you have to supply a valid url for that method. It’s easy to use ResolveUrl to get this right.

json: As you can see, I used jQuery’s ajaxSetup method in order to assure that all ajax calls are sent using json. Naturally, you don’t have to do this if you do not want JSON, or if not your calls are json-based. But, no doubt that placing the ajaxSetup call in a js file that gets loaded from a master page is very comfortable to ensure that all your jQuery ajax calls are using the same basis.

JSON arguments: This is the part where it get tricky. As Dave Ward explains so well in his post, ASP.NET expects arguments in JSON format, whereas jQuery sends JavaScript objects (which are mistaken for JSON objects) in query string format (that is, if you try to send JavaScript objects). This means that you have to provide jQuery not with a JavaScript object, but rather with a proper JSON string. This will ensure that jQuery wont parametrize your data in key=value format and will provide ASP.NET the desired JSON format. This is important because it means that if you decide to pass your data object as an invalid JSON string, you’ll be frustrated why the thing isn’t working. That’s why when I learned of the JSON native support that now exists in the popular browsers (IE8, FF 3.5 etc.), it became an obvious solution: using JSON.stringify() ensures that the data gets sent in proper JSON format.

Whats with the ‘d’? As explained in Dave’s post, ASP.NET 3.5 and onwards returns a JSON response from the server with a key of ‘d’, for security purposes (preventing XSS attacks). While ASP.NET Ajax framework automatically handles the new ‘d’ formatted result, so that you’ll end up with the intended data without ever knowing that a ‘d’ was there in the first place, you have to handle this manually when using jQuery and WebMethods. jQuery’s combination of ajaxSetup and dataFilter event allows us to handle response from the server prior to actually calling your “onsuccess” callbacks. The proposed solution in the code above assumes that our site is working only with ASP.NET 3.5 WebMethods, and therefore a ‘d’ is always returned. I believe that usually that will be the case, although a more complex solution for checking if a ‘d’ is there already exists. Anyway, the proposed solution simply takes a JSON string returned from from the server, parses it and extracts the actual data from the ‘d’ key. It then transforms the actual data back to a string (which is what jQuery will expect to get when the dataFilter event has ended…).

Following the described steps should get you to use jQuery with ASP.NET’s WebMethods and PageMethods.

After this has consumed precious time to get going, the big question that remains is: was it worth it. You could argue that using jQuery is faster, or that it seems like MS’s client framework is destined to “retire” in favor of jQuery. But honestly, having to go through all this trouble for calling a PageMethod just seems way too much. In short, personally, I’d rather have the good old script proxies which are extremely easy to use over the not-so-trivial jQuery alternative.

Just a reminder though: in MVC using jQuery is quite trivial.

 
2 Comments

Posted by on 20/05/2011 in Software Development

 

Tags: , , , ,