Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ModelBinding: POST data (possibly from Ruby) in MVC4/C#

We're integrating with chargify http://www.chargify.com and I need to handle webhooks from Chargify in our MVC4/C# server. Chargify sends POST data in the (ruby) way - sub objects are delimited in square brackets, like so:

POST /1ffaj2f1 HTTP/1.1
X-Chargify-Webhook-Signature: 526ccfd9677668674eaa6ba5d447e93a
X-Chargify-Webhook-Id: 11238622
User-Agent: Ruby
Host: requestb.in
Content-Type: application/x-www-form-urlencoded
Content-Length: 5159
Connection: close
Accept-Encoding: gzip, deflate
Accept: */*; q=0.5, application/xml

id=11238622&event=payment_success&payload[subscription][activated_at]=2013-05-22%2001%3A25%3A20%20-0400&payload[subscription][balance_in_cents]=0&...

The issue is that on the .NET side, the default model binder does not like the square brackets[ ] and instead expects subobjects to be delimited in a dotted notation like a.b.c.d=value

What are my options to make the model binder work? I can think of the following:

  1. Hook into the request with a delegating handler, read the req body and replace [] with dot (.). However, this feels dicey at the least
  2. Write a custom model binder that will work with [] - hopefully being able to delegate to the default handler somehow. Dunno how to go about this - but would love to hear some thoughts.
  3. Just use a formcollection param in the Action. Process the form collection keys to form a recursive dictionary based on the keys. This also means that I have to deal wiht strings throughout (for dates, ids etc etc)

What's the best way and how do I go about it.

like image 937
Raghu Avatar asked Oct 22 '22 07:10

Raghu


1 Answers

So I think I've finally solved this one using Request.Filter property (msdn)

Also to note - There's no delegating handler in MVC - that's WebApi stuff (some of these things are made just to lead you astray!)

Basically, wrote a decorator over Stream that reads the input stream, replaces the square brackets with dots and then stores it in another stream. There's a check in Application_BeginRequest that sees if the input request is for the 'correct' controller and if so, creates a decorator instance and sets it to Request.Filter.

Code here. feel free to fork :)

[Update - 2013.08.19]: As noted by @benpage below in comments, we also need the original unmodified stream for SHA256 validation. I ended up exposing the unmodified stream contents from the decorator and downcasting the Request.InputStream property in the controller to get the unmodified content for signature validation.

like image 102
Raghu Avatar answered Oct 24 '22 02:10

Raghu