A day in the life (of a developer) RSS 2.0
 Tuesday, September 23, 2008

ASP.NET makes it fairly simple to enable security on content hosted inside of ASP.NET (ASPX pages, ASHX, etc.) just by enabling form-based authentication. Any attempt to access those pages will automatically get redirected to a login page for authentication. However, static content (HTM or HTML pages) for example aren't passed through the ASP.NET pipeline, so anyone can access that content (unless you've set-up something like Basic Authentication) - your user's aren't required to authenticate before accessing the content.

It turns out it's pretty simple to get IIS to pass requests to ASP.NET and let it handle any permissions, logging, etc. that you may want to apply - in fact, this is how ASP.NET itself hooks into IIS. The first thing you need to do is open the IIS Manager. Right-click on the website and select properties. Click on the "Home Directory" tab, then click on the Configuration button. That will display a list of application extensions and the EXE/DLL that is responsible for those filetypes. We're going to handle any files with a .HTM extension, so click on Add. In the "Executable" field enter something like "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" - I just copied this from the ASPX extension and pasted it in. In the extension field enter ".HTM". Leave everything else as-is and click on OK. If you want to map HTML files as well, repeat this procedure and enter ".HTML" as the extension (w/o the quotes). Hit OK to all the prompts to close the various dialogs. At this point IIS will forward requests for files with either .HTM or .HTML files to ASP.NET. 

 

Properties

AddExtMap

Now we need to tell ASP.NET how to handle these file types by editing the web.config file to add an HTTP handler for these types. In the system.web section, we're going to map these types to use a built-in handler called "StaticFileHandler". The web.config will look something like this:

<system.web>
   <httpHandlers>
      <add path="*.htm" verb="*" type="System.Web.StaticFileHandler" />
      <add path="*.html" verb="*" type="System.Web.StaticFileHandler" />

Save the web.config. At this point requests for .HTM and .HTML will be passed through ASP.NET and it will pass the request to an instance of the StaticFileHandler class. If you have form-based security, you will automatically be redirected to log in as well before being able to view any HTM or HTML pages (assuming you've protected all pages). The main downside to pushing this through ASP.NET is that IIS can no longer handle accessing HTM/HTML pages, which will reduce the efficiency and scalability of the site. However, with most sites this isn't really much of an issue. The other issue I've noticed is that the StaticFileHandler class automatically sets the caching of the item served up to 1 day (regardless of what you've configured in IIS), and there isn't any way of overridding this behavior short of writing your own handler.

BTW - This can be used to secure other content as well, for example, PDF files, JPG's, GIF's, etc. You would just need to add an entry in IIS like we did for HTM/HTML files and make an associated entry in the web.config. In the example above I was specifically mapping each content type on an as-needed basis. I should also mention that you can also just use a wildcard mapping in IIS (option below the one we used) which will map any file types not in the first list. In that case you don't need to modify the web.config - it should be handled by the DefaultHttpHandler class (ASP.NET 2.0 and later - this isn't the case for 1.1).

If you're interesting in writing your own handler for static files, I did some searching and found this. He includes code for his own implementation of a static file handler which includes compression and caching.

 

Links

http://msmvps.com/blogs/omar/archive/2008/06/30/deploy-asp-net-mvc-on-iis-6-solve-404-compression-and-performance-problems.aspx
Tuesday, September 23, 2008 8:00:44 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -

ASP.NET | IIS
 Tuesday, April 15, 2008

One request that comes up from time to time is to embed a checkbox inside of an ASP.NET grid control for boolean data.

Unfortunately, one of the downsides to this is that the controls are "read only" - they don't let you check or uncheck them

without putting the row into edit mode. That works, but in some cases this isn't all that great either. What do you do if you just want a checkbox on each row, that's "live" and causes a postback as you check/uncheck items? Thankfully this doesn't take much code but figuring out the first time can be really painful. To get the "live" checkbox, just wrap it inside of a template. (In the example code I created a new Web Form named "GridCheckbox").

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GridCheckbox.aspx.cs" Inherits="WebApplication1.GridCheckbox" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<html xmlns="http://www.w3.org/1999/xhtml" >

<head id="Head1" runat="server">

    <title>Untitled Page</title>

</head>

