Introduction
One of the most common things that .NET WebForms developers deal with is form data. Treating form data as a strongly typed editable collection improves the handling of form data greatly. I have created a wrapper class that handles many of the common scenarios developers run into. It allows form data to be read in as input for processing, and/or allows a collection to be created that can subsequently be used to pass data between pages as POST or GET data.
Here are the primary features provided by this library. When exporting the collection data into a querystring , it URL-encodes the value data for you, and when exporting the collection data into hidden variables in a form to be posted, it HTML-encodes this data. A very useful feature is that it provides the capability to use thread local storage (TLS) to allow the Server.Transfer
API call result in data being available in the page that execution has been transferred to. The ASP.NET model only permits the existing read-only collection of form data to be relayed to the transferred page.
I have created a test page that allows you to experiment with almost all of the import and export options. It allows you to submit the source page using either the GET or POST method. You always have the choice to Response.Redirect
using the GET method, or Server.Transfer
with TLS storing data between page. The other sample page provided shows how to take data passed into a page upon its initial load and store this data as a set of hidden fields on the form.
The code is extremely straightforward, yet is among the most useful utility classes that I work with. Data is imported using the BuildFromGetRequest
, BuildFromPostRequest
, and BuildFromTls
methods. Key value pairs can also be added or removed. Data is exported, if necessary, using the ToPostForm
, ToQueryString
, and ToTls
methods.
The only bit of code that is somewhat esoteric is the TLS-related API calls in System.Threading
. First, we allocate a named data slot using a name of our choice, with the Thread.AllocateNamedDataSlot
call. Since ASP.NET will use a thread per request, we are safe (unless the asynchronous page model is specifically being utilized) in using a constant name. Second, we obtain a reference to the data slot with Thread.GetNamedDataSlot
. Finally, we call the Thread.SetData
API to assign the collection to the data slot.
Retrieving TLS data is similar in nature to setting TLS data. First, the reference to the data slot is retrieved as above. Subsequently, the Thread.GetData
API will retrieve the collection. It is important to remember to deallocate the memory being used, with the Thread.FreeNamedDataSlot
call.
There are a few usage considerations worth noting in the class. TLS data is only useful in processing a single request. After a response is returned to the user, the thread will be returned to the thread pool. Another scenario worth noting is when a page has several text fields with the same name. These fields are provided as a series of comma-delimited values for the name attribute used. For example, if the fields have values val1 and val2, the results would be "val1, val2". Supporting this usage would be difficult at best. I ignore this scenario and treat the results as a single value (as above). Another design choice is that if you add an entry whose key already exists in the collection, then the old entry is deleted. Yet another design choice made was to always remove the entry, if it exists, with a key value of submit, since this is commonly used as the name of the Submit button field.
Imports System.Collections.Specialized
Imports System.Text
Imports System.Threading
Public Class HttpFormUtil
Inherits System.Object
Private fc As New NameValueCollection
Private sActionPage As String
Public Sub Add(ByVal sKeyName As String, ByVal sValString As String)
If Not fc.Item(sKeyName) Is Nothing Then
fc.Remove(sKeyName)
End If
fc.Add(sKeyName, sValString)
End Sub
Public Function Count() As Integer
Return fc.Count()
End Function
Public Sub Remove(ByVal sKeyName As String)
If Not fc.Item(sKeyName) Is Nothing Then
fc.Remove(sKeyName)
End If
End Sub
Public Function Lookup(ByVal sKeyName As String) As String
Dim sValueString As String
sValueString = fc.Item(sKeyName)
If sValueString Is Nothing Then
sValueString = String.Empty
End If
Return sValueString
End Function
Public Sub BuildFromPostRequest()
fc = New NameValueCollection(System.Web.HttpContext.Current.Request.Form)
End Sub
Public Sub BuildFromGetRequest()
fc = New NameValueCollection(System.Web.HttpContext.Current.Request.QueryString)
End Sub
Public Sub BuildFromTls()
Dim dataSlot As LocalDataStoreSlot
Dim retrievedData As Object
dataSlot = Thread.GetNamedDataSlot("passedData")
retrievedData = Thread.GetData(dataSlot)
Try
If retrievedData Is Nothing Then
Else
fc = CType(retrievedData, NameValueCollection)
End If
Catch ex As Exception
fc = New NameValueCollection
Finally
Try
Thread.FreeNamedDataSlot("passedData")
Catch ex As Exception
End Try
End Try
End Sub
Public Function ToPostForm() As String
Dim iCurrElem As Integer
Dim sb As New StringBuilder
Remove("submit")
For iCurrElem = 0 To fc.Count - 1
sb.Append("<input type=""hidden"" name =""")
sb.Append( System.Web.HttpContext.Current.Server.HtmlEncode(fc.Keys(iCurrElem)))
sb.Append(""" value=""")
sb.Append( System.Web.HttpContext.Current.Server.HtmlEncode(fc.Get(iCurrElem)))
sb.AppendLine(""" />")
Next
Return sb.ToString()
End Function
Public Function ToQueryString() As String
Dim sb As New StringBuilder
Dim oPageContext As System.Web.HttpContext
Dim iCurrElem As Integer
oPageContext = System.Web.HttpContext.Current
Remove("submit")
For iCurrElem = 0 To fc.Count - 1
If iCurrElem = 0 Then
sb.Append("?")
Else
sb.Append("&")
End If
sb.Append(oPageContext.Server.UrlEncode(fc.Keys(iCurrElem)))
sb.Append("=")
sb.Append(oPageContext.Server.UrlEncode(fc.Get(iCurrElem)))
Next
Return sb.ToString()
End Function
Public Sub ToTls()
Dim dataSlot As LocalDataStoreSlot
Remove("submit")
Thread.AllocateNamedDataSlot("passedData")
dataSlot = Thread.GetNamedDataSlot("passedData")
Thread.SetData(dataSlot, fc)
End Sub
Public Function ToDictionary() As Dictionary(Of String, String)
Dim oList As New Dictionary(Of String, String)
For Each currEntry As String In fc.AllKeys
oList.Add(currEntry, fc(currEntry))
Next
Return oList
End Function
End Class