Serialize and deserialize HttpRequestMessage objects

15,713

Solution 1

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;  

Solution 2

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
}

Solution 3

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.

Share:
15,713
Oddleif
Author by

Oddleif

Coding around on Azure and Office 365 for the most part.

Updated on July 26, 2022

Comments

  • Oddleif
    Oddleif over 1 year

    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
    }); 
    
  • dstj
    dstj over 5 years
    One important thing to note about this solution is that will lose the HTTP scheme and credentials if any. So HTTPS will become HTTP upon deserialization without any credentials...
  • Tóth Tibor
    Tóth Tibor over 5 years
    @dstj What credentials do you refer?
  • dstj
    dstj over 5 years
    The optional credentials that can be part of a url, for example https://username:[email protected]