Oddleif
Oddleif

Reputation: 791

Serialize and deserialize HttpRequestMessage objects

Anyone have experience on serializing HttpRequestMessage objects? Trying with Json.net and it partially works. That said, JsonConvert.DeserializeObject fails due to constructure issues StringContent: "Unable to find a constructor to use for type System.Net.Http.StringContent".

The use case here is in short that I want to save the web request and issue it later, in case of temporary network issues or service unavailability etc..

Example code that is failing:

var request = new HttpRequestMessage(HttpMethod.POST, "http://www.something.com");
request.Headers.Date = DateTimeOffset.UtcNow;
request.Headers.AcceptLanguage.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("en-US"));
request.Content = new StringContent("Hello World!");
request.Content.Headers.Add("x-some", "thing");                       

var result = JsonConvert.SerializeObject(request, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects,
    TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full
});

var deserializeRequest = JsonConvert.DeserializeObject<HttpRequestMessage>(result, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Objects
}); 

Upvotes: 10

Views: 22390

Answers (3)

Oddleif
Oddleif

Reputation: 791

It's possible to serialize/deserialize the relevant parts of an HttpRequest using the following example code.

var request = new HttpRequestMessage(HttpMethod.Post, "http://www.something.com");
request.Content = new StringContent("Hello World!");

var serializedRequestByteArray = new HttpMessageContent(request).ReadAsByteArrayAsync().Result;

var tmpRequest = new HttpRequestMessage();
tmpRequest.Content = new ByteArrayContent(serializedRequestByteArray);
tmpRequest.Content.Headers.Add("Content-Type", "application/http;msgtype=request");

var deserializedRequest = tmpRequest.Content.ReadAsHttpRequestMessageAsync().Result;  

Upvotes: 4

cgotberg
cgotberg

Reputation: 2085

The problem is that StringContent doesn't have a default constructor. So when Json.Net tries to go create a new StringContent it can't. I took a look writing a custom JsonConverter to handle this but that won't work either because it doesn't appear that you can get at the value you assigned to StringContent. I.e.

public class StringContentConverter :JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(HttpContent).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jObject = JObject.Load(reader); //There's nothing in here that says "Hello World!".  The string content hides it.
    }
}

Essentially you'll need to store the input for you HttpRequest message in your own custom class.

Upvotes: 1

George Polevoy
George Polevoy

Reputation: 7671

I would suggest serializing only those objects for which you maintain implementation in your own codebase.

For HttpRequestMessage one of the reasons not to try serializing it is the fact it implements IDisposable, that means it can hold references to unmanaged resources. You can hardly predict it's behaviour with serializers.

In this case i'd better go with a builder pattern:

var builder = new MyRequestBuilder()
    .SetUrl("http://www.something.com")
    .SetMethod("POST")
    .SetStringContent("My content")
    .DefaultRequestSettings() // One benefit you can encapsulate some logic in builder.

// ...

using(HttpRequestMessage requestMessage = builder.Build(DateTime.UtcNow)) // probably need to renew Date header upon issuing the actual request
{
   // issue request
}

Upvotes: 2

Related Questions