<body>

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

    <div>       

        <asp:GridView runat="server" ID="grdList" AutoGenerateColumns="false"

            onrowdatabound="grdList_RowDataBound">

            <Columns>

                <asp:TemplateField>

                    <ItemTemplate>                       

                        <asp:CheckBox runat="server" ID="chkItem" Checked='<%# Container.DataItem %>' AutoPostBack="True" OnCheckedChanged="CheckChanged"/>                                              

                    </ItemTemplate>

                </asp:TemplateField>

            </Columns>

        </asp:GridView>

    </div>

    </form>

</body>

</html>

 

Make sure you set the "AutoPostBack" to true. Then set it's OnCheckedChanged event handler to call your server side code.

Now let's add some code to the Page_Load to give our grid something to bind to:

protected void Page_Load(object sender, EventArgs e)

{

    bool[] items = { true, true, false, false, true };

    if (!Page.IsPostBack)

    {

        this.grdList.DataSource = items;

        this.grdList.DataBind();

    }

}

 

 

In my example code, I'm just binding to a simple boolean collection to keep things simple. Now we need an event handler to respond to our checkbox event:

protected void CheckChanged(object sender, EventArgs e)

{          

}

 

The code doesn't do anything yet but at this point you might be wondering how we figure out which checkbox we checked - normally, your grid is bound to a different row in some table. Each checkbox is related to that specific row. How or where do we get that from? The grid isn't much help in this case because it's not raising the event. If we were using a Button we could set it's CommandName and/or CommandArgument properties to hold something like the primary key of the row in our table. The checkbox control doesn't have any such properties. ASP.NET controls have an InputAttributes collection where you can place name/value pairs - this information is passed to the client (and back to the server), so it makes this relatively painless.

First, add an event to onRowDataBound of the grid (already shown in the ASPX above). Now add the event in our code-behind:

protected void grdList_RowDataBound(object sender, GridViewRowEventArgs e)

{       

}

 

We can't reference the control directly by name, since it was embedded in the grid and ASP.NET has made sure it's uniquely named. Let's use the FindControl method to do this. I've also added the code to add a Value attribute and added a member called m_row. I'm just using this so we can have a different value on each and every row of the grid. In a real application this is where you'd pull the primary key value from your datasource (probably using e.Row.DataItem).

private int m_row = 0;

protected void grdList_RowDataBound(object sender, GridViewRowEventArgs e)

{

    CheckBox chk = e.Row.FindControl("chkItem") as CheckBox;

 

    if (chk != null)

        chk.InputAttributes.Add("Value", this.m_row.ToString());

    this.m_row++;

}


Now we're just about done - all that's left is retrieving this value back on the server when the Checkbox is checked or unchecked:

protected void CheckChanged(object sender, EventArgs e)

{

    CheckBox chk = sender as CheckBox;

    string val = "";

    if (chk != null)

        val = chk.InputAttributes["Value"];

}


So our completed codebehind page looks like this:

using System;

using System.Collections;

using System.Configuration;

using System.Data;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.HtmlControls;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

 

namespace WebApplication1

{

    public partial class GridCheckbox : System.Web.UI.Page

    {

        private int m_row = 0;

        protected void Page_Load(object sender, EventArgs e)

        {

            bool[] items = { true, true, false, false, true };

            if (!Page.IsPostBack)

            {

                this.grdList.DataSource = items;

                this.grdList.DataBind();

            }

        }

 

        protected void CheckChanged(object sender, EventArgs e)

        {

            CheckBox chk = sender as CheckBox;

            string val = "";

            if (chk != null)

                val = chk.InputAttributes["Value"];

        }

 

        protected void grdList_RowDataBound(object sender, GridViewRowEventArgs e)

        {

            CheckBox chk = e.Row.FindControl("chkItem") as CheckBox;

 

            if (chk != null)

                chk.InputAttributes.Add("Value", this.m_row.ToString());

            this.m_row++;

        }

    }

}

Now when you run this every time you check or uncheck an item you should see the page post-back (set a breakpoint on CheckChanged). You can now update your data, or whatever else you need to do in response to this event.
Tuesday, April 15, 2008 9:18:15 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -

ASP.NET
 Friday, April 04, 2008

This has hit me more than once already. If you create an HTTP Handler (ASHX) file, by default the context object reference (passed in to ProcessRequest) doesn't contain a reference to the Session object, so attempting to access context.Session will fail.

The fix is to add a "using System.Web.SessionState" to the using section, then inherit from the IReadOnlySessionState interface.

 

Ex.

 

using System.Web.SessionState;

