I have inherited a project from my predecessor which uses OpenRasta to host a webservice for my OpenSource colleagues to access for their applications. This is my first foray into OpenRasta I've added a lot of additional features all of which is working via manual browser requests, although not 100% reliably but that's perhaps another question later. So I have embarked on creating a set of Unit Tests to test the functionality, which I should be doing anyway. I have successfully created a unit test or two for each GET request all of which are passing, but I am stuck on the test for the single POST I have in the project.
I'm getting a HTTP 415 Error '8-[2012-12-07 11:23:19Z] Information(0) Executing OperationResult OperationResult: type=RequestM ediaTypeUnsupported, statusCode=415.' from the output window. I've taken inspiration from a post by Nate Taylor http://taylonr.com/integration-testing-openrasta and have asked him the same question, which he has kindly replied to. I'm still trying to decipher his answer, and perhaps someone might be able to expand and fill in the gaps in my understanding? Here is the code which I have been trying:
[Test]
public void AuthenticateUserJSONPOSTTest()
{
object content = new AuthenticationStructure { Username = "matthew.radford", Password = "obviously not going to tell you that bit and will replace with a domain test user when the time comes", AppId = 4 };
OpenRastaJSONTestMehods.POST<AuthenticationResult, AuthenticationStructure>("http://localhost/user", content);
}
[Test]
public static void POST<T, U>(string uri, object content)
{
const string LocalHost = "http://localhost/";
if (uri.Contains(LocalHost))
POST<T, U>(new Uri(uri), content);
else
throw new UriFormatException(string.Format("The uri doesn't contain {0}", LocalHost));
}
[Test, WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public static void POST<T,U>(Uri serviceuri, object content)
{
using (var host = new InMemoryHost(new Configuration()))
{
var request = new InMemoryRequest()
{
Uri = serviceuri,
HttpMethod = "POST"
};
request.Entity.ContentType = MediaType.Json;
request.Entity.Headers["Accept"] = "application/json";
var serializer = new DataContractJsonSerializer(typeof(T), new [] { typeof(AuthenticationStructure) });
serializer.WriteObject(request.Entity.Stream, content);
request.Entity.Stream.Seek(0, SeekOrigin.Begin);
request.Entity.ContentLength = request.Entity.Stream.Length;
//Just a read test, not necessary for the output
byte[] readbyte = new byte[(int)request.Entity.ContentLength];
request.Entity.Stream.Read(readbyte, 0, (int)request.Entity.ContentLength);
request.Entity.Stream.Seek(0, SeekOrigin.Begin);
U readObject = (U)serializer.ReadObject(request.Entity.Stream);
request.Entity.Stream.Seek(0, SeekOrigin.Begin);
NUnit.Framework.Assert.AreEqual(content, readObject);
var response = new InMemoryResponse();
response.Entity.ContentType = MediaType.Json;
response.Entity.Headers["Accept"] = "application/json";
response = (InMemoryResponse)host.ProcessRequest(request);
int statusCode = response.StatusCode;
//this is where the test fails because the above response isn't correct and gives the 415 statusCode
NUnit.Framework.Assert.AreEqual(201, statusCode, string.Format("Http StatusCode Error: {0}", statusCode));
object returnedObject;
if (response.Entity.ContentLength > 0)
{
response.Entity.Stream.Seek(0, SeekOrigin.Begin);
//Just a read test, not necessary for the output
readbyte = new byte[(int)response.Entity.ContentLength];
response.Entity.Stream.Read(readbyte, 0, (int)response.Entity.ContentLength);
response.Entity.Stream.Seek(0, SeekOrigin.Begin);
returnedObject = serializer.ReadObject(response.Entity.Stream);
//return returnedObject;
}
}
}
Thanks in advance.
I've tried so many different things this morning to try and get this working. The first good step forward I made by trying to read the JSON stream as a string to actually see what the object was being serialized as.
To do that I found How to convert an Stream into a byte[] in C#? this set me off in the right direction to read the stream out to a string. I therefore came up with this line to write it to the output window:
This was the result: {"__type":"AuthenticationStructure","username":"matthew.radford","appid":4,"password":"###########"}
I realised there were two problems in this output. Firstly and simply, the password should be before the appid, which was easily fixed in the AuthenticationStructure class, where I had made a mistake. DataMember Order needed to equal 3 for AppId
Secondly though, the default serialization includes a '__type' member at the beginning of the notation. This obviously then doesn't match my parameters on the POST method of my Handler:
At this point I looked at trying to remove the type notation from the JSON string. I found a good sites Deserialize array values to .NET properties using DataContractJsonSerializer which showed me both how to write the constructor to include alwaysEmitTypeInformation which they had set to false, but I wanted to Emit the type information, so changed it to true. And it also showed me how to create an Surrogate based on IDataContractSurrogate, which I called AuthenticationTypeSurrogate.
The serialization worked for this but now the Deserialization, which I never bothered to debug and get right because it still wasn't working because instead of creating a JSON it was serializing a object array, which you would expected because that was what the method GetObjectoSerialize method is returning. So I hit another brick wall unless I could work out how to change this into JSON.
So finally I thought well I'll just add the __type parameter into an overloaded POST method and pass on the other parameters to the original POST method.
This was so nearly right but it still didn't work, so finally I created another overloaded method and passed the whole type:
And finally this worked. I'm not perfectly happy with it and would have liked to link straight into my existing method, but it works.
Thanks to everyone who looked and especially Nate, thanks for your original responses, and hopefully this will help people in the future.