Handling Dynamic Images in ASP.NET 3.5 AJAX Applications

In the examples in the first part of this series, we discussed ways to make static image data persist in the server side files or databases. Nevertheless, in many other web applications, such as those on finance and economics sites, the images or charts are usually generated on the server side dynamically, and then sent back to the browser side in binary data via the HttpResponse output stream. This article, the conclusion to this two-part series, will show you how.

Contributed by
Rating: 4 stars4 stars4 stars4 stars4 stars / 9
September 03, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

GDI+ comes to the rescue

How can you create and manipulate these kinds of dynamic server side images? Traditionally, most solutions fall back on third party libraries or other applications-provided image engines (such as Microsoft Office). Nowadays, things have changed drastically; GDI+, the next generation of image engine under .NET environments, offers a solution. ASP.NET applications can use GDI+ as the back-end image engine to generate any required static or dynamic images or charts on the fly.

As you may have realized from reading my previous articles, GDI+ brings forward a fully object-oriented programming model instead of the device context and object handle to greatly simplify the image handling in .NET environments. The most important concept introduced in GDI+ is Graphics, which plays the role of a central controller when rendering. The other important concepts are Image class, Bitmap class, and many other new object models.

Moreover, it is worth noticing that, to provide images dynamically, you must utilize a custom HTTP handler. The modes for the handler to obtain the image bits should rest on their inner implementation. There are generally three ways to do this: store the image bits in a database, ASP.NET Cache, or generate the image data dynamically.

Constructing an ASP.NET AJAX Styled Message Board Sample

In this section, we are going to show you another interesting ASP.NET AJAX based message board sample. This sample is more complex that the previous ones: it uses an .xml file to hold message data, and the myNorthWind.mdf database to store verification code related data. And also, it uses an ASP.NET AJAX Toolkit control, NoBot, to automatically judge whether the message data entered is the result of an action by a human being or done by a robot instead of traditional manual programming.

First, you can look at one of the running time snapshots of the message board application, as is shown in Figure 4.

Figure 4-using the ASP.NET AJAX Toolkit control-NoBot to help to block off possible rubbish message


As the ResponseMinimumDelaySeconds property of the NoBot control hints, if it takes less than 15 seconds to enter all the message related data, the NoBot control will give a 'Suspicious Robot action...' prompt at the bottom and record this piece of information.

Now, let us dissect the how-to behind the scene.

Introducing the ASP.NET AJAX Toolkit control NoBot

NoBot is one of the great ASP.NET AJAX Toolkit controls that attempts to provide CAPTCHA-like bot/spam prevention without requiring any user interaction. This approach is easier to bypass than an implementation that requires actual human intervention, but NoBot has the benefit of being completely invisible. NoBot is probably most relevant for low-traffic sites where blog/comment spam is a problem and 100% effectiveness is not required.

In all, NoBot employs the following different anti-bot techniques:

  • Forcing the client's browser to perform a configurable JavaScript calculation and verifying the result as part of the postback. (Ex: the calculation may be a simple numeric one, or may also involve the DOM for added assurance that a browser is involved).

  • Enforcing a configurable delay between when a form is requested and when it can be posted back. (Ex: a human is unlikely to complete a form in less than two seconds).

  • Enforcing a configurable limit to the number of acceptable requests per IP address per unit of time. (Ex: a human is unlikely to submit the same form more than five times in one minute).

Note you can test NoBot by violating any of the above techniques: posting back quickly, posting back many times, or disabling JavaScript in the browser.

About the Data Storage

In this sample, the GuestBook.xml file under the App_Data folder is used to store the message information submitted by users. Moreover, the other two classes, GuestBook and Comment, are auxiliary classes to maintain this kind of data. The following defines the schema of this .xml file and already entered data:

<?xml version="1.0"?>

<GuestBook xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://demo.126.com/">

<comment name="jime" text="hi,good work." />

<comment name="Mike" text="Thank you. This helps me a lot!" />

<comment name="John" text="I like your ASP.NET AJAX article. Would you please provide the related source code?" />

</GuestBook>

Next, the SQL Server 2005 database myNorthWind.mdf with only a table namedcodetableis defined to persist the verifying codes to help generate the verifying picture. In this sample, we put a string, 'GenerateCAPTCHATest' to the field Code in the codetable table to be used to generate the verification code (five characters are selected at random here).

Next, let us delve into the most interesting point in this sample.

Using GDI+ to Create the Verifying Picture in the Custom HTTP Handler

