A Simple Web Page Template Parser And A Template Pool

xiaoxiao2021-03-06  104

Download Demo Project - 27.4 KB Download Source - 5.09 KB

Introduction

ASP.NET provides us with many easy ways to build our web system, especially the code-behind technique which amazingly allows a separation of layout and code. However, ASP.NET also offers some mechanisms to allow you to build a custom programming model more than that offered by code-behind. One mechanism is HTTP Handler which gives you a means of interacting with the low-level request and response services of the IIS Web server, and provides functionality much like ISAPI extensions but with a simpler programming model. Great !.................. ..

But when you are writing custom HTTP handlers, hard-coding the page layout is boring and error-prone. We do need a way to separate layout from code. Thus, class TmplParser, TemplatePool etc. were born. The main job of class TmplParser is to parse a layout template file with some tags and labels whose rules are simple and defined by myself :-). The class TemplatePool is used to buffer a set of templates, which can reduce the I / O operations with less template file reading, And Improve The Performance. I Will Illustrate How To Use Them To Separate Layout from Code, Later in this article.

Notes: It's the first time that I programmed ASP.NET, the first time that I programmed in C #, the first time that I touched IIS, the first time that I submitted an article to Code Project So, there must be some problems or. Something Not Good Enough In The Article and Code. And i do welcome any feedback. Thanks!

USING THE CODE

First, I will tell you the rules of using the parser and some basic information as well.A template file consists of two and only two basic elements They are block and label A block is defined with begin-flag <-..! BEGIN : Blockname -> and end-flag . A label is defined as {labelname}. Let's look at an example template file example .html to you. EXAMPLE. HTML

