0

I'm maintaining a legacy WebForms application and one of the pages just serves GET requests and works with many query string parameters. This work is done in the code-behind and does a lot of this type of check and casting.

protected override void OnLoad(EventArgs e)
{
    string error = string.Empty;

    string stringParam = Request.Params["stringParam"];
    if (!String.IsNullOrEmpty(stringParam))
    {
        error = "No parameter";
        goto LoadError;
    }

    Guid? someId = null;
    try
    {
        someId = new Guid(Request.Params["guidParam"]);
    }
    catch (Exception){}
    if (!someId.HasValue)
    {
        error = "No valid id";
        goto LoadError;
    }

    // parameter checks continue on

LoadError:
    log.ErrorFormat("Error loading page: {0}", error);
    // display error page
}

I'd like to create a testable class that encapsulates this parsing and validation and moves it out of the code-behind. Can anyone recommend some approaches to this and/or examples?

2
  • 6
    goto? Don't see that every day. Commented Dec 28, 2012 at 6:30
  • Yes, exactly, that's one of the refactoring targets as well. Commented Dec 28, 2012 at 6:33

2 Answers 2

2

As a first big step, I'd probably create some form of mapper/translator object, like this:

class SpecificPageRequestMapper 
{
    public SpecificPageRequest Map(NameValueCollection parameters)
    {
        var request = new SpecificPageRequest();
        string stringParam = parameters["stringParam"];
        if (String.IsNullOrEmpty(stringParam))
        {
            throw new SpecificPageRequestMappingException("No parameter");
        }

        request.StringParam = stringParam;

        // more parameters

        ...

        return request;
    }
}

class SpecificPageRequest
{
    public string StringParam { get; set; }
    // more parameters...
}

Then your OnLoad could look like this:

protected override void OnLoad(EventArgs e)
{  
    try
    {
        var requestObject = requestMapper.Map(Request.Params);
        stringParam = requestObject.StringParam;
        // so on, so forth. Unpack them to the class variables first.
        // Eventually, just use the request object everywhere, maybe.
    }
    catch(SpecificPageRequestMappingException ex)
    {
        log.ErrorFormat("Error loading page: {0}", ex.Message);
        // display error page
    }
}

I've omitted the code for the specific exception I created, and assumed you instantiate a mapper somewhere in the page behind.

Testing this new object should be trivial; you set the parameter on the collection passed into Map, then assert that the correct parameter on the request object has the value you expect. You can even test the log messages by checking that it throws exceptions in the right cases.

Sign up to request clarification or add additional context in comments.

Comments

1

Assuming that you may have many such pages using such parameter parsing, first create a simple static class having extension methods on NamedValueCollection. For example,

static class Parser
{    
    public static int? ParseInt(this NamedValueCollection params, string name)
    {
        var textVal = params[name];
        int result = 0;
        if (string.IsNullOrEmpty(textVal) || !int.TryParse(textVal, out result))
        {
            return null;
        }
        return result;
    }

   public static bool TryParseInt(this NamedValueCollection params, string name, out int result)
    {
        result = 0;
        var textVal = params[name];
        if (string.IsNullOrEmpty(textVal))
            return false;
        return int.TryParse(textVal, out result);
    }

    // ...    
}

Use it as follows

int someId = -1;
if (!Request.Params.TryParseInt("SomeId", out someId))
{
   // error
}

Next step would be writing page specific parser class. For example,

public class MyPageParser
{
    public int? SomeId { get; private set; } 
    /// ...

    public IEnumerable<string> Parse(NamedValueCollection params)
    {
        var errors = new List<string>();
        int someId = -1;
        if (!params.TryParseInt("SomeId", out someId))
        {
            errors.Add("Some id not present");
            this.SomeId = null;
        }
        this.SomeId = someId;

        // ...  
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.