Reputation: 8820
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?
Upvotes: 0
Views: 539
Reputation: 8820
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.
Upvotes: 1
Reputation: 3136
Sounds wacky but have you tried moving the id to the top of your CustomerForm object?
[XmlRoot("customer", Namespace = ClientSettings.Namespace)]
public class CustomerForm : FormBase, ICustomer
{
[XmlAttribute("id")]
public int Id { get; set; }
[XmlElement("contact-info")]
public ContactInfo ContactInfo { get; set; }
}
Openrasta uses the (awful) .net data contract xml serializer which is sensitive to element positions. We actually wrote our own serialiser which simply reverts to the traditional 'dot net' xml serializer.
Upvotes: 1