Time Logging – Convert HTML – C# example
In this article, you will find an example of using custom message handlers to convert HTML from ZIP archive to PDF and log the execution time of this process.
Sometimes, in order to optimize the performance, you may need to find out the web request execution time. For example, you want to know how much time is taken to convert HTML-based file from a ZIP archive to PDF.
Execution Time Logging
Creating a Message Handler for a Custom Schema Implementation
Let’s create a custom handler that you can use for custom schema (protocol) implementation. Take the next steps:
- Use the necessary Namespace, which is the Aspose.Html.Net.
- Define your own CustomSchemaMessageHandler class that will be derived from the MessageHandler class.
- Initialize an instance of the CustomSchemaMessageHandler class and define a Filter property for it.
- Create the CustomSchemaMessageFilter class that is derived from the MessageFilter class.
- Override the Match() method of the MessageFilter class to implement the custom message handler behaviour.
The following code snippet shows how to create the CustomSchemaMessageHandler class:
1// Define the CustomSchemaMessageFilter class that is derived from the MessageFilter class
2class CustomSchemaMessageFilter : MessageFilter
3{
4 private readonly string schema;
5 public CustomSchemaMessageFilter(string schema)
6 {
7 this.schema = schema;
8 }
9 // Override the Match() method
10 public override bool Match(INetworkOperationContext context)
11 {
12 return string.Equals(schema, context.Request.RequestUri.Protocol.TrimEnd(':'));
13 }
14}
15
16// Define the CustomSchemaMessageHandler class that is derived from the MessageHandler class
17abstract class CustomSchemaMessageHandler : MessageHandler
18{
19 // Initialize an instance of the CustomSchemaMessageHandler class
20 protected CustomSchemaMessageHandler(string schema)
21 {
22 Filters.Add(new CustomSchemaMessageFilter(schema));
23 }
24}
The CustomSchemaMessageHandler(schema
) constructor instantiates the CustomSchemaMessageHandler object and takes the schema
as a parameter. The Add() method append the CustomSchemaMessageFilter object at the end of the collection. The Match() method tests whether a context
satisfies the filter criteria.
Creating the ZipFileSchemaMessageHandler for Working with ZIP archives
The following code snippet shows how to create the ZipFileSchemaMessageHandler class for working with ZIP archives:
1// Define the ZipFileSchemaMessageHandler class that is derived from the CustomSchemaMessageHandler class
2class ZipFileSchemaMessageHandler : CustomSchemaMessageHandler
3{
4 private readonly Archive archive;
5
6 public ZipFileSchemaMessageHandler(Archive archive) : base("zip-file")
7 {
8 this.archive = archive;
9 }
10
11 // Override the Invoke() method
12 public override void Invoke(INetworkOperationContext context)
13 {
14 var pathInsideArchive = context.Request.RequestUri.Pathname.TrimStart('/').Replace("\\", "/");
15 var stream = GetFile(pathInsideArchive);
16
17 if (stream != null)
18 {
19 // If a resource is found in the archive, then return it as a Response
20 var response = new ResponseMessage(HttpStatusCode.OK);
21 response.Content = new StreamContent(stream);
22 response.Headers.ContentType.MediaType = MimeType.FromFileExtension(context.Request.RequestUri.Pathname);
23 context.Response = response;
24 }
25 else
26 {
27 context.Response = new ResponseMessage(HttpStatusCode.NotFound);
28 }
29
30 // Invoke the next message handler in the chain
31 Next(context);
32 }
33
34 Stream GetFile(string path)
35 {
36 var result = archive
37 .Entries
38 .FirstOrDefault(x => x.Name == path);
39 return result?.Open();
40 }
41}
In the above example, searching for a resource (zip archive) at its URI is realized. If a resource is found, FromFileExtension() method returns the MimeType of the resource.
Message Handler for a Web Request Execution Time Logging
The following code snippet shows how to create StartRequestDurationLoggingMessageHandler and StopRequestDurationLoggingMessageHandler to log the time taken for web request execution.
1// Define the RequestDurationLoggingMessageHandler class that is derived from the MessageHandler class
2abstract class RequestDurationLoggingMessageHandler : MessageHandler
3{
4 private static ConcurrentDictionary<Url, Stopwatch> requests = new ConcurrentDictionary<Url, Stopwatch>();
5
6 protected void StartTimer(Url url)
7 {
8 requests.TryAdd(url, Stopwatch.StartNew());
9 }
10
11 protected TimeSpan StopTimer(Url url)
12 {
13 var timer = requests[url];
14 timer.Stop();
15 return timer.Elapsed;
16 }
17}
18
19class StartRequestDurationLoggingMessageHandler : RequestDurationLoggingMessageHandler
20{
21 // Override the Invoke() method
22 public override void Invoke(INetworkOperationContext context)
23 {
24 // Start the stopwatch
25 StartTimer(context.Request.RequestUri);
26
27 // Invoke the next message handler in the chain
28 Next(context);
29 }
30}
31
32class StopRequestDurationLoggingMessageHandler : RequestDurationLoggingMessageHandler
33{
34 // Override the Invoke() method
35 public override void Invoke(INetworkOperationContext context)
36 {
37 // Stop the stopwatch
38 var duration = StopTimer(context.Request.RequestUri);
39
40 // Print the result
41 Debug.WriteLine($"Elapsed: {duration:g}, resource: {context.Request.RequestUri.Pathname}");
42
43 // Invoke the next message handler in the chain
44 Next(context);
45 }
46}
Adding the Message Handlers to the Pipeline
The key concept of message handlers work is chaining them together. We have created several message handlers and should add them to the pipeline in a specific order to implement the example of HTML from ZIP archive to PDF conversion time logging. Let’s perform the following steps:
- Create an instance of the Configuration class.
- Realize ZIP Custom Schema. Use the Add() method to append ZipFileSchemaMessageHandler to the end of the pipeline.
- Execute Duration Logging. Use the Insert() method to add the StartRequestDurationLoggingMessageHandler first in the pipeline, and the Add() method to append the StopRequestDurationLoggingMessageHandler to the end of the pipeline.
- Initialize an HTML document with specified configuration.
- Create the PDF Device and render HTML to PDF.
1// Add this line before you try to use the 'IBM437' encoding
2System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
3
4// Prepare path to a source zip file
5string documentPath = Path.Combine(DataDir, "test.zip");
6
7// Prepare path for converted file saving
8string savePath = Path.Combine(OutputDir, "zip-to-pdf-duration.pdf");
9
10// Create an instance of the Configuration class
11using var configuration = new Configuration();
12var service = configuration.GetService<INetworkService>();
13var handlers = service.MessageHandlers;
14
15// Custom Schema: ZIP. Add ZipFileSchemaMessageHandler to the end of the pipeline
16handlers.Add(new ZipFileSchemaMessageHandler(new Archive(documentPath)));
17
18// Duration Logging. Add the StartRequestDurationLoggingMessageHandler at the first place in the pipeline
19handlers.Insert(0, new StartRequestDurationLoggingMessageHandler());
20
21// Add the StopRequestDurationLoggingMessageHandler to the end of the pipeline
22handlers.Add(new StopRequestDurationLoggingMessageHandler());
23
24// Initialize an HTML document with specified configuration
25using var document = new HTMLDocument("zip-file:///test.html", configuration);
26
27// Create the PDF Device
28using var device = new PdfDevice(savePath);
29
30// Render ZIP to PDF
31document.RenderTo(device);
The Configuration() constructor creates an instance of the Configuration class. After the configuration is created, the GetService<INetworkService>(), MessageHandlers.Add() and MessageHandlers.Insert() methods are invoked. The Insert() and Add() methods append the custom message handlers in the collection of existing message handlers. The figure shows the chain of message handlers for this example:
Note: The
HTMLDocument(address, configuration
) constructor takes the absolute path to the ZIP archive. But all related resources have relative paths in the HTML document and in the example’s code.
You can download the complete examples and data files from GitHub.