Akavache and ETags - http://cam.macfar.land/blog/akavache-and-etags/
public static class AkavacheExtensions | |
{ | |
public static IObservable<string> GetOrFetchWithETag(this IBlobCache cache, string url) | |
{ | |
var result = | |
// Get from cache | |
cache.GetObject<string>(url) | |
// Cached values are true | |
.Select(x => Tuple.Create(x, true)) | |
// Turn exceptions into false | |
.Catch(Observable.Return(Tuple.Create("", false))) | |
// If true, return an observable with the result, else an empty observable. | |
.SelectMany(x => x.Item2 ? Observable.Return(x.Item1) : Observable.Empty<string>()); | |
var fetch = | |
// Get the ETag from cache | |
cache.GetObject<string>("etag-" + url) | |
// Exceptions => Blank ETag | |
.Catch(Observable.Return("")) | |
// Call our web method | |
.SelectMany(etag => GetFromWeb(url, etag) | |
// Invalidate the old and add the new etag to the cache | |
.SelectMany(x => cache.InvalidateObject<string>("etag-" + url).Select(_ => x)) | |
.SelectMany(x => cache.InsertObject("etag-" + url, x.Item1).Select(_ => x)) | |
// Invalidate the old and add the new data to the cache | |
.SelectMany(x => cache.InvalidateObject<string>(url).Select(_ => x)) | |
.SelectMany(x => cache.InsertObject(url, x.Item2).Select(_ => x))) | |
// Select the data from the tuple | |
.Select(x => x.Item2); | |
return result | |
.Concat(fetch) | |
.Replay() | |
.RefCount(); | |
} | |
private static HttpClient CreateWebClient() | |
{ | |
var handler = new HttpClientHandler(); | |
if (handler.SupportsAutomaticDecompression) | |
{ | |
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; | |
} | |
var client = new HttpClient(handler); | |
return client; | |
} | |
private static IObservable<Tuple<string, string>> GetFromWeb(string url, string etag) | |
{ | |
return Observable.Create<Tuple<string, string>>(async observer => | |
{ | |
using (var client = CreateWebClient()) | |
{ | |
var request = new HttpRequestMessage | |
{ | |
Method = HttpMethod.Get, | |
RequestUri = new Uri(url) | |
}; | |
if (!string.IsNullOrEmpty(etag)) | |
{ | |
request.Headers.TryAddWithoutValidation("If-None-Match", etag); | |
} | |
var response = await client.SendAsync(request) | |
.ConfigureAwait(false); | |
if (!response.IsSuccessStatusCode && | |
response.StatusCode != HttpStatusCode.NotModified) | |
{ | |
observer.OnError(new HttpRequestException( | |
"Status code: " + response.StatusCode)); | |
} | |
else if (response.IsSuccessStatusCode) | |
{ | |
var data = await response.Content.ReadAsStringAsync() | |
.ConfigureAwait(false); | |
observer.OnNext(Tuple.Create(response.Headers.ETag.Tag, data)); | |
} | |
} | |
observer.OnCompleted(); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment