I'm experiencing some really strange behavior when handling an HTTP PUT in an OpenRasta handler. The handler method signature looks like this:
public CustomerResource Put(CustomerForm customerForm)
And here is the relevant ResourceSpace
configuration:
ResourceSpace.Has.ResourcesOfType<CustomerListResource>()
.AtUri("/customers")
.HandledBy<CustomerHandler>()
.RenderedByAspx("~/Views/Customer/CustomerListView.aspx")
.And.AsJsonDataContract().ForMediaType("application/json;q=0.3")
.And.AsXmlSerializer().ForMediaType("application/xml;q=0.2");
ResourceSpace.Has.ResourcesOfType<CustomerResource>()
.AtUri("/customers/{id}")
.HandledBy<CustomerHandler>()
.RenderedByAspx("~/Views/Customer/CustomerEditView.aspx")
.And.AsJsonDataContract().ForMediaType("application/json;q=0.3")
.And.AsXmlSerializer().ForMediaType("application/xml;q=0.2");
// To support POST and PUT to /customers
ResourceSpace.Has.ResourcesOfType<CustomerForm>()
.WithoutUri
.RenderedByAspx("~/Views/Customer/CustomerEditView.aspx")
.And.AsJsonDataContract().ForMediaType("application/json;q=0.3")
.And.AsXmlSerializer().ForMediaType("application/xml;q=0.2");
CustomerForm
looks like this:
[XmlRoot("customer", Namespace = ClientSettings.Namespace)]
public class CustomerForm : FormBase, ICustomer
{
[XmlElement("contact-info")]
public ContactInfo ContactInfo { get; set; }
[XmlAttribute("id")]
public int Id { get; set; }
}
ContactInfo
looks like this:
[XmlRoot("contact-info", Namespace = ClientSettings.Namespace)]
public class ContactInfo
{
[XmlElement("email")]
public string Email{ get; set; }
[XmlElement("first-name")]
public string FirstName{ get; set; }
[XmlElement("last-name")]
public string LastName{ get; set; }
[XmlElement("mobile-phone-number")]
public string MobilePhoneNumber { get; set; }
}
My problem is that when CustomerForm
is PUT to the server, OpenRasta is unable to deserialize it properly. What it does is deserialize it with a ContactInfo
set to null
although it is sent successfully from the client. I have even digged in to the IRequest.Entity.Stream
to ensure that the XML I need is indeed there, and it is:
<?xml version="1.0" encoding="utf-8"?>
<customer id="1" xmlns="urn:namespace">
<contact-info>
<email>[email protected]</email>
<first-name>0440a6d5f071478d8571bac1301552bc</first-name>
<last-name>49069fb41eb141c79326dc64fa034573</last-name>
<mobile-phone-number>59980075</mobile-phone-number>
</contact-info>
</customer>
How can IRequest.Entity.Stream
contain the necessary data while the deserialized CustomerForm
object received in the Put(CustomerForm)
method doesn't? I have tests that ensure serialization and deserialization works perfectly and I even have a Post(CustomerForm)
in the exact same handler that also works. It's just when the HTTP method is PUT that CustomerForm
has ContactInfo
set to null
.
I've thoroughly read the debug output from OpenRasta and all it says is:
27-[2011-07-15 11:09:15Z] Information(0) Operation CustomerHandler::Put(CustomerForm customerForm) was selected with a codec score of 0
27-[2011-07-15 11:09:15Z] Information(0) Loaded codec OpenRasta.Codecs.XmlSerializerCodec
27-[2011-07-15 11:09:15Z] Verbose(0) Switching to full object media type reading.
27-[2011-07-15 11:09:15Z] Stop(1) Exiting PipelineRunner
27-[2011-07-15 11:09:15Z] Start(1) Entering PipelineRunner: Executing contributor OperationInterceptorContributor.WrapOperations
27-[2011-07-15 11:09:16Z] Stop(1) Exiting PipelineRunner
27-[2011-07-15 11:09:16Z] Start(1) Entering PipelineRunner: Executing contributor OperationInvokerContributor.ExecuteOperations
27-[2011-07-15 11:09:16Z] Verbose(0) Ignoring constructor, following dependencies didn't have a registration:OpenRasta.OperationModel.Interceptors.IOperationInterceptor[]
The only weirdness I have spotted is that a MissingMethodException
is thrown as a FirstChanceException
(but never bubbles up) right after this step, but the stack trace of it is so short I have no idea what the problem might be:
2011-07-15T13:09:16.036 AppDomain.FirstChanceException
System.MissingMethodException: Member not found.
at System.DefaultBinder.BindToMethod(BindingFlags bindingAttr, MethodBase[] match, Object[]& args, ParameterModifier[] modifiers, CultureInfo cultureInfo, String[] names, Object& state)
I have no idea why a MissingMethodException
is being thrown and why it doesn't bubble if I don't subscribe to the AppDomain.FirstChanceException
event, but it might be related to why my CustomerForm
isn't deserialized correctly. However, since it does deserialize correctly on HTTP POST, I have my doubts.
Ideas?
The problem seems to be with how the URL is interpreted and mapped to the handler, because if I add a handler method that also takes an int id
, like this:
CustomerResource Put(int id, CustomerForm customerForm)
It works. This is probably due to the following resource registration:
ResourceSpace.Has.ResourcesOfType<CustomerResource>()
.AtUri("/customers/{id}")
Although I have this:
ResourceSpace.Has.ResourcesOfType<CustomerForm>()
.WithoutUri
I've tried to modify the registration of CustomerForm
to include an ID:
ResourceSpace.Has.ResourcesOfType<CustomerForm>()
.AtUri("/customers/{id}")
And also to make the ID optional on both the CustomerResource
and CustomerForm
registrations:
ResourceSpace.Has.ResourcesOfType<CustomerResource>()
.AtUri("/customers/{*id}")
ResourceSpace.Has.ResourcesOfType<CustomerForm>()
.AtUri("/customers/{*id}")
None of this helps, but adding the int id
parameter to the handler method does, so I'm pleased as this makes OpenRasta successfully deserialize my entity.