example </ title></p> <p></ hEAD></p> <p><body></p> <p><table></p> <p><! - begin: format1 -></p> <p><TR> <TH> {THEAD1} </ th> </ tr></p> <p><! - begin: row1 -></p> <p><TR> <TD> {Value1} </ td> </ tr></p> <p><! - end: row1 -></p> <p><! - end: format -></p> <p><! - begin: format2 -></p> <p><TR> <TH> {THEAD1} </ tH> <TH> {THEAD2} </ tH> </ tr></p> <p><! - begin: row2 -></p> <p><Tr> <TD> {Value1} </ td> <td> {value2} </ td> </ tr></p> <p><! - end: row2 -></p> <p><! - end: format2 -></p> <p></ TABLE></p> <p></ body></p> <p></ Html> In the template file example.html, there are four blocks: FORMAT1, ROW1, FORMAT2 and ROW2 Block FORMAT1 has one label: THEAD1; block ROW1 has one label:. VALUE1; block FORMAT2 has two labels: THEAD1 and THEAD2 ; block ROW2 has two labels: VALUE1 and VALUE2 FORMAT1 and FORMAT2 are parent blocks of ROW1 and ROW2 respectively Actually, there is one more block It's the root block, namely the template file itself Let's call it DOCUMENT-BLOCK Rules..... Of template file definition</p> <p>.. A block name must be unique in one template file It's not only for code readability but also for easy use And my principle is:.. The simpler, the better A block can be a parent by enclosing other blocks But any two blocks can not overlap And there is no sibling relation, but parent-children relation is maintained between blocks A Deep Look When the template file is parsed by TmplParser as the following code:.. // tmplDir is the path of the directory in which example.html Is Placed.// IT's in The Format Like this: E: / DEMO / TMPL /</p> <p>// 50 is The capacity of the pool.</p> <p>// The pool Uses lru algorithm for template replacement.</p> <p>TemplatePool.singleton (TMPLDIR, 50);</p> <p>// What getTemplate Does is to load the template file,</p> <p>// Create and Initialize a New Instance</p> <p>// of tmparchser for this template file,</p> <p>// do the Parsing and return it if the filename</p> <p>// passed is not found in the pool,</p> <p>// Otherwise Just Return A Clone</p> <p>// of the instance find in pool.</p> <p>// it's thread-safe.</p> <p>// itemplate is an interface inherited by TmpLParser.</p> <p>Itemplate TMPL = TemplatePool.singleton (). GetTemplate ("eXample.html");</p> <p>// the second time, Just Return A Cloned Instance</p> <p>// for Template Example.html</p> <p>// The code below is just for instruction.</p> <p>. ITemplate tmpl2 = TemplatePool.Singleton () GetTemplate ( "example.html"); Then five BlockParser (a private class nested in TmplParser) instances will be built and maintained in TmplParser The contents of the five blocks are below: ROW1 <tr. > <TD> {Value1} </ td> </ tr> row2 <tr> <td> {value1} </ td> <td> {value2} </ td> <TR> Format1 <Tr> <TH> {THEAD1} </ tH> </ tr></p> <p><Tag: Row1 /> Format2 <Tr> <TH> {THEAD1} </ th> <TH> {THEAD2} </ th> </ TR> <Tag: Row2 /> Document-block <HTML></p> <p><HEAD></p> <p><title> example </ title></p> <p></ hEAD></p> <p><body></p> <p><table></p> <p><tag: format1 /></p> <p><tag: format2 /></p> <p></ TABLE></p> <p></ body></p> <p></ html> Once A Child Block (Example: Row1) IS out, ITS Current Content Will Be Placed Just Before The Tag <Tag: BlockName /> (Example: <Tag: Row1 />) in ITS Parent Block and It Will Return To the Original Raw Content. after the folding code is executed, the content of document-block will be ... itMplblock tmplrow1 = tmpl.Parseblock ("Row1");</p> <p>ITMPLBLOCK TMPLFORMAT1 = TMPL.PARSEBLOCK ("Format1");</p> <p>// - 1: Child Block (row1) is not out</p> <p>// Replace the label {thead1} in block format1 with os.</p> <p>TMPLFORMAT1.Assign ("THEAD1", "OS");</p> <p>// replace the label {value1} in block row1 with winxp.</p> <p>TMPLROW1.Assign ("Value1", "WinXP");</p> <p>// be Careful, The Code Below is commented Delibely.</p> <p>// SO Block Row1 Content Won't be Embeded Into ITS Parent Block Format1.</p> <p>// TMPLROW1.Out ();</p> <p>TMPLFORMAT1.out ();</p> <p>// - 2: Parent Block (Format1) IS Out Before Child Block (Row1)</p> <p>// you Won't see the row1 content in the result estil.</p> <p>TMPLFORMAT1.out ();</p> <p>TMPLROW1.Assign ("Value1", "WinXP");</p> <p>TMPLROW1.OUT ();</p> <p>// - 3: Replace the child block's label in the parent</p> <p>// block by outing child block first</p> <p>// it does work.</p> <p>TMPLROW1.OUT ();</p> <p>TMPLROW1.OUT ();</p> <p>TMPLFORMAT1.ASIGN ("THEAD1", "Tools");</p> <p>// {Value1} is a label in block row1.</p> <p>TMPLFORMAT1.Assign ("Value1", "Visual Studio .NET");</p> <p>// - 4: General Usage</p> <p>ITMPLBLOCK TMPLROW2 = TMPL.PARSEBLOCK ("Row2"); itMplBlock Tmplformat2 = TMPL.PARSEBLOCK ("Format2");</p> <p>TMPLFORMAT2.Assign ("THEAD1", "country");</p> <p>TMPLFORMAT2.ASSIGN ("THEAD2", "City");</p> <p>TMPLROW2.ASSIGN ("Value1", "China");</p> <p>TMPLROW2.Assign ("Value2", "pekin");</p> <p>TMPLROW2.Out ();</p> <p>TMPLROW2.ASSIGN ("Value1", "China");</p> <p>TMPLROW2.Assign ("Value2", "Shanghai");</p> <p>TMPLROW2.Out ();</p> <p>TMPLROW2.Assign ("Value2", "Tianjin");</p> <p>TMPLROW2.Out ();</p> <p>TMPLROW2.Assign ("Value2", "Chongqing");</p> <p>TMPLROW2.Out ();</p> <p>TMPLROW2.Assign ("Value2", "Shenzhen");</p> <p>TMPLROW2.Out ();</p> <p>TMPLFORMAT2.Assign ("Value1", "China");</p> <p>TMPLFORMAT2.Out ();</p> <p>// Calling The Method Parseblock, The One without</p> <p>// Parameters, Can Get Document-block.</p> <p>ITMPLBLOCK TMPLDOC = TMPL.PARSEBLOCK ();</p> <p>TMPLDoc.out ();</p> <p>// the next step will usually be like the code below</p> <p>// Which Just Sends The Result Content to the Client.</p> <p>Response.write (TMPLDoc.blockstring); THE RESULT Content of Document-Block Is Shown As Follows: <HTML></p> <p><HEAD></p> <p><title> example </ title></p> <p></ hEAD></p> <p><body></p> <p><table></p> <p><! - 1 -></p> <p><TR> <TH> OS </ th> </ tr></p> <p><! - 2 -></p> <p><TR> <TH> {THEAD1} </ th> </ tr></p> <p><! - 3 -></p> <p><TR> <TH> Tools </ th> </ tr></p> <p><TR> <TD> Visual Studio .NET </ TD> </ TR></p> <p><TR> <TD> Visual Studio .NET </ TD> </ TR></p> <p><! - 4 -></p> <p><TR> <TH> Country </ th> <TH> City </ tH> </ tr></p> <p><Tr> <TD> China </ TD> <TD> Pekin </ TD> </ TR></p> <p><Tr> <TD> China </ TD> <TD> Shanghai <TD> </ Tr> <TD> <TD> China </ TD> <TD> Tianjin </ TD> </ TR></p> <p><Tr> <TD> China </ TD> <TD> CHONGQIN </ TD> </ TR></p> <p><Tr> <TD> China </ TD> <TD> Shenzhen </ TD> </ TR></p> <p></ TABLE></p> <p></ body></p> <p></ Html> The comments in the result content are added just for your convenience to contrast to the C # code above. They do not exist in the real result content. As you all see, the presentation code (HTML) can be reused much WITH THIS TECHNIQUE.</p> <p>Now, I believe that you have known much about this technique, which really will makes me happy :-) One point should be accentuated That is:.. With one block's Out method being not called, its content will not be placed in its Parent.</p> <p>Second, I will illustrate how to use the code with a simple demo project. Actually, it's a framework shown with the template parser and pool more than a usage instruction. However, I'll just list the main code. Details should be viewed in The source code by yourself. All the source code can be downloaded through the link.</p> <p>Build a web site called Demo whose virtual directory is the one that you extract the demo project's source files to. Below I will use $ DEMO to refer to this virtual directory. In the IIS, disable all the access privilege of the directory $ DEMO / tmpl under which our web page template files are placed, and $ DEMO / src under which our source code files are placed. It just prevents clients from accessing to any resources under the two directories in any way. View the config file web.config. Each request whose path matches * do.aspx will be handled by Demo.Handler.Controller $ DEMO is the absolute path of your website virtual directory For example, if your virtual directory is E:.. / MyWebsite / demo, then the config below Should be: <add key = "tmpldir" value = "e: / mywebsite / demo / tmp1 /" /> <configuration> <system.Web></p> <p>...</p> <p><httphandlers></p> <p><add verb = "*" path = "* danaaspx" type = "demo.handler.controller, demo" /></p> <p></ httphandlers></p> <p>...</p> <p></system.web></p> <p><appsettings></p> <p><add key = "tmpldir" value = "$ demo / tmpl /" /></p> <p><add key = "capacity" value = "50" /></p> <p></ appsettings></p> <p></ configuration> The Following Code in the file global.asax.cs is to create a single instance of templatepool with the singleton pattern. public class global: system.web.httpApplication</p> <p>{</p> <p>...</p> <p>//</p> <p>// Application's Initialization</p> <p>//</p> <p>Protected void Application_Start (Object Sender, Eventargs E)</p> <p>{</p> <p>TemplatePool.singleton</p> <p>ConfigurationSettings.AppSettingS.Get ("tmpldir"),</p> <p>INT32.PARS (ConfigurationSettings.AppSettings.get ("Capacity"))))));</p> <p>}</p> <p>...</p> <p>..} What TemplatePool.Singleton does is shown below As you all see, it is thread-safe and uses the double-check trick to improve the performance And the pool instance will exist through the process life.public sealed class TemplatePool: ITmplLoader {</p> <p>...</p> <p>Public Static TemplatePool Singleton (String Tmpldir, Int Capacity)</p> <p>{</p> <p>IF (pool == null)</p> <p>{</p> <p>Lock (Objlock)</p> <p>{</p> <p>IF (pool == null)</p> <p>Pool = New TemplatePool (TMPLDIR, CAPACITY);</p> <p>}</p> <p>}</p> <p>Return pool;</p> <p>}</p> <p>...</p> <p>Private static templatepool pool = null;</p> <p>Private static object objlock = new object ();</p> <p>} View class demo.handler.controller to see how the handler deals with the request. Public class controller: IHTTPHANDAL, IREQUIRESSESSITIONSTATE</p> <p>{</p> <p>Public void processRequest (httpcontext context)</p> <p>{</p> <p>...</p> <p>// a more Sophistated Way Is To Put the Info,</p> <p>// Such as demo.application.menudealer, MenuPanel.html ETC,</p> <p>// INTO A Config (An XML File OR A Table In DB Or Others).</p> <p>Iapplication aapp = (ketprib)</p> <p>Activator.createInstance (Type.GetType ("Demo.Application.menudeler));</p> <p>THEAPP.INIT ("MenuPanel.html", "Login.html");</p> <p>THEAPP.SESSION = Context.Session;</p> <p>THEAPP.DOPROCESS (CONTEXT.REQUEST.PARAMS);</p> <p>Context.Response.write (THEAPP.out);</p> <p>}</p> <p>...</p> <p>View class demo.application.menudeler to see what method doprocess does. It just composites the data with the template to generate a string to be responded. Public class menudealer: dealer</p> <p>{</p> <p>...</p> <p>// generate menus</p> <p>String OldModName = ""</p> <p>String Modname = NULL;</p> <p>String username = reqparams.get ("username");</p> <p>Hashtable htmenu = NULL;</p> <p>ITMPLBLOCK TMPLMENUFRM = OUTPAGETMPL.PARSEBLOCK ("MenufRM");</p> <p>ITMPLBLOCK TMPLMENU = OUTPAGETMPL.PARSEBLOCK ("MENU"); INT Modix = 0;</p> <p>Operation Operation = new operation ();</p> <p>Operation.getMenuStart ();</p> <p>String Action = "Do.ASPX? OPNAME =";</p> <p>While (htmenu = Operation.getMenuNext ())! = null)</p> <p>{</p> <p>MODNAME = (String) HTMENU ["MODNAME"];</p> <p>IF (! MODNAME.EQUALS (OldModName))</p> <p>{</p> <p>IF (MODIDX> 0)</p> <p>TMPLMENUFRM.out ();</p> <p>TMPLMENUFRM.Assign ("iDIDX", Modidx.toString ());</p> <p>TMPLMENUFRM.Assign ("Module", MODNAME;</p> <p>OldModName = modname;</p> <p>Modidx ;</p> <p>}</p> <p>String opName = (string) htmenu ["opName"];</p> <p>TMPLMENU.Assign ("Request", OPNAME ACTION OPNAME);</p> <p>TMPLMENU.Assign ("Menu", (String) HTMENU ["OpValue"]);</p> <p>TMPLMENU.Out ();</p> <p>}</p> <p>IF (MODIDX> 0)</p> <p>TMPLMENUFRM.out ();</p> <p>TMPLDOC = OUTPAGETMPL.PARSEBLOCK ();</p> <p>TMPLDoc.assign ("UserName", UserName;</p> <p>...</p> <p>} View class demo.application.dealer to see what The property out and outpagetmpl do. Public Abstract Class dealer: ketprific</p> <p>{</p> <p>...</p> <p>Public String Out</p> <p>{</p> <p>get</p> <p>{</p> <p>IF (TMPLDOC == NULL)</p> <p>Return NULL;</p> <p>TMPLDoc.out ();</p> <p>Return TMPLDoc.blockstring;</p> <p>}</p> <p>}</p> <p>Protected Itemplate GetTemplate (String Tmplfilename)</p> <p>{</p> <p>Return (itemplate)</p> <p>(TemplatePool.singleton (). GetTemplate (TMPLFileName);</p> <p>}</p> <p>Protected Itemplate OutpageTMPL</p> <p>{</p> <p>get</p> <p>{</p> <p>IF (OutpageTMPL == NULL)</p> <p>OutpageTMPL = GetTemplate (OUTPAGE);</p> <p>Return OutpageTMPL;</p> <p>}</p> <p>}</p> <p>...</p> <p>}</p> <p>At last, build the system with Visual Studio .NET, and you will see what the image above shows. And I believe you will find more useful code in the demo project's source code. As I already said, the demo project is just a framework For A Small Project. May You like it!</p> <p>Points of interest</p> <p>When I implement class TemplatePool, I need a class for linked list. But, I can not find one in the namespace System.Collections. There may be someone shouting why not use ArrayList. Good question! But I guess that ArrayList will do the elements copying to move their positions when operations Insert and Remove are being done, which does raise performance penalty when the two operations are done frequently in LRU algorithm. So, I implemented a simple double linked list Agemo.Utility.DoubleLinkedList to meet my needs. It's really simple. Again, the simpler, the better. The non-recursive DFS algorithm is used to disassemble the template into blocks. The HttpHandler class must inherit the interface IRequiresSessionState which is just a marker interface if it will use the session object. I Was Harassed for a long time by this problem.agemo</p> <p>Click Here to View Agemo's Online Profile.</p></div><div class="text-center mt-3 text-grey"> 转载请注明原文地址:https://www.9cbs.com/read-124113.html</div><div class="plugin d-flex justify-content-center mt-3"></div><hr><div class="row"><div class="col-lg-12 text-muted mt-2"><i class="icon-tags mr-2"></i><span class="badge border border-secondary mr-2"><h2 class="h6 mb-0 small"><a class="text-secondary" href="tag-2.html">9cbs</a></h2></span></div></div></div></div><div class="card card-postlist border-white shadow"><div class="card-body"><div class="card-title"><div class="d-flex justify-content-between"><div><b>New Post</b>(<span class="posts">0</span>) </div><div></div></div></div><ul class="postlist list-unstyled"> </ul></div></div><div class="d-none threadlist"><input type="checkbox" name="modtid" value="124113" checked /></div></div></div></div></div><footer class="text-muted small bg-dark py-4 mt-3" id="footer"><div class="container"><div class="row"><div class="col">CopyRight © 2020 All Rights Reserved </div><div class="col text-right">Processed: <b>0.046</b>, SQL: <b>9</b></div></div></div></footer><script src="./lang/en-us/lang.js?2.2.0"></script><script src="view/js/jquery.min.js?2.2.0"></script><script src="view/js/popper.min.js?2.2.0"></script><script src="view/js/bootstrap.min.js?2.2.0"></script><script src="view/js/xiuno.js?2.2.0"></script><script src="view/js/bootstrap-plugin.js?2.2.0"></script><script src="view/js/async.min.js?2.2.0"></script><script src="view/js/form.js?2.2.0"></script><script> var debug = DEBUG = 0; var url_rewrite_on = 1; var url_path = './'; var forumarr = {"1":"Tech"}; var fid = 1; var uid = 0; var gid = 0; xn.options.water_image_url = 'view/img/water-small.png'; </script><script src="view/js/wellcms.js?2.2.0"></script><a class="scroll-to-top rounded" href="javascript:void(0);"><i class="icon-angle-up"></i></a><a class="scroll-to-bottom rounded" href="javascript:void(0);" style="display: inline;"><i class="icon-angle-down"></i></a></body></html><script> var forum_url = 'list-1.html'; var safe_token = 'cl5Y8uh_2F7idaDItr15bie_2FS4x8Rh1e6W988diCjlR95zE2tj77t_2F3BN_2B36_2BD8Z3x0DtcPcqxEWLHtmCvFy_2F5nw_3D_3D'; var body = $('body'); body.on('submit', '#form', function() { var jthis = $(this); var jsubmit = jthis.find('#submit'); jthis.reset(); jsubmit.button('loading'); var postdata = jthis.serializeObject(); $.xpost(jthis.attr('action'), postdata, function(code, message) { if(code == 0) { location.reload(); } else { $.alert(message); jsubmit.button('reset'); } }); return false; }); function resize_image() { var jmessagelist = $('div.message'); var first_width = jmessagelist.width(); jmessagelist.each(function() { var jdiv = $(this); var maxwidth = jdiv.attr('isfirst') ? first_width : jdiv.width(); var jmessage_width = Math.min(jdiv.width(), maxwidth); jdiv.find('img, embed, iframe, video').each(function() { var jimg = $(this); var img_width = this.org_width; var img_height = this.org_height; if(!img_width) { var img_width = jimg.attr('width'); var img_height = jimg.attr('height'); this.org_width = img_width; this.org_height = img_height; } if(img_width > jmessage_width) { if(this.tagName == 'IMG') { jimg.width(jmessage_width); jimg.css('height', 'auto'); jimg.css('cursor', 'pointer'); jimg.on('click', function() { }); } else { jimg.width(jmessage_width); var height = (img_height / img_width) * jimg.width(); jimg.height(height); } } }); }); } function resize_table() { $('div.message').each(function() { var jdiv = $(this); jdiv.find('table').addClass('table').wrap('<div class="table-responsive"></div>'); }); } $(function() { resize_image(); resize_table(); $(window).on('resize', resize_image); }); var jmessage = $('#message'); jmessage.on('focus', function() {if(jmessage.t) { clearTimeout(jmessage.t); jmessage.t = null; } jmessage.css('height', '6rem'); }); jmessage.on('blur', function() {jmessage.t = setTimeout(function() { jmessage.css('height', '2.5rem');}, 1000); }); $('#nav li[data-active="fid-1"]').addClass('active'); </script>