|Delphi Clinic||C++Builder Gate||Training & Consultancy||Delphi Notes Weblog||Dr.Bob's Webshop|
Delphi Prism and Http Handlers
In this article, I'll demonstrate we can use Delphi Prism to create Http Handlers that return just about anything (images in my example, but this could also be XML or PDF files).
Apart from Web Services and Web Forms applications, there is another - less well known - area of ASP.NET which can be very powerful and useful to explore.I'm talking about HttpHandlers and HttpModules, and in this article I'll cover HTTP Handlers in detail, using the latest version of Delphi Prism, showing how we can use HttpHandlers to enhance our ASP.NET experience even further.
In order to understand HttpHandlers in more detail, we should take a look at the inner workings of ASP.NET.When a request comes in at the web server (usually IIS), the web server examines the extension of the request, and locates the process that should handle the request.For an ASP.NET request using the .aspx or .asmx extension, the request is passed to the ASP.NET ISAPI extension (aspnet_isapi.dll) that sends it to the ASP.NET worker process (aspnet_wp.exe), which in turn forwards the request to the corresponding ASP.NET application (loaded in a separate AppDomain).
The request for an ASP.NET web form (using the .aspx extension) is then handled by an ASP.NET Web Page class, typically derived from System.Web.UI.Page. For a Web Service request (using the .asmx extension), the request is handled by a Web Service, typically derived from System.Web.Services.WebService. The actual base classes can differ, however, what's important is that both the .aspx and .asmx requests are in the end processed by a HttpHandler. There is a default ASP.NET Page Handler for .aspx requests, as well as a default ASP.NET Service Handler for .asmx requests. Both handlers, as well as custom handlers, implement the IHttpHandler interface, which in Delphi syntax can be as follows:
type IHttpHandler = interface public method ProcessRequest(context: HttpContext); property IsReusable: boolean; end;
The IsReusable property is used to indicate whether or not it's OK to reuse the implementing class by placing it in a pool of HttpHandlers.
The ProcessRequest method is doing the actual processing of the incoming request.
For .aspx requests, the System.Web.UI.Page class implements the IHttpHandler interface, and its implementation of the ProcessRequest method handles the request of ASP.NET web pages, controlling the rendering of HTML output. Apart from the Page class, the HttpApplication and HttpRemotingHandler classes also implement the IHttpHandler interface.
For ASP.NET Web Services, the WebServiceHandler (produced by the WebServiceHandlerFactory from the System.Web.Services.Protocols namespace) includes the functionality for Web Services, including support for SOAP, XML, and WSDL. The WebServiceHandlerFactory class implements the IHttpHandlerFactory, and apart from the WebServiceHandlerFactory, there are other factory classes like the HttpRemotingHandlerFactory (returning a HttpRemotingHandler) and the generic HttpHandlerFactory class.
Let's now see what it takes to build a custom HttpHandler. Since the IHttpHandler interface is the core of all HttpHandlers, a custom HttpHandler must implement this interface, and implement the ProcessRequest methods. We can do this using Delphi Prism with an ASP.NET Web Site or ASP.NET Web Project, adding a Generic Handler. My personal preference is to use a project as starting point, so let's start by creating a new ASP.NET Web Project:
Specify HttpHander as name of the project, which will result in a solution HttpHandler with project HttpHandler in the Projects directory.
Next, right-click on the HttpHandler project node in the Solution Explorer, and select Add New item. From the Web category, select the Generic Handler icon, and change the Name to NumToWord.ashx, as follows:
This results in a NumToWord.ashx file, with a one line content:
<%@ WebHandler Language="Oxygene" CodeBehind="NumToWord.ashx.pas" Class="HttpHandler.NumToWord" %>
This line specifies that we're dealing with a WebHandler (a HttpHandler), which will be compiled (if we deploy the source code) with the Oxygene compiler, and implements the httpHandler.NumToWord class which can be found in the code behind file NumToWord.ashx.pas.
More interesting is the contents of the actual NumToWord.ashx.pas file, which contains the following code:
namespace HttpHandler; interface uses System, System.Collections.Generic, System.Linq, System.Web, System.Web.Services; type /// <summary> /// Summary description for $codebehindclassname$ /// </summary> [WebService(&Namespace := "http://tempuri.org/")] [WebServiceBinding(ConformsTo := WsiProfiles.BasicProfile1_1)] NumToWord = public class(IHttpHandler) private public method ProcessRequest (context : HttpContext); property IsReusable : boolean := false; end; implementation method NumToWord.ProcessRequest(context : HttpContext); begin context.Response.ContentType := 'text/plain'; context.Response.Write('Hello World'); end; end.
Note that you need at least the May 2009 update of Delphi Prism in order to get this (compiable) code - with previous versions of Delphi Prism the code would be incorrect (and could not compile, without some manual changes).
Also note the [WebService] and [WebServiceBinding] attributes, which were probbaly copied from an ASP.NET Web Service template, but which are unnecessary here for an HttpHandler (so you can remove them, just as I will for the continuation of the example source code).
type /// <summary> /// Summary description for $codebehindclassname$ /// </summary> NumToWord = public class(IHttpHandler) private public method ProcessRequest (context: HttpContext); property IsReusable: boolean := false; end;
The method NumToWord.ProcessRequest is the place where the HttpHandler is doing its work, and I will implement a more interesting example instead of just Hello World.
For starters, I want to receive input from the incoming request, and display that information as a JPG image.
Apart from using the context.Response, we should not forget that context also holds the Request part, which is needed to be able to respond to specific incoming requests, passing parameters on the URL for example, which can be found in the context.Request.QueryString name-value collection.
And we can also use the Trace object, as context.Trace, calling the Write and Warn methods. Note that this only requires the trace enabled="true" attribute to be set in the web.config - there's no additional trace="true" option required at the .ashx level.
So, with that in mind, the first part is easy: we can use the Context.Request to get to the Params collection - let's use field Str, so the client needs to pass ?Str=... on the calling URL. The second part involves a Bitmap image, that we can use to extract a graphic and call the DrawString method. The tricky part is to obtain the length of the text in order to use that to set the width of the image (almost a catch-22 situation, that I solve by first creating a 1,1 bitmap to produce the graphic from which to call MeasureString which is then used to create the actual bitmap for the string. In pure Delphi Prism code, this is as follows:
method NumToWord.ProcessRequest(context: HttpContext); var Image: Bitmap; Str: String; F: Font; begin if Assigned(Context.Request.Params['Str']) then Str := Context.Request.Params['Str'].ToString else Str := ''; F := new Font('Comic Sans MS', 14); Context.Response.ContentType := 'image/jpeg'; Image := Bitmap.Create( Convert.ToInt32(Graphics.FromImage(Bitmap.Create(1,1)).MeasureString(Str, F).Width), 36); Graphics.FromImage(Image).DrawString(Str, F, SolidBrush.Create(Color.PeachPuff), 2,2); Image.Save(Context.Response.OutputStream, ImageFormat.Jpeg); end;
Without any typing mistakes, the Http Handler will compile and can be tested (before deployment).
Delphi Prism in Visual Studio includes a built-in ASP.NET Development Server that can be used to run the HttpHandler - with or without debugging. When you start it, you'll see the following window:
Apart from that, your default browser will also be started. Calling the NumToWord.ashx HttpHandler with the ?Str=ASP.NET Http Handlers with Delphi Prism, will give the following result:
Num to Word
The previous months, I've demonstrated ASP.NET Web Services with Delphi Prism using a special Word-2-Numbers example. The same thing can be used in combination with HttpHandlers, were we return an image with the number translated to (Dutch) words.
For this, I've added a new private method Num2Word to the NumToWord class, taking a value integer argument, returning a string:
method NumToWord.Num2Word(value: Integer): String; begin if value >= 1000000 then if (value mod 1000000) = 0 then Result := Num2Word(value / 1000000) + 'miljoen' else Result := Num2Word(value / 1000000) + 'miljoen ' + Num2Word(value mod 1000000) else if value >= 1000 then if (value mod 1000) = 0 then Result := Num2Word(value / 1000) + 'duizend' else Result := Num2Word(value / 1000) + 'duizend ' + Num2Word(value mod 1000) else if value >= 100 then if (value mod 100) = 0 then Result := Num2Word(value / 100) + 'honderd' else Result := Num2Word(value / 100) + 'honderd' + Num2Word(value mod 100) else case (value / 10) of 5,6,7,9: if (value mod 10) = 0 then Result := Num2Word(value / 10) + 'tig' else Result := Num2Word(value mod 10) + 'en' + Num2Word(value / 10) + 'tig'; 8: if value = 80 then Result := 'tachtig' else Result := Num2Word(value mod 10) + 'entachtig'; 4: if value = 40 then Result := 'veertig' else Result := Num2Word(value mod 10) + 'enveertig'; 3: if value = 30 then Result := 'dertig' else Result := Num2Word(value mod 10) + 'endertig'; 2: if value = 20 then Result := 'twintig' else Result := Num2Word(value mod 10) + 'entwintig'; 0,1: case value of 0: Result := 'nul'; 1: Result := 'een'; 2: Result := 'twee'; 3: Result := 'drie'; 4: Result := 'vier'; 5: Result := 'vijf'; 6: Result := 'zes'; 7: Result := 'zeven'; 8: Result := 'acht'; 9: Result := 'negen'; 10: Result := 'tien'; 11: Result := 'elf'; 12: Result := 'twaalf'; 13: Result := 'dertien'; 14: Result := 'veertien'; 15: Result := 'vijftien'; 16: Result := 'zestien'; 17: Result := 'zeventien'; 18: Result := 'achttien'; 19: Result := 'negentien' end end end;
The ProcessRequest method itself must be modified a little bit - taking a value from a parameter X, converting it to an integer, and then to the words which are finally put on the image (after calculating the required width of the image).
method NumToWord.ProcessRequest(context: HttpContext); var Image: Bitmap; Str: String; X: Integer; F: Font; begin if Assigned(Context.Request.Params['X']) then Str := Context.Request.Params['X'].ToString else Str := ''; try X := Convert.ToInt32(Str); except X := 0 end; Str := Num2Word(X); F := new Font('Comic Sans MS', 14); Context.Response.ContentType := 'image/jpeg'; Image := Bitmap.Create( Convert.ToInt32(Graphics.FromImage(Bitmap.Create(1,1)).MeasureString(Str, F).Width), 36); Graphics.FromImage(Image).DrawString(Str, F, SolidBrush.Create(Color.PeachPuff), 2,2); Image.Save(Context.Response.OutputStream, ImageFormat.Jpeg); end;
The final result of calling NumToWord.ashx?X=1234567 can be seen below:
Returning XML Files
Another example of useful HttpHandlers is the fact that we can return any type of output: from text to binary, including images, but also XML documents.
Some of my clients have had the need to have an ASP.NET application return XML documents. At first, we wanted to use ASP.NET Web Services, since SOAP is also XML, right? But it soon turned out that the client at the receiving end had some specific "expectations" regarding the XML format, and SOAP would be too bloated and hard to parse for them. So, we thought about using an ASP.NET Web Form, "abusing" the Page HttpHandler and Response object by producing an XML document and then calling the Response.&End method. That would lead to the desired solution, but with the ASP.NET Page class and web form support as unnecessary overhead [is overhead ever necessary?].
ASP.NET custom HttpHandlers came as a welcome alternative, and proved to be a good solution for producing custom output without the overhead of the Page class. The code below shows an example of using an XmlTextWriter to produce an RSS feed, writing directly to the context.Response.OutputStream, producing an XML document on the fly.
Note that this particular code was written using RAD Studio 2007, and not 100% compatible with Delphi Prism code, but you'll get the idea.
procedure TMyHandler.ProcessRequest(context: HttpContext); var Xml: XmlTextWriter; begin context.Response.ContentType := 'text/xml'; Xml := XmlTextWriter.Create(context.Response.OutputStream, nil); Xml.Formatting := Formatting.None; // alternative: Indented Xml.WriteStartDocument; Xml.WriteStartElement('', 'rss', ''); // prefix, name, ns Xml.WriteAttributeString('version', '2.0'); // .... more XML Xml.WriteEndElement; // rss Xml.WriteEndDocument; Xml.Flush; Xml.Close; end;
If you set the Xml.Formatting to None, you don't get any extra spaces in the XML output, which can sometimes save up to 15% on the size of the output stream - another welcome way to increase performance by reducing bandwidth requirements.
In this article, I've demonstrated how we can use Delphi Prism to create Http Handlers that return just about anything (images in my example, but this could also be XML or PDF files).