I have an ASP.NET MVC4 website implementing a REST API, which I'm consuming from a client application. My ApiController methods take and return complex objects, as XML.
I recently discovered RestSharp, and have begun moving my client project over to that. However, I'm having real problems with it. It seems to almost work - it's so close that I can almost taste success - but I just can't get it to work 100%.
The objects I'm passing across the wire look something like this:
// The object I'm passing across the wire
public class Example
{
bool IsActive { get; set; }
string Name { get; set; }
}
My ApiController method looks like this:
// My ApiController method
public HttpResponseMessage PostExample(Example example)
{
if (ModelState.IsValid)
{
db.Examples.Add(example);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, example);
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
The problem occurs when I try to POST an object to my website, like this:
var example = new Example () { IsActive = true, Name = "foo" };
var request = new RestSharp.RestRequest("/api/example", RestSharp.Method.POST);
request.AddBody(example, XmlNamespace);
var client = new RestClient();
client.BaseUrl = "foo.com";
var response = client.Execute<Example>(request);
The code above does hit the PostExample method in my ApiController, and it has an Example object as the parameter. However the values of the properties of the Example object are not the same as I passed to the Execute method! In one case, the IsActive member was false instead of true, although I also saw a case where the Name member was null where it should have had a value.
I did some investigation using Fiddler, and it seems that the correct values are being created in the XML that RestSharp generates. However, the XML is not quite in the same format that the web server emits when doing a GET. The differences are subtle, but seem to make the difference between it working and not working. The framework at the web server end seems to be sensitive to these formatting differences, and is mis-interpreting the XML as a result.
Here's the XML I get from RestSharp:
<Example xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
<Name>foo</Name>
<IsActive>true</IsActive>
</Example>
This is what I get when doing a GET on the webserver (or when serializing using the DataContractSerializer, which is what I was previously doing):
<Example xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
<IsActive>true</IsActive>
<Name>foo</Name>
</TagDto>
The RestSharp version has the following differences from the DataContractSerializer's version:
- Fields are in a different order
- RestSharp doesn't include the extra namespace XMLSchema-instance namespace
- DataContractSerializer doesn't include any spaces or line-breaks (I added those above for readability)
I'm surprised that any of those make much of a difference, but clearly they do. Note also that until I added an explicit namespace in the AddBody call, this was missing in the generated XML (obviously), and the Example object passed into my ApiController was null
.
Anyway, I noticed that RestSharp allows you to override the serializer, and provides a way to use the .NET XML serializer. I tried using that (to no avail).
This is what I added before the call to AddBody:
request.XmlSerializer = new RestSharp.Serializers.DotNetXmlSerializer(XmlNamespace);
..and this is what I get out:
<?xml version="1.0" encoding="utf-8"?>
<Example>
<Name>foo</Name>
<IsActive>true</IsActive>
</Example>
This is clearly no good, not least because it starts with an XML declaration, which I imagine would cause problems. There's no way to turn that off, because the RestSharp derived class provides no way to do so. Also, there's no namespace - and I can't get one to appear in the output no matter how I try to set the namespace in RestSharp (in the constructor for the DotNetXmlSerializer, by setting the Namespace member, or by passing in a namespace to AddBody). To my eyes, this class is nothing more than a trap.
It looks like my only option is to create my own serializer class and use the DataContractSerializer internally. Is that right, or am I missing something?
(BTW, I can set the RequestFormat of the request to JSON and it just works - but I'd still like to know how to get this working with XML).