Search code examples
wcfwcf-clientwcf-web-api

How to POST a POCO with WCF WebApi's HttpClient


I'm using the WCF WebApi stack (Preview 4) via NuGet (pkg version 0.3.0) and cannot seem to figure out how to "POST a POCO" using HttpClient.

Given the following:

Public Class MyInfo
    Public Property MyDate As DateTime
    Public Property MyId As Guid
End Class

...
Dim value = New MyInfo With {.MyDate = Today, .MyId = Guid.NewGuid()}

Using client as New HttpClient(baseUri)
    Using response = client.Post(requestUri, New ObjectContent(Of MyInfo)(value))
        ' Do stuff
    End Using
End Using
...

When the Post method is called, I get the following exception:

The 'XmlSerializer' serializer cannot serialize the type 'MyInfo'.

at Microsoft.ApplicationServer.Http.XmlMediaTypeFormatter.GetSerializerForType(Type type)
at Microsoft.ApplicationServer.Http.XmlMediaTypeFormatter.OnWriteToStream(Type type, Object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext context)
at Microsoft.ApplicationServer.Http.MediaTypeFormatter.WriteToStream(Type type, Object instance, Stream stream, HttpContentHeaders contentHeaders, TransportContext context)
at Microsoft.ApplicationServer.Http.ObjectContent.WriteToStreamInternal(Stream stream, TransportContext context)
at Microsoft.ApplicationServer.Http.ObjectContent.SerializeToStream(Stream stream, TransportContext context)
at System.Net.Http.HttpContent.LoadIntoBuffer(Int32 maxBufferSize)
at System.Net.Http.HttpClientChannel.PrepareWebRequestForContentUpload(HttpWebRequest webRequest, HttpRequestMessage request)
at System.Net.Http.HttpClientChannel.CreateAndPrepareWebRequest(HttpRequestMessage request)
at System.Net.Http.HttpClientChannel.Send(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.Send(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.Send(HttpRequestMessage request)
at System.Net.Http.HttpClient.Post(Uri requestUri, HttpContent content)
at System.Net.Http.HttpClient.Post(String requestUri, HttpContent content)
...

This is using the NuGet 0.3.0 package.

I've tried adding <Serializable()> and even <DataContract()> to MyInfo, but that didn't help. Am I just doing something wrong?

I found this post here on StackOverflow where it looks like someone is doing something similar to what I did above. I've even duplicated his work (guessing that his Machine object was a simple POCO like my MyInfo is) and ran into the same "cannot serialize" exception.


Solution

  • I ended up finding the problem myself. I was missing the media type. I changed the Post method to look like:

    Using client as New HttpClient(baseUri)
        Using response = client.Post(requestUri, New ObjectContent(Of MyInfo)(value, "text/xml"))
            ' Do stuff
        End Using
    End Using
    

    and it started working. I guess that makes sense. For some reason, I thought that there was some sort of default media type precedence built-in so that you didn't have to go out of your way to specify the media type each time. Maybe there is, but I'm still doing something wrong?

    Update:

    My original problem actually had nothing to do with the media type, even though that actually did fix/workaround the problem. The problem seems to happen when MyInfo is nested inside of a Module like this.

    Module Module1
    
        Sub Main()
            Dim value = New MyInfo With {.MyDate = Today, .MyId = Guid.NewGuid()}
            Dim payload As HttpContent = New ObjectContent(Of MyInfo)(value)
    
            Using client as New HttpClient(baseUri)
                Using response = client.Post(requestUri, New ObjectContent(Of MyInfo)(value))
                    ' Do stuff
                End Using
            End Using
        End Sub
    
        Public Class MyInfo
            Public Property MyDate As DateTime
            Public Property MyId As Guid
        End Class
    
    End Module
    

    Once MyInfo moved outside of the Module, the error no longer happens. It's also worth noting that while the media type is no longer required to prevent the exception, the lack of it results in no Content-Type header, which may not fly server-side as the server may not know how to deserialize the message body. In my case, I needed to leave the media type in so that the request would include a Content-Type: application/xml header for the server's sake.