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

11

50

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?

Oscillator answered 8/12, 2008 at 14:39 Comment(0)
M
52

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);
}
Milda answered 8/12, 2008 at 15:11 Comment(3)
I had to change line 15 and 17 to made this work : var converter = TypeDescriptor.GetConverter(typeof(T)); if (!converter.CanConvertTo(value.GetType()))Federalese
Hi, thats pretty nice however it doesn't work with nullables, e.g int? There may be a bug in type converter? Worth testing if you are interested.Pharyngeal
I am using this code right now in a project, and we have Request.QueryString.GetValue<int?>("someValue") in many places and it works just fine. Is that syntax what you meant?Milda
T
34

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

if (!int.TryParse(Request.QueryString["id"], out id))
{
  // error case
}
Timer answered 8/12, 2008 at 14:42 Comment(0)
V
19

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")
Vitellin answered 29/9, 2010 at 19:52 Comment(0)
N
17

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);
Newmark answered 8/12, 2008 at 14:50 Comment(2)
What a great idea for a helper method to put in a class library!Button
In fact there's an error inside the method. Instead of "Request.QueryString" it should be "HttpContext.Current.Request.QueryString".Newmark
R
10

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.

Rheostat answered 8/12, 2008 at 14:43 Comment(0)
H
9

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;
}
Haul answered 8/12, 2008 at 14:58 Comment(2)
We use this technique, but have non-nullable versions, e.g. "int ToInt(int defaultValue = 0)" and "int? AsInt()"Broughton
@Jerph's suggestion is pretty good, but you can also use the above as Request["id"].ToInt().GetValueOrDefault(0) to get the same effect.Brisbane
S
4
if(!string.IsNullOrEmpty(Request.QueryString["id"]))
{
//querystring contains id
}
Speechmaking answered 8/12, 2008 at 14:44 Comment(3)
what if user messes up like page.aspx?id=123 to page.aspx?1234Retake
@DamienJoe Then the user should get a 404.Grovergroves
@DamienJoe: Why would the user be typing the query string parameters in manually, shouldn't they only be clicking the (well-formed) links that you give them?Brisbane
P
1

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
Pontoon answered 8/12, 2008 at 14:49 Comment(0)
K
1

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;
}
Kristof answered 8/12, 2008 at 14:54 Comment(1)
This is pretty much the same as what I use.. :)Avila
A
1

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.

Avila answered 8/12, 2008 at 14:57 Comment(0)
S
1

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;
Subgroup answered 19/10, 2009 at 19:0 Comment(2)
'Int32Converter' is unable to convert 'System.String' to 'System.Nullable`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.Sepal
Thanks @Sepal for the comment, I changed the code a little to handle Nullables.Subgroup

© 2022 - 2024 — McMap. All rights reserved.