public class MyAshx : IHttpHandler, IReadOnlySessionState
{

}

 

This will give you read only access to the session state. If you need to add to the session, inherit from IRequiresSessionState instead.

Friday, April 04, 2008 6:15:49 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -

ASP.NET
 Wednesday, November 28, 2007

I'm not sure when this was added (if it's just in VS 2008 or if it's been there since VS 2005), but I just noticed this while I was updating the website (I just added a download section). I created a new master page for the site, then updated the original home page with one that used the new master page. The master page is now home to the header, menu, and footer with a "hole" punched in the middle for my custom content. Then I added the download page and started adding files. You might not have noticed before, but the home page has a footer which moves with the browser; it basically sticks to the bottom by using a bit of CSS. This works well on the home page, and appeared to be OK while I was working on the download page. That is. up until the number of downloads forced the page to scroll. At that point the footer actually sat on top of the downloads listed, which isn't quite the effect I was hoping for. If I changed the footer style to float, it worked well for the download page but was pretty ugly for the home page (since it's a bit sparse the footer was like 1 inch below the header).

I played around with the idea of embedding a div tag in my home page that had relative heights to force the content back to where it belonged. That didn't work out too nicely, so I briefly thought about using JavaScript to handle this instead. That seemed like overkill, so I nixed that idea. If the footer was defined in my home page content section, I could just override the CSS style for that one page. However, it's in the master page. I could define two CSS files instead, but I really didn't like the idea of having to maintain two stylesheets. That's when I noticed a new section in my master page - a content holder for the head section of the page:

 MPContent

 

That made it really simple to just add an overriding style (with the same name as the one defined in my CSS file) into the home page content. I had to move the CSS link above the ContentPlaceHolder so that it was defined before my overriding style. Then I just added a <style> section inside of the header and I was good to go. This isn't earth shaking stuff, but it was a nice addition.

Wednesday, November 28, 2007 10:01:27 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -

ASP.NET
 Saturday, October 27, 2007

Imagine this scenario: You want to create a simple user control that you can use in a few web pages. It incorporates a few JavaScript functions that change the UI slightly during a mouse click; pretty standard stuff. The script isn't particularly useful in other controls, so putting it into a common library doesn't make much sense. It could be broken out into it's own library, which can at least be cached by a browser. But that introduces another dependency in the hosting page - you have to remember to include a reference to this JS library before things will work. The easiest thing to do would be to just include the JS code inside the user control itself (I'm skipping the option of embedding it as a resource; maybe another day). Including the code in the control is pretty simple and seamless. Until you drop two instances of the same control on page. What you'll find (like I did) is that your JS code is now rendered one time for each control. That's not very cool. In fact, you run into this same issue even if you decide to move the code into it's own library - you end up with more than one reference to the library.

 

A relatively simple way around all this is the ClientScriptManager class that is available hanging off the Page object as ClientScript. It lets you register JS code to be rendered into the main page. You can also check for the existence of the registered code (to keep from registering it more than once, regardless of the number of controls on the page). In my case, since I'm just embedding the code right in the page, there are only two methods required for this: IsClientScriptBlockRegistered and RegisterClientScriptBlock.

 

The routines use the object type and the passed in name as a key; if you don't pass it into IsClientScriptBlockRegistered (or the other variants) the routines will use the Page's type. I'd suggest always passing in something besides the page type (for example, the user control type). This will keep you from running into conflicts if you happen to use the same registration name (although things will still break if you picked the same JS function names).

 

Here's what that might look like:


if (!Page.ClientScript.IsClientScriptBlockRegistered(typeof(WebmailAddressBookBizObj), "ToggleCheck"))

{

    string js = @"

                  function ToggleCheck(node)

                  {

                    if (node.Checked)

                        node.UnCheck();

                    else

                        node.Check();

                  }";

 

    Page.ClientScript.RegisterClientScriptBlock(typeof(WebmailAddressBookBizObj), "ToggleCheck", js, true);

}

Saturday, October 27, 2007 3:07:56 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -

ASP.NET | Javascript
 Thursday, October 18, 2007

Once in a while I’ll get lucky and find a good sample of a solution to exactly the problem I’m having. Last Friday was my day for this. I’ve been working on a portal-style web application which includes security and logging on everything. I make some logging calls from within my global.asax file. Running ASP.NET web apps under development isn’t the fastest thing in the world and it’s always a bit of a problem to get a good feel for it’s speed in production. However, I had a few pages which felt particularly sluggish.

 

