Parsing The Payload

project_referencesSo this week we got to start working a brand spanking new MVC project. So far we’re leveraging Castle Windsor, NHibernate, Fluent Nhibernate, and kind of running Linq to NHibernate. It’s amazing how quickly you can get a project up and running in such a short amount of time. (BTW, Fluent NHibernate rocks!) When you’re building off the trunk of these projects, it’s almost like the contributors to all these great projects are extended members of the team. Thank you all!

Moving on… One of the things that are cool, but also slightly annoying, is how the MVC framework parses out items from the http payload to populate any input arguments on controller actions.

It’s great how it just works, but it’s a little annoying if it’s under test and you have to add more fields, or remove fields from a form, then you have to go update the signature of the action then go update the test…. yada yada The changes just ripple down…

So one thing we tried out this week was to create a payload parser. What this guy does is take a DTO parse out the values for each of the properties on the DTO from the current requests payload and fill it. This makes it easy to package up the form parameters in a nicely packaged DTO and fire it off down to a service layer to do some work.

So instead of declaring an action method on a controller that looks like this, where the signature would have to change based on what fields are submitted on a form:

viewresult register_new_account(string user_name, string first_name,string last_name)

we can write this…

public viewresult register_new_account() {
    var accountsubmissiondto = parser.mapfrompayloadto<accountsubmissiondto>();
    var validationresult = task.validate(accountsubmissiondto);
    if (validationresult.isvalid) {
        task.submit(accountsubmissiondto);
        return view("Success", accountsubmissiondto);
    }

    return view("Index", validationresult.brokenrules);
}

this better allows us to adhere to the ocp. if we need to include additional fields on the form, we can add them to the form as long as the control name is the same as the name of the property on the dto that it will be bound to. the implementation of the payload parser is quite primitive for now, but at the moment it’s all that we needed.

first up the specs… simple enough, for now!

public class when_parsing_the_values_from_the_current_request_to_populate_a_dto : context_spec<ipayloadparser> {
    [test]
    public void should_return_a_fully_populated_dto() {
        result.name.should_be_equal_to("adam");
        result.age.should_be_equal_to(15);
        result.birthdate.should_be_equal_to(new datetime(1982, 11, 25));
        result.id.should_be_equal_to(1);
    }

    protected override ipayloadparser undertheseconditions() {
        var current_request = dependency<iwebrequest>();
        var payload = new namevaluecollection();

        payload["Name"] = "adam";
        payload["Age"] = "15";
        payload["Birthdate"] = new datetime(1982, 11, 25).tostring();
        payload["Id"] = "1";

        current_request.setup_result_for(r => r.payload).return(payload);

        return new payloadparser(current_request);
    }

    protected override void becauseof() {
        result = sut.mapfrompayloadto<somedto>();
    }

    private somedto result;
}

public class when_parsing_values_from_the_request_that_is_missing_values_for_a_properties_on_the_dto :
    context_spec<ipayloadparser> {
    private accountsubmissiondto result;

    [test]
    public void it_should_supress_any_errors() {
        result.lastname.should_be_null();
        result.emailaddress.should_be_null();
    }

    protected override ipayloadparser undertheseconditions() {
        var current_request = dependency<iwebrequest>();

        var payload = new namevaluecollection();

        payload["FirstName"] = "Joel";
        current_request.setup_result_for(x => x.payload).return(payload);

        return new payloadparser(current_request);
    }

    protected override void becauseof() {
        result = sut.mapfrompayloadto<accountsubmissiondto>();
    }
    }
public class somedto {
    public long id { get; set; }
    public string name { get; set; }
    public int age { get; set; }
    public datetime birthdate { get; set; }
}

the current implementation:

public interface IPayloadParser {
    TypeToProduce MapFromPayloadTo<TypeToProduce>() where TypeToProduce : new();
}

public class PayloadParser : IPayloadParser {
    private readonly IWebRequest current_request;