In this sample, we mainly employ two techniques to hold up the possible robot action. One is to use the above-introduced ASP.NET AJAX Toolkit control, NoBot, which is designed to achieve the result from the angle of time. In other words, only the data entered within specified time is allowed. The other solution is to take the traditional action, which is to draw some characters commingled with some pictures so that only users entering the specified correct characters can continue to take the next step.

For convenience, let's again look at the pictures which  contain the letters users are required to enter, as shown Figure 5 below.


Figure 5-only users that are able to enter the letters within the picture below is allowed to leave work


In this sample, to accomplish the above second blocking solution we have recourse to a custom HTTP handler, GenerateCAPTCHA.ashx. As you have guessed, all the secrets behind the scene are finished within the important function named ProcessRequest, defined in this HTTP handler. The following lists the complete source code of this function.

// render the verification code

public void ProcessRequest (HttpContext context)

{

// Create the Bitmap

using (Bitmap objBitmap = new Bitmap(_width, _height, PixelFormat.Format32bppArgb))

{

// create the canvas

using (Graphics objGraphics = Graphics.FromImage(objBitmap))

{

//specify the smoothing mode

objGraphics.SmoothingMode = SmoothingMode.AntiAlias;


//Define a rectangle to render the verification code

Rectangle rect = new Rectangle(0, 0, _width, _height);

 

//define a brush with specified hatching style

HatchBrush hBr = new HatchBrush(HatchStyle.WideDownwardDiagonal, Color.Yellow, Color.White);

// create a rectangle for rendering picture

objGraphics.FillRectangle(hBr, rect);

hBr.Dispose();

 

// define the character offset in the string

int charOffset = 0;

//compute the width for each character

double charWidth = Convert.ToDouble(_width) / Convert.ToDouble(_randomTextLength);

//look on per character as a rectangle so as to twist it easily

Rectangle rectChar;

// define the fond style

Font fnt = null;

 

//first use a black color brush to render the letter within the rectangle,

//then twist the rectangle, and at last use the rectangle to draw

using (Brush br = new SolidBrush(Color.Black))

{

foreach (Char ch in _randomText)

{

fnt = GetFontStyle();

rectChar = new Rectangle(Convert.ToInt32(charOffset * charWidth), 0, Convert.ToInt32(charWidth), _height);


GraphicsPath gp = TextPath(ch.ToString(), fnt, rectChar);

TwistText(gp, rectChar);

objGraphics.FillPath(br, gp);

charOffset += 1;

}

}


// add some background noise points as well as the line noise

AddNoise(objGraphics, rect);

AddLine(objGraphics, rect);


// specify the ContentType of the Response object

//, and require when the drawing is finished return the result to the browser side

context.Response.ContentType = "Image/Jpeg";

context.Response.Clear();

// set the Cache-Control: no-cache

context.Response.Cache.SetCacheability(HttpCacheability.NoCache);

context.Response.BufferOutput = true;

context.Response.Flush();

objBitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);

}

 

HttpApplication app = context.ApplicationInstance;


//you can use the following two sentence to create a unique GUID to access the newly-generated verifying code

//here we simply hard code it

//Guid guid = System.Guid.NewGuid();

// string strGuid = guid.ToString();

string strGuid = "CAPTCHA";


if (strGuid != String.Empty)

{

HttpRuntime.Cache.Insert(strGuid, _randomText);

}

}

}

I've put enough comments in the code that additional explanation should not be necessary. As for the other helper functions used in it, you can refer to the source code, which can be downloaded at the end of this article.

Creating the Sample .aspx Page

As for adding this sample required .aspx page, it is very easy. With Visual Studio 2008's built-in support for ASP.NET AJAX Extensions, you can directly add an AJAX web form, as is shown in Figure 6 below.


Figure 6-Adding an AJAX web form in Visual Studio 2008


As many web developers prefer to do, we have also leveraged <table> elements to lay out the page. Now, let's look at the HTML elements related definitions below:

<form id="form1" runat="server">

<div style="width: 761px">

<asp:ScriptManager ID="ScriptManager1" runat="server">

</asp:ScriptManager>

 

<!-- create ObjectDataSource -->

<asp:ObjectDataSource ID="odsComments" runat="server"

DataObjectTypeName="DemoSpace.Comment"

InsertMethod="AddComment"

SelectMethod="GetComments"

TypeName="DemoSpace.GuestBookDB">

