cache a httpmessage content

131 Views Asked by At

I would like to start caching some of the data in web API But I am getting some errors. I am using the HttpContent and storing it in cache using the CacheHelper.SaveTocache. On the next connection I am checking if it in cache then it’s creating a new httpmessage and setting the httpcontent for the new message but I am getting some errors ? How can I cache a httpmessage?

Errors when trying to get the data from cache

Error: read ECONNRESET
This site can’t be reached

Controller

    public class UserDataController : ApiController
    {
        [System.Web.Http.HttpGet]
        public IHttpActionResult GetUser(string accessKey, string id)
        {
   
           DateTime cacheDuration = DateTime.Now.AddSeconds(cacheDurationTime);
           string cacheKey = String.Format("{0}_{1}", accessKey, id);
           if (CacheHelper.GetObjectFromCach(cacheKey) != null){
              var cacheContent = CacheHelper.GetFromCache<HttpContent>(cacheKey);

              var response = this.Request.CreateResponse(HttpStatusCode.OK);
              response.Content = cacheContent;

              return ResponseMessage(response);

          else {
            var Results = User.UserFromId(id);

            CacheHelper.SaveTocache(cacheKey, Results.Content, cacheDuration);
            return ResponseMessage(Results);
       
          }
        }

    }

Helpers

   public static void SaveTocache(string cacheKey, object savedItem, DateTime absoluteExpiration)
   {
        MemoryCache.Default.Add(cacheKey, savedItem, absoluteExpiration);
   }


   public static T GetFromCache<T>(string cacheKey) where T : class
   {
        return MemoryCache.Default[cacheKey] as T;
   }
1

There are 1 best solutions below

2
VonC On BEST ANSWER

Caching HttpContent or HttpResponseMessage objects directly is generally not advisable, because of the internal state that these objects can maintain, including streams and headers which can be difficult to serialize or may be stateful in nature.

A "read ECONNRESET" or "That site cannot be reached" could indicate you are reusing an HttpContent object that has already been consumed, or an internal state that has been corrupted. When you store these complex objects in cache, they might retain some state that becomes invalid or non-reusable upon retrieval.

A more recommended approach is to cache the raw data that you want to send or have received. For instance, if you are returning JSON or XML data, consider caching that string representation instead of the entire HttpContent or HttpResponseMessage object.

public class UserDataController : ApiController
{
    [System.Web.Http.HttpGet]
    public IHttpActionResult GetUser(string accessKey, string id)
    {
        DateTime cacheDuration = DateTime.Now.AddSeconds(cacheDurationTime);
        string cacheKey = String.Format("{0}_{1}", accessKey, id);

        var cachedData = CacheHelper.GetFromCache<string>(cacheKey);
        
        if (cachedData != null)
        {
            var response = this.Request.CreateResponse(HttpStatusCode.OK);
            response.Content = new StringContent(cachedData, Encoding.UTF8, "application/json");
            return ResponseMessage(response);
        }
        else
        {
            var Results = User.UserFromId(id);
            string rawData = Results.Content.ReadAsStringAsync().Result;
            
            CacheHelper.SaveTocache(cacheKey, rawData, cacheDuration);
            return ResponseMessage(Results);
        }
    }
}

That would make sure you are caching only the data that you need and minimizes the chances of running into issues due to object state.


Is there a way to get the raw data from the httpcontent?

Yes, you should be able to extract the raw data from HttpContent by reading the content as a string, byte array, or stream. One of the most common ways is to read it as a string using the ReadAsStringAsync method. That is an asynchronous method, so you would typically use it with await. In a synchronous context, you can use .Result to get the value, but be cautious as this could lead to deadlocks in some situations.

HttpContent content = /* your HttpContent object */;
string rawData = await content.ReadAsStringAsync();

(see below, next section, for more)

Or, in a synchronous context:

string rawData = content.ReadAsStringAsync().Result;

If you want to read the content as a byte array:

byte[] rawData = await content.ReadAsByteArrayAsync();

Or, to read it as a stream:

Stream rawData = await content.ReadAsStreamAsync();

Once you have the raw data, you can cache it using your caching logic and reconstruct the HttpContent or HttpResponseMessage object when needed.


When reading the content of an HttpContent object asynchronously, you can use the async and await keywords to ensure that the operation is awaited, thus preventing the main execution thread from being blocked. If you are inside an async method, you can directly await the asynchronous methods.

public async Task<string> ReadContentAsync(HttpContent content)
{
    // Await the ReadAsStringAsync method and return the result
    string rawData = await content.ReadAsStringAsync();
    return rawData;
}

You can then use this method within another async method like so:

public async Task<IHttpActionResult> GetUserAsync(string accessKey, string id)
{
    // other logic ...

    HttpResponseMessage Results = await User.UserFromIdAsync(id);
    string rawData = await ReadContentAsync(Results.Content);

    // Cache the rawData
    CacheHelper.SaveTocache(cacheKey, rawData, cacheDuration);

    // other logic ...
}

Note the async keyword added to the method definition and the await keyword before calling asynchronous methods. That ensures that the asynchronous operations are correctly awaited.