Blog post .NET, Technical, Custom Development

Clean way to pass values across web pages

A common requirement of working with web applications is to pass values across web pages. For instance when we’re on a listing page clicking a hyperlink will redirect us to some detail page. This detail page then will render additional information. There are various ways to achieve something like that. Some are suitable in certain scenarios while others aren’t at all.

Let’s take a look at the usual suspects for this kind of requirement.

Cookies

Cookies are nowadays a rather disliked feature of the internet. People try to avoid them whenever possible and users can even disable them. Otherwise a completely valid option here.

Session State

The session state is a common tool for persisting values across the user journey. It’s unique for each user and works well until the session expires – the user drops out. The downside is that session variables consume memory and if not cleaned up efficiently and on time the web server loses valuable resources. In our requirement we don’t even need to persist values for the entire user journey so the session state adds more overhead than value.

Query string parameters

The most common way of passing values from one web page to another is by using query string parameters. It’s simple, effective and doesn’t cause any overhead. If they just weren’t that ugly…….. Besides being ugly query string parameters are not very SEO friendly and often you will get negative remarks about them from SEO agencies that do audits.

Right… So basically even the best option here will not always suffice. What’s left? Well we can improvise a bit. Let’s write down our own requirements:

  1. We don’t consume any additional server resources
  2. We don’t need to worry about the users browser settings
  3. We’re totally SEO friendly
  4. We don’t leave any traces in the web pages about the passed through values
  5. And still we can pass values from one web page to another web page

Injecting values into the upcoming request

In order to achieve our requirements we need a transport mechanism for our values. Something I’ve been using quite often is a simple HTML form, populated on the current web page with hidden fields for all my values and injected into the upcoming request.

First let’s write a class which will construct our ‘virtual’ form and populate it with all values.

namespace Exlrt.Sandbox.Utilities
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Web;
 
    public class FormRequestHelper
    {
        public static string GetRequestForm(string formName, string postUrl, Dictionary<string, string> formValues)
        {
            StringBuilder hiddenFields = new StringBuilder();
            StringBuilder result = new StringBuilder();
 
            foreach (KeyValuePair<string, string> pair in formValues)
            {
                hiddenFields.Append($"<input type='hidden' name='{pair.Key}' value='{pair.Value}' />");
            }
 
            result.Append("
<form action=\'{0}\' id='" + formName + "' method='POST'>{1}</form>
 
");
            result.Append("<script type='text/javascript'> document.getElementById('" + formName + "').submit();</script>");
 
            return String.Format(result.ToString(), postUrl, hiddenFields);
        }
 
        public static void SetRequestFormValue(string key, string value)
        {
            HttpContext context = HttpContext.Current;
 
            if (context != null && context.Request.Form.AllKeys.Contains(key))
            {
                MethodInfo WritableMethod;
                MethodInfo ReadOnlyMethod;
 
                BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
                WritableMethod = context.Request.Form.GetType().GetMethod("MakeReadWrite", Flags);
                ReadOnlyMethod = context.Request.Form.GetType().GetMethod("MakeReadOnly", Flags);
 
                FieldInfo FormField = context.Request.GetType().GetField("_form", Flags);
                WritableMethod.Invoke(context.Request.Form, null);
 
                context.Request.Form[key] = value;
 
                FormField.SetValue(context.Request, context.Request.Form);
                ReadOnlyMethod.Invoke(context.Request.Form, null);
            }
        }
    }
}

The created class contains two static methods. The GetRequestForm method accepts three parameters: a name for the form, the target URL for the redirect and a dictionary for our data. As a result it returns the constructed form as a string value. This method is called from the initial page.

The SetRequestFormValue method is called in the target page. It’s purpose is to set the form values of the current request. And because the Form property of the Request object is read-only we need some Reflection magic to write to the collection. That’s one side of the story. The other one, though, is that we don’t want to manually set anything and we don’t have to. The reason why we need this method is that we want to remove the passed values from the request once we’re done with them.

The initial page

public partial class Default : Page
{
    protected void OnButtonClicked(object sender, EventArgs e)
    {
        Dictionary<string, string> data = new Dictionary<string, string>()
        {
            { "SomeUniqueKey", this.txtInput1.Text },
            { "AnotherUniqueKey", this.txtInput2.Text } }
        };
 
        Response.Write(FormRequestHelper.GetRequestForm(Guid.NewGuid().ToString(), "SecondPage.aspx", data));
    }
}

The code above states that our initial web page has two input fields and a button. When the button is clicked, we collect the values from the input fields and store them in a Dictionary<string, string> object. Then we call our FormRequestHelper and pass a generated GUID for the form name, the URL of the target web page and the dictionary object. The result we write into the current HTTP response which causes the actual redirect.

The target page

public partial class SecondPage : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string[] keys = { "SomeUniqueKey", "AnotherUniqueKey" };
 
        foreach(string key in keys)
        {
            string value = Request.Form.AllKeys.Contains(key) ? Request.Form[key] : null;
 
            if(!String.IsNullOrWhiteSpace(value))
            {
                // Do something with 'value'
 
                FormRequestHelper.SetRequestFormValue(key, String.Empty);
            }
        }
    }
}

The code above simulates a situation where we actually know the keys stored in the request. One could also iterate the complete collection but this can lead to unwanted results. There are definitely different views on how to solve that so I’ll drop that part for now.

So for each (known) key we verify that the key exists in the collection and if it does we retrieve the related value. Do something with it and remove the key/value pair from the collection by calling the SetRequestFormValue method of our utility class.

From my point of view a completely valid and clean solution for our problem.

Contact us to discuss your project.
We're ready to work with you.
Let's talk