将序列化的 HttpResponseMessage 缓存到 Redis。读取错误。 "InvalidOperationException: The stream was already consumed. It cannot be read again."
Caching Serialized HttpResponseMessage to Redis. Error on read. "InvalidOperationException: The stream was already consumed. It cannot be read again."
我有一个接受 API 请求的函数,检查结果是否存在于 Redis 缓存中,如果存在 returns 缓存值,如果不存在则发送 API请求然后缓存值。
private async Task<HttpResponseMessage> RestGetCachedAsync(string query, ILogger logger = null)
{
string key = $"GET:{query}";
HttpResponseMessage response;
var cacheResponse = await _cacheService.GetStringValue(key);
if (cacheResponse != null)
{
response = JsonConvert.DeserializeObject<HttpResponseMessage>(cacheResponse, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
});
if(response.IsSuccessStatusCode) return response;
}
response = await RestGetAsync(query, logger);
if (response.IsSuccessStatusCode)
{
await _cacheService.SetStringValue(key, JsonConvert.SerializeObject(response, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
}));
}
return response;
}
读取之前查询过的API
public async Task<string> DeserializeHttpResponse(HttpResponseMessage response)
{
return await response.Content.ReadAsStringAsync();
}
我收到以下错误。
InvalidOperationException: The stream was already consumed. It cannot be read again.
在对问题的评论中进行讨论后,我意识到了我的错误。我认为内容数据与 HttpResponseMessage 一起存储,如果我进行序列化和反序列化,内容数据就会存在。但是,看起来 HttpResponseMessage 中的 Content 数据更像是一个指针,它提供有关如何读取存储在其他地方的值的指令,这些指令由 HttpContent class 中的 ReadAsStringAsync() 函数调用。
所以我的快速修复是创建一个包装器对象来存储序列化的 HttpResponseMessage 以及 ReadAsStringAsync() 返回的内容结果。这个包装看起来像这样。
public class WrapperHttpResponse
{
public HttpResponseMessage HttpResponseMessage { get; set; }
public string Content { get; set; }
public WrapperHttpResponse()
{
}
public WrapperHttpResponse(HttpResponseMessage httpResponseMessage)
{
HttpResponseMessage = httpResponseMessage;
Content = httpResponseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
}
public WrapperHttpResponse(HttpResponseMessage httpResponseMessage, string content)
{
HttpResponseMessage = httpResponseMessage;
Content = content;
}
}
此方法有 3 个构造函数,它们允许我实例化 HttpResponseMessages 的 null、已读和未读实例。然后我重写了我的缓存执行如下。
private async Task<WrapperHttpResponse> RestGetCachedAsync(string query, ILogger logger = null)
{
string key = $"GET:{query}";
WrapperHttpResponse response;
var cacheResponse = await _cacheService.GetStringValue(key);
if (cacheResponse != null)
{
response = JsonConvert.DeserializeObject<WrapperHttpResponse>(cacheResponse, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
});
if(response.HttpResponseMessage.IsSuccessStatusCode) return response;
}
response = await RestGetAsync(query, logger);
if (response.HttpResponseMessage.IsSuccessStatusCode)
{
await _cacheService.SetStringValue(key, JsonConvert.SerializeObject(response, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
}));
}
return response;
}
我有一个接受 API 请求的函数,检查结果是否存在于 Redis 缓存中,如果存在 returns 缓存值,如果不存在则发送 API请求然后缓存值。
private async Task<HttpResponseMessage> RestGetCachedAsync(string query, ILogger logger = null)
{
string key = $"GET:{query}";
HttpResponseMessage response;
var cacheResponse = await _cacheService.GetStringValue(key);
if (cacheResponse != null)
{
response = JsonConvert.DeserializeObject<HttpResponseMessage>(cacheResponse, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
});
if(response.IsSuccessStatusCode) return response;
}
response = await RestGetAsync(query, logger);
if (response.IsSuccessStatusCode)
{
await _cacheService.SetStringValue(key, JsonConvert.SerializeObject(response, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
}));
}
return response;
}
读取之前查询过的API
public async Task<string> DeserializeHttpResponse(HttpResponseMessage response)
{
return await response.Content.ReadAsStringAsync();
}
我收到以下错误。
InvalidOperationException: The stream was already consumed. It cannot be read again.
在对问题的评论中进行讨论后,我意识到了我的错误。我认为内容数据与 HttpResponseMessage 一起存储,如果我进行序列化和反序列化,内容数据就会存在。但是,看起来 HttpResponseMessage 中的 Content 数据更像是一个指针,它提供有关如何读取存储在其他地方的值的指令,这些指令由 HttpContent class 中的 ReadAsStringAsync() 函数调用。
所以我的快速修复是创建一个包装器对象来存储序列化的 HttpResponseMessage 以及 ReadAsStringAsync() 返回的内容结果。这个包装看起来像这样。
public class WrapperHttpResponse
{
public HttpResponseMessage HttpResponseMessage { get; set; }
public string Content { get; set; }
public WrapperHttpResponse()
{
}
public WrapperHttpResponse(HttpResponseMessage httpResponseMessage)
{
HttpResponseMessage = httpResponseMessage;
Content = httpResponseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
}
public WrapperHttpResponse(HttpResponseMessage httpResponseMessage, string content)
{
HttpResponseMessage = httpResponseMessage;
Content = content;
}
}
此方法有 3 个构造函数,它们允许我实例化 HttpResponseMessages 的 null、已读和未读实例。然后我重写了我的缓存执行如下。
private async Task<WrapperHttpResponse> RestGetCachedAsync(string query, ILogger logger = null)
{
string key = $"GET:{query}";
WrapperHttpResponse response;
var cacheResponse = await _cacheService.GetStringValue(key);
if (cacheResponse != null)
{
response = JsonConvert.DeserializeObject<WrapperHttpResponse>(cacheResponse, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
});
if(response.HttpResponseMessage.IsSuccessStatusCode) return response;
}
response = await RestGetAsync(query, logger);
if (response.HttpResponseMessage.IsSuccessStatusCode)
{
await _cacheService.SetStringValue(key, JsonConvert.SerializeObject(response, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
}));
}
return response;
}