I’ve played around with various .NET code profilers and while they’ve all been OK, I haven’t committed to buying any of them. This time around I downloaded and installed ANTS Profiler from Red Gate. Once limitation that most profilers seem to have is that they require me to debug under IIS, which means I have to screw around with the code a bit to get it ready for deployment before I can look into any performance issues (although you can usually force them to fire up Cassini and profile it that way, it’s usually not very intuitive). So it was kind of nice to be able to profile an app. directly from ANTS.

 

After a quick profile test it was pretty clear the problem was in the logging code. There isn’t much going on here, grab some info. about the request (IP Address, page, time, user ID, etc.) create a connection, then create and run the insert SQL code. It was adding around 250ms to every page hit. I couldn’t do much to optimize this code, but I thought I might be able to create a new thread to run this on so the user wouldn’t have to wait while it ran. I found an excellent example of the exact issue by Peter Bromberg on the Egghead Café site: http://www.eggheadcafe.com/tutorials/aspnet/aa5ff306-16a1-4014-a51d-6ffde0894a0d/aspnet-request-logging-w.aspx

 

I didn’t need his connection or logging code, so I just snagged the thread class. Then I created a delegate for the logging method call.

 

My delegate code:

public delegate void SqlExecDelegate(SqlCommand command);

Then I just wrapped the call and passed in the SqlCommand variable I needed:

ThreadUtil.FireAndForget(new SqlExecDelegate(SqlExec), Command);

Here’s the code I grabbed from Peter – his article explains how it works in detail:

using System;
using System.Runtime.CompilerServices; 

namespace WirelessToyz.Utility
{
    /// <summary>
    /// Downloaded this class from:
    /// http://www.eggheadcafe.com/tutorials/aspnet/aa5ff306-16a1-4014-a51d-6ffde0894a0d/aspnet-request-logging-w.aspx
    /// </summary>
    public class ThreadUtil
    {
        private static AsyncCallback callback = new AsyncCallback(ThreadUtil.EndWrapperInvoke);
        private static DelegateWrapper wrapperInstance = new DelegateWrapper(ThreadUtil.InvokeWrappedDelegate);
 
        private static void EndWrapperInvoke(IAsyncResult ar)
        {
            wrapperInstance.EndInvoke(ar);
            ar.AsyncWaitHandle.Close();
        }
 
        public static void FireAndForget(Delegate d, params object[] args)
        {
            wrapperInstance.BeginInvoke(d, args, callback, null);
        } 

        private static void InvokeWrappedDelegate(Delegate d, object[] args)
        {
            d.DynamicInvoke(args);
        } 

        private delegate void DelegateWrapper(Delegate d, object[] args);
    }
}

I immediately saw a difference in my app. The pages felt a noticeable snappier. While it still takes 250ms to run my logging code, I no longer have to make the user wait for it.

Links

http://www.eggheadcafe.com/tutorials/aspnet/aa5ff306-16a1-4014-a51d-6ffde0894a0d/aspnet-request-logging-w.aspx

Thursday, October 18, 2007 9:47:55 PM (Eastern Standard Time, UTC-05:00)  #    Comments [2] -

ASP.NET

This is one of those things that's really easy to do, but not particularly clear the first time you run across it (well, it wasn't clear to me at least). There are times when you need to access ASP.NET created controls from within your JavaScript code. In the ASPX page you may have set the ID of a button control to "btnAdd". However, when you run the page the control name gets mangled into something like ctl00_ContentPlaceHolder1_btnAdd. In order to reference it in your JavaScript, you need that mangled name. To get it, just embed something like this in your JavaScript code:

function DoSomething()
{
    var addButton = document.getElementById('<%= btnAdd.ClientID %>');
    if (addButton)
        alert('<%= btnAdd.ClientID %>');
}

 

When the page is run, the ASP.NET eval engine will replace the ID with the correct one.

Thursday, October 18, 2007 9:36:20 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] -

ASP.NET | Javascript


Navigation
Archive
<October 2008>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2008
Paul Mrozowski / RCS Solutions, Inc.
Sign In
Statistics
Total Posts: 57
This Year: 32
This Month: 0
This Week: 0
Comments: 21
All Content © 2008, Paul Mrozowski / RCS Solutions, Inc.
DasBlog theme 'Business' created by Christoph De Baene (delarou)