</asp:ObjectDataSource>

 

<table>

<tr>

<td valign="top">

<asp:UpdatePanel ID="upAddComment" runat="server">

<ContentTemplate>

<asp:FormView ID="fvAddComment" runat="server"

DataSourceID="odsComments"

DefaultMode="Insert" OnItemCommand="fvAddComment_ItemCommand">

<InsertItemTemplate>

<table style="width: 550px">

<tr>

<td class="commentTitle">

<img alt="" src="imgs/Bullet01.gif" />Name:</td>

<td>

<asp:TextBox ID="txtName" runat="server"

CssClass="commentNormal"

Text='<%# Bind("Name") %>' Width="150px">

</asp:TextBox>

<br />

<asp:RequiredFieldValidator ID="rfvName" runat="server"

ErrorMessage="Please enter your name" ControlToValidate="txtName">

</asp:RequiredFieldValidator>

</td>

</tr>

<tr>

<td class="commentTitle">

<img src="imgs/Bullet01.gif" />Words:</td>

<td>

<asp:TextBox ID="txtComment" runat="server"

Height="100px" TextMode="MultiLine" Width="300px"

CssClass="commentNormal" Text='<%# Bind("Text") %>'>

</asp:TextBox>

<br />

<asp:RequiredFieldValidator ID="rfvComment" runat="server"

ErrorMessage="Please leave your word"

ControlToValidate="txtComment">

</asp:RequiredFieldValidator>

</td>

</tr>

<tr>

<td class="commentTitle">

<img src="imgs/Bullet01.gif" />Verifying code:</td>

<td>

<asp:TextBox ID="txtCAPTCHA" runat="server"

CssClass="commentNormal" Width="150px"

MaxLength="5"></asp:TextBox>

<br />

<asp:RequiredFieldValidator ID="rfvCAPTCHA" runat="server"

ErrorMessage="Please enter the verifying code"

ControlToValidate="txtCAPTCHA">

</asp:RequiredFieldValidator>

</td>

</tr>

<tr>

<td align="center" colspan="2">

(Please enter the characters shown within the picture below.)<br />

<img src="GenerateCAPTCHA.ashx" title="Verifying code" /><br />

<img src="imgs/Hr_pen.gif" /></td>

</tr>

<tr>

<td colspan="2" align="center">

<table align="center" cellpadding="10" cellspacing="10" width="100%">

<tr>

<td>

<asp:Button ID="btnSend" runat="server"

BackColor="#C0FFFF" CommandName="Submit"

Font-Bold="True" Font-Size="Medium" Text="Submit" /></td>

<td>

<asp:Button ID="btnReset" runat="server"

CausesValidation="False" CommandName="Cancel"

Font-Bold="True" Font-Size="Medium" ForeColor="Blue"

Text="Clear" /></td>

</tr>

</table>

</td>

</tr>

</table>

</InsertItemTemplate>

<HeaderTemplate>

Leave Your Word

</HeaderTemplate>

<HeaderStyle CssClass="header" />

</asp:FormView>

 

<ajaxToolkit:NoBot ID="myNoBot" runat="server"

ResponseMinimumDelaySeconds="15"

CutoffMaximumInstances="3"

CutoffWindowSeconds="150"

OnGenerateChallengeAndResponse="MyChallengeResponse" />

 

 

<asp:Label ID="lblMessage" runat="server" ForeColor="Red"></asp:Label>

</ContentTemplate>

</asp:UpdatePanel>

</td>

<td valign="top">

<asp:UpdatePanel ID="upShowComments" runat="server"

UpdateMode="Conditional">

<ContentTemplate>

<asp:DataList ID="dlComments" DataSourceID="odsComments" runat="server">

<ItemTemplate>

<table align="left">

<tr>

<td class="dlTitle">

Name:</td>

<td class="dlContent">

<asp:Label ID="lblName" runat="server" Text='<%# Eval("Name") %>'></asp:Label></td>

</tr>

<tr>

<td class="dlTitle">

Word:</td>

<td class="dlContent">

<asp:Label ID="lblText" runat="server" Text='<%# Eval("Text") %>'></asp:Label></td>

</tr>

</table>

</ItemTemplate>

<HeaderTemplate>

Already Left Words

</HeaderTemplate>

<SeparatorTemplate>

<img src="imgs/Hr_book_pen.gif" />

</SeparatorTemplate>

<HeaderStyle CssClass="header" />