    public PayloadParser(IWebRequest current_request) {
        this.current_request = current_request;
    }

    public TypeToProduce MapFromPayloadTo<TypeToProduce>() where TypeToProduce : new() {
        var dto = new TypeToProduce();
        foreach (var propertyInfo in typeof (TypeToProduce).GetProperties()) {
            var value = Convert.ChangeType(current_request.Payload[propertyInfo.Name], propertyInfo.PropertyType);
            propertyInfo.SetValue(dto, value, null);
        }

        return dto;
    }
}

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Mo Khan

mO, is just a kid who's excited about writing software. He's a student of his profession, and just wants to share his thoughts on software development.
This entry was posted in ASP.NET MVC, TDD. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

7 Responses to Parsing The Payload

  1. Rob says:

    This is a lot like what we are doing in our current MVC project (we are suing all the same pieces as you), with a couple of notable differences. Insead of DTOs we are using command objects from our domain layer and using the Castle integration to accomplish the databinding. So, you get something like this:

    public ActionResult Create([CastleBind]CreateNewAccount command)
    {
    var result = Execute(command);

    if(result.HasErrors)
    {
    ViewData["Title"] = “New Account”;
    ViewData["Errors"] = result.Errors;
    ViewData["command"] = command;
    return View(“New”);
    }

    ViewData["Title"] = “Account Created”;
    ViewData["Message"] = “Your account was created successfully.”;
    return View(“AccountCreated”);
    }

  2. joey says:

    Why don’t people just use monorail? ;)

    I guess a better question is why are you using MVC what does MVC have over MonoRail?

  3. Colin Jack says:

    Very cool, and can definitely see how it helps. I’ve got some questions/comments though.

    What are the the tasks you show for, are they some sort of service or something different entirely?

    Off topic but I like the way you structure your BDD specs backwards, can see the sense in that, mind you can see the sense in opposite too :)

    Finally, the way you’re inserting code snippets doesn’t seem to work when you view it via a feed (I just tried from Google Reader) and it also seems to have lost some the casing when you view it in the browser e.g. “validationresult.isvalid”.

    @Rob
    Got a blog entry about the entirety of the approach you use, not familiar with the castle binding approach.

  4. mO says:

    @Mr. Rob
    That looks super clean… I will definitely have to learn more about Castle bindings. Thanks for the tip!

    @joey
    I keep tellin’ myself I’m going to invest some time in spiking MonoRail… maybe you could point me to some good resources for getting started quickly?

    @Mr. Colin
    the tasks are indeed service layer components.

    I’ve found that code snippets don’t appear well in my feed as well. Can you suggest something for copying and pasting C# source code into html documents?

    Our team just found that writing the specs “backwards” was just more readable. We try to keep the technical language as much as possible and try to write them as readable sentences.

  5. Unless I’m missing something, and I realize it’s slightly more verbose. But, you should just be able to use the built in asp.net mvc binder:

    public viewresult register_new_account() {
    var accountsubmissiondto = new accountsubmissiondto();
    System.Web.Mvc.BindingHelperExtensions.UpdateFrom(accountsubmissiondto, Request.Form);
    var validationresult = task.validate(accountsubmissiondto);
    if (validationresult.isvalid) {
    task.submit(accountsubmissiondto);
    return view(“Success”, accountsubmissiondto);
    }

    return view(“Index”, validationresult.brokenrules);
    }

  6. Simone Busoli says:

    You can give a look at the DeserializeAttribute in Mvccontrib as well.

  7. Colin Jack says:

    @mO
    Yeah the whole backwards thing is cool, need to try it to see if it works for me in practice but good idea for sure.

    I use Windows Live Writer which has a plugin that lets you insert snippets from Visual Studio which is cool (Jimmy Bogard suggested it in the comments and provides a link):

    http://colinjack.blogspot.com/2008/02/posting-c-code-snippets.html