How do you test your Request.QueryString[] variables?

C#Coding StyleTryparseIsnumericrequest.querystring

C# Problem Overview


I frequently make use of Request.QueryString[] variables.

In my Page_load I often do things like:

       int id = -1;
        
        if (Request.QueryString["id"] != null) {
            try
            {
                id = int.Parse(Request.QueryString["id"]);
            }
            catch
            {
                // deal with it
            }
        }

        DoSomethingSpectacularNow(id);

It all seems a bit clunky and rubbish. How do you deal with your Request.QueryString[]s?

C# Solutions


Solution 1 - C#

Below is an extension method that will allow you to write code like this:

int id = request.QueryString.GetValue<int>("id");
DateTime date = request.QueryString.GetValue<DateTime>("date");

It makes use of TypeDescriptor to perform the conversion. Based on your needs, you could add an overload which takes a default value instead of throwing an exception:

public static T GetValue<T>(this NameValueCollection collection, string key)
{
    if(collection == null)
    {
        throw new ArgumentNullException("collection");
    }

    var value = collection[key];

    if(value == null)
    {
        throw new ArgumentOutOfRangeException("key");
    }

    var converter = TypeDescriptor.GetConverter(typeof(T));

    if(!converter.CanConvertFrom(typeof(string)))
    {
        throw new ArgumentException(String.Format("Cannot convert '{0}' to {1}", value, typeof(T)));
    }

    return (T) converter.ConvertFrom(value);
}

Solution 2 - C#

Use int.TryParse instead to get rid of the try-catch block:

if (!int.TryParse(Request.QueryString["id"], out id))
{
  // error case
}

Solution 3 - C#

Try this dude...

List<string> keys = new List<string>(Request.QueryString.AllKeys);

Then you will be able to search the guy for a string real easy via...

keys.Contains("someKey")

Solution 4 - C#

I'm using a little helper method:

public static int QueryString(string paramName, int defaultValue)
{
	int value;
	if (!int.TryParse(Request.QueryString[paramName], out value))
		return defaultValue;
	return value;
}

This method allows me to read values from the query string in the following way:

int id = QueryString("id", 0);

Solution 5 - C#

Well for one thing use int.TryParse instead...

int id;
if (!int.TryParse(Request.QueryString["id"], out id))
{
    id = -1;
}

That assumes that "not present" should have the same result as "not an integer" of course.

EDIT: In other cases, when you're going to use request parameters as strings anyway, I think it's definitely a good idea to validate that they're present.

Solution 6 - C#

You can use the extension methods below as well and do like this

int? id = Request["id"].ToInt();
if(id.HasValue)
{

}

// Extension methods

public static int? ToInt(this string input) 
{
    int val;
    if (int.TryParse(input, out val))
        return val;
    return null;
}

public static DateTime? ToDate(this string input)
{
    DateTime val;
    if (DateTime.TryParse(input, out val))
        return val;
    return null;
}

public static decimal? ToDecimal(this string input)
{
    decimal val;
    if (decimal.TryParse(input, out val))
        return val;
    return null;
}

Solution 7 - C#

if(!string.IsNullOrEmpty(Request.QueryString["id"]))
{
//querystring contains id
}

Solution 8 - C#

I do have functions for each (actually it's one small class, with lots of statics):

  • GetIntegerFromQuerystring(val)
  • GetIntegerFromPost(val)
  • ....

It returns -1 if fails (which is almost always OK for me, I have some other functions for negative numbers as well).

Dim X as Integer = GetIntegerFromQuerystring("id")
If x = -1 Then Exit Sub

Solution 9 - C#

Eeee this is a karma risk...

I have a DRY unit-testable abstraction because, well, because there were too many querystring variables to keep on in a legacy conversion.

The code below is from a utility class whose constructor requires a NameValueCollection input (this.source) and the string array "keys" is because the legacy app was rather organic and had developed the possibility for several different strings to be a potential input key. However I kind of like the extensibility. This method inspects the collection for the key and returns it in the datatype required.

private T GetValue<T>(string[] keys)
{
	return GetValue<T>(keys, default(T));
}

private T GetValue<T>(string[] keys, T vDefault)
{
	T x = vDefault;
	
	string v = null;

	for (int i = 0; i < keys.Length && String.IsNullOrEmpty(v); i++)
	{
		v = this.source[keys[i]];
	}

	if (!String.IsNullOrEmpty(v))
	{
		try
		{
			x = (typeof(T).IsSubclassOf(typeof(Enum))) ? (T)Enum.Parse(typeof(T), v) : (T)Convert.ChangeType(v, typeof(T));
		}
		catch(Exception e)
		{
			//do whatever you want here
		}
	}

	return x;
}

Solution 10 - C#

I actually have a utility class that uses Generics to "wrap" session, which does all of the "grunt work" for me, I also have something almost identical for working with QueryString values.

This helps remove the code dupe for the (often numerous) checks..

For example:

public class QueryString
{
    static NameValueCollection QS
    {
        get
        {
            if (HttpContext.Current == null)
                throw new ApplicationException("No HttpContext!");

            return HttpContext.Current.Request.QueryString;
        }
    }

    public static int Int(string key)
    {
        int i; 
        if (!int.TryParse(QS[key], out i))
            i = -1; // Obviously Change as you see fit.
        return i;
    }

    // ... Other types omitted.
}

// And to Use..
void Test()
{
    int i = QueryString.Int("test");
}

NOTE:

This obviously makes use of statics, which some people don't like because of the way it can impact test code.. You can easily refactor into something that works based on instances and any interfaces you require.. I just think the statics example is the lightest.

Hope this helps/gives food for thought.

Solution 11 - C#

I modified Bryan Watts' answer so that if the param your asking does not exist and you have specified a nullable type it will return null :

public static T GetValue<T>(this NameValueCollection collection, string key)
    {
        if (collection == null)
        {
            return default(T);
        }

        var value = collection[key];

        if (value == null)
        {
           return default(T);
        }

        var type = typeof(T);

        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            type = Nullable.GetUnderlyingType(type);
        }

        var converter = TypeDescriptor.GetConverter(type);

        if (!converter.CanConvertTo(value.GetType()))
        {
            return default(T);
        }

        return (T)converter.ConvertTo(value, type);
    }

You can now do this :

Request.QueryString.GetValue<int?>(paramName) ?? 10;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIan GView Question on Stackoverflow
Solution 1 - C#Bryan WattsView Answer on Stackoverflow
Solution 2 - C#VVSView Answer on Stackoverflow
Solution 3 - C#AutomationNationView Answer on Stackoverflow
Solution 4 - C#M4NView Answer on Stackoverflow
Solution 5 - C#Jon SkeetView Answer on Stackoverflow
Solution 6 - C#terjetylView Answer on Stackoverflow
Solution 7 - C#Colin DekkerView Answer on Stackoverflow
Solution 8 - C#dr. evilView Answer on Stackoverflow
Solution 9 - C#annakataView Answer on Stackoverflow
Solution 10 - C#Rob CooperView Answer on Stackoverflow
Solution 11 - C#W3MaxView Answer on Stackoverflow