<AlternatingItemStyle BackColor="#F7F7F7" />

<ItemStyle BackColor="#E7E7FF" />

</asp:DataList>

</ContentTemplate>

<Triggers>

<asp:AsyncPostBackTrigger ControlID="fvAddComment" EventName="ItemInserted" />

<asp:AsyncPostBackTrigger ControlID="fvAddComment" EventName="ItemCommand" />

</Triggers>

</asp:UpdatePanel>

</td>

</tr>

</table>

</div>

</form>

First, you may notice that the ASP.NET AJAX server side controls ScriptManager and UpdatePanel are employed, which nearly all ASP.NET AJAX server-centric applications must put into use. Therefore, we do not dwell upon them.

Second, you should see that the important ASP.NET data source control ObjectDataSource appears herein. It is used to provide data for the FormView 'fvAddComment' and the DataList control 'dlComments.' This is typical ASP.NET 2.0 programming.

In the middle of the code hides the AJAX Toolkit control NoBot (with its ID being 'myNoBot'). There are two properties, ResponseMinimumDelaySeconds and OnGenerateChallengeAndResponse, which need to be discussed. The  ResponseMinimumDelaySeconds property is set to 15 seconds, which means that within this period of time, if the "Submit" button is pressed, then the current action is suspected to be from a robot, as a result of which the submitting content is rejected and a warning message is thrown at the left bottom of the page.

Property Related Value


Next, let's look at the OnGenerateChallengeAndResponse property related value- a JavaScript function, as follows:

protected void MyChallengeResponse(object sender, NoBotEventArgs e)

{

Panel p = new Panel();

Random rand = new Random();


// set the related properties for the panel

p.ID = "panCheckNoBot";

p.Width = rand.Next(123);

p.Height = rand.Next(456);

// hide Panel

p.Style.Add(HtmlTextWriterStyle.Visibility, "hidden");

p.Style.Add(HtmlTextWriterStyle.Position, "absolute");

// add control Panel into the NoBot control

((NoBot)sender).Controls.Add(p);

// specify the challenge and response

e.ChallengeScript = String.Format(

"var e = document.getElementById('{0}'); e.offsetWidth + e.offsetHeight;",

p.ClientID);

e.RequiredResponse = (p.Width.Value + p.Height.Value).ToString();

}

In this function, a <div> element with random width and height is generated and then added onto the DOM tree in the page. At the same time, the sum of the width and height is stored, and a JavaScript code snippet is written in the page. Note that this JavaScript code snippet will be invoked at the client-side running time to find the above <div> and obtain its factual sum of its width and height. In this way, when the page postback takes place, the NoBot control can compare the expected value with that fetched from the browser to judge whether the client side is a browser and further judge whether or not the action came from a robot.

Summary

In this two-part series, we have extensively examined the image-related issues under the ASP.NET 3.5 environment by building several simple yet typical sample applications for the corresponding image solutions. One important point you should take notice of is that nearly all the solutions are in relation to the HTTP handlers. Therefore, it is strongly suggested that you become familiar with them first before diving into any of the image-related solutions provided here.

In the last section, we discussed how to use a custom HTTP handler in ASP.NET 3.5 environments to generate images and charts dynamically. Yet, it is noticeable that the HTTP handler is not the recommended tool for creating advanced diagrams. In fact, the HTTP handler is especially suitable to display images at the running time. To generate dynamic diagrams, the preferable solution is to seek help from the server side controls that are developed based upon the HTML <img> element.

Feel free to have a look at the source code for these two articles:

-DOWNLOAD SOURCE PART 2-

blog comments powered by Disqus
ASP.NET ARTICLES

- Implementing ASP.NET 4.0 Page.MetaDescriptio...
- ASP.Net Development Tips
- Intro to Sessions in ASP.Net
- Google Maps API Introduction in ASP.NET usin...
- Creating an ASP.NET 3.5 Gridview Image Galle...
- Encrypt QueryString in ASP.NET 3.5 using VB....
- ASP.NET 3.5 Drop Down List Controls
- Connect to Access Database with ASP.Net
- Secure Audio Streaming with ASP.Net and Flash
- Dynamic Sitemap and Navigation in ASP.Net
- Implement Gzip and Deflate Compression in AS...
- Run ASP.Net in Ubuntu with Apache
- ASP.Net Mono Website Contact Forms
- ASP.Net URL Rewriting Methods
- Murach`s ASP.NET 4 Web Programming with C# 2...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 8 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials