Solution Architecture For The Masses. Step 4: Design Your Presentation Layer – Part II
This post is a follow up of Part I. I am following the Web Application frame outlined in Web Application Archetype. In Part I I covered Authentication, Authorization, Caching, Exception, Management, Logging & Instrumentation. In this post I will cover the rest of Web Application categories – Navigation, Page Layout (UI), Page Rendering, Presentation Entity, Request Processing, Session Management, and Validation | Quick Resource Box
|
NavigationTo visualize navigation I am using ASP.NET’s built-in treeview control bound to web.sitemap file and SiteMapPath [bread crumb] control for visualization. The navigation controls located in dedicated ACSX files that are placed inside formatted Master Page – more on that in Page Layout (UI) section. Follow these steps:
<?xml version="1.0" encoding="utf-8" ?> <siteMap xmlns="https://schemas.microsoft.com...." > <siteMapNode url="deafault.aspx" title="Home" description=""> <siteMapNode url="Restricted/UC1.aspx" title="UC1" description="" /> <siteMapNode url="Restricted/UC2.aspx" title="UC2" description="" /> </siteMapNode> </siteMap>
<%\@ OutputCache Duration="3000" VaryByParam="None"%> Page Layout (UI)Page layout is based on ASP.NET built-in Master Pages. Think of Master Page as a “server side frameset”. The page assembled on the server from the Master Page and the controls it hosts, rendered as a single HTML output and sent for rendering in the browser. The advantages are:
Follow these steps to implement page layout based on Master Page:
<table class="style1"> <tr> <td colspan="2" align="left" valign="top"> </td> </tr> <tr> <td align="left" valign="top"> </td> <td align="left" valign="top"> <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server"> </asp:ContentPlaceHolder> </td> </tr> </table>
Page RenderingPage rendering is the process of generating and displaying HTML in the browser. It usually involves displaying dynamic data from some data source such as SQL Server. For example, if I am required to present all transactions I have made, I’d follow these steps:
<asp:DataPager ID="DataPager1" runat="server" PageSize="3" PagedControlID="ListView1">
Presentation EntityPresentation entity is the data being presented on the page. In my case it is TransactionInfo that was implemented together with its business services in Step 2. In my case I get the list of the transactions generated by corresponding business services component and then bind it to the ListView control for presentation. Also I handle the paging event by calling ListView1_PagePropertiesChanging function [with little help from this post]. Code behind looks similar to the one that follows this paragraph. Notice IsPostBack check to make sure I do not run this code in case of postback [POST]. In this case the presentation re-generated from the ViewState so I save on CPU cycles and improve performance with regards to response time and resource utilization: protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BindListView(); } } protected void ListView1_PagePropertiesChanging( object sender, PagePropertiesChangingEventArgs e) { this.DataPager1.SetPageProperties(e.StartRowIndex, e.MaximumRows, false); BindListView(); } void BindListView() { List<TransactionInfo> transactions = TransactionServices.GetCurrentUserTransactions(); ListView1.DataSource = transactions; ListView1.DataBind(); } In this specific case all the input was available immediately in the request. In fact GetCurrentUserTransactions would use IPrincipal and IIdentity interfaces to access HttpContext.Current.User to locate the actual current user internally as described in Part I – that is why I do not pass any parameters to it. In some cases I will need to provide parameters submitted from different controls such as in the case of search scenario. In that case the parameter submitted by one ASCX control and the rendering of the result performed by other control or page. This is covered in the next section, Request Processing. Request ProcessingWhen looking at the guidelines for request processing the recurrent theme is separating user interface, processing, and the data. The patterns that mentioned are MVC and MVP. Following these patterns achieves loosely coupling between the components which increases maintainability. In other words, when one component changes it does not affect the other one. One recurrent scenario is search. I followed these steps to implement my search:
protected void btnSearch_Click(object sender, EventArgs e) { string searchCriteria = txtSearch.Text; Page.Items.Add("SearchCriteria", searchCriteria); }
protected void Page_Prerender(object sender, EventArgs e) { string searchCriteria = Page.Items["SearchCriteria"] as string; if (null != searchCriteria) { Label2.Text = string.Format("Your search for {0} generated the following results: ",searchCriteria); List<AccountInfo> accounts = AccountServices.FindAccountsBySearchCriteria (searchCriteria); GridView1.DataSource = accounts; GridView1.DataBind(); } }
In this example the page shows the results that and it’s completely decoupled from the search input control that accepts the input. Session ManagementThe guide stresses the importance of session management with regards to scalability and performance: “When designing a Web application, an efficient and secure session-management strategy is important for performance and reliability.” Here are variations and implications:
For my case I will be using in-proc Session state. It’s easy to implement, I do not plan to store large data to avoid memory pressure, serialization cost, and recycles. I am taking the risk of the case when the end user loses the state due to the server failure since my scenarios do not assume massive data input. ValidationInput and Data Validations are extremely important aspects that affect system’s security and reliability. Making sure only sanitized input gets in prevent unnecessary exceptions that eat up CPU cycles. It also helps prevent injection attacks. Making sure the system produces encoded output prevents Cross Site Scripting [XSS] attacks that usually end up with identity theft. For input and data validation I will use:
After reviewing the code I have produced so far, following are the changes that need to be done:
Label2.Text = EncodingServices.HtmlEncode( string.Format ("Your search for {0} generated the following results: ",searchCriteria));
<%# EncodingServices.HtmlEncode(((Entities.TransactionInfo)Container.DataItem).TransactionAccount)%> || <%# EncodingServices.HtmlEncode(((Entities.TransactionInfo)Container.DataItem).TransactionAmount.ToString())%> || ...
<asp:RegularExpressionValidator ID="RegularExpressionValidator1" runat="server" ControlToValidate="txtSearch" ErrorMessage="Invlaid input" ValidationExpression= ""^[a-zA-Z'.\s]{1,40}$" "> </asp:RegularExpressionValidator>
if (!Page.IsValid) { //THE BETTER WAY WOULD BE TO USE //VALIDATION SUMMARY CONTROL throw new ApplicationException("Invalid input provided."); } Related Books |