{"id":24358465,"url":"https://github.com/thomasgalliker/httpclient.caching","last_synced_at":"2025-04-06T20:09:59.067Z","repository":{"id":33397644,"uuid":"158063199","full_name":"thomasgalliker/HttpClient.Caching","owner":"thomasgalliker","description":"Caching extensions for .NET HttpClient","archived":false,"fork":false,"pushed_at":"2025-02-24T06:50:09.000Z","size":164,"stargazers_count":22,"open_issues_count":2,"forks_count":7,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-03-29T10:56:26.337Z","etag":null,"topics":["cache","caching","httpclient","memorycache"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thomasgalliker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"patreon":"user?u=21232884"}},"created_at":"2018-11-18T08:19:22.000Z","updated_at":"2025-02-24T06:50:12.000Z","dependencies_parsed_at":"2024-09-10T08:46:40.324Z","dependency_job_id":"38a7efd3-3ae2-4562-bedb-fc58a3d0b9a3","html_url":"https://github.com/thomasgalliker/HttpClient.Caching","commit_stats":{"total_commits":54,"total_committers":4,"mean_commits":13.5,"dds":"0.14814814814814814","last_synced_commit":"500953d8a51102a899af46ecd15e2c843e6bf2f5"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasgalliker%2FHttpClient.Caching","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasgalliker%2FHttpClient.Caching/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasgalliker%2FHttpClient.Caching/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomasgalliker%2FHttpClient.Caching/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thomasgalliker","download_url":"https://codeload.github.com/thomasgalliker/HttpClient.Caching/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247543591,"owners_count":20955865,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","caching","httpclient","memorycache"],"created_at":"2025-01-18T20:11:20.107Z","updated_at":"2025-04-06T20:09:59.048Z","avatar_url":"https://github.com/thomasgalliker.png","language":"C#","readme":"# HttpClient.Caching\n[![Version](https://img.shields.io/nuget/v/HttpClient.Caching.svg)](https://www.nuget.org/packages/HttpClient.Caching)  [![Downloads](https://img.shields.io/nuget/dt/HttpClient.Caching.svg)](https://www.nuget.org/packages/HttpClient.Caching)\n\n### Download and Install HttpClient.Caching\nThis library is available on NuGet: https://www.nuget.org/packages/HttpClient.Caching/\nUse the following command to install HttpClient.Caching using NuGet package manager console:\n\n    PM\u003e Install-Package HttpClient.Caching\n\nYou can use this library in any .Net project which is compatible to .Net Framework 4.5+ and .Net Standard 1.2+ (e.g. Xamarin Android, iOS, Universal Windows Platform, etc.)\n\n### The Purpose of HTTP Caching\nHTTP Caching affects both involved communication peers, the client and the server. On the server-side, caching is appropriate for improving throughput (scalability). HTTP caching doesn't make a single HTTP call faster but it can lead to better response performance in high-load scenarios. On the client-side, caching is used to avoid unnecessarily repetitiv HTTP calls. This leads to less waiting time on the client-side since cache reads have naturally a much better response performance than HTTP calls over relatively slow network links.\n\n### API Usage\n#### Using MemoryCache\nDeclare IMemoryCache in your API service, either by creating an instance manually or by injecting IMemoryCache into your API service class.\n```C#\nprivate readonly IMemoryCache memoryCache = new MemoryCache();\n```\n\nFollowing example show how IMemoryCache can be used to store an HTTP GET result in memory for a given time span (cacheExpirection):\n```C#\npublic async Task\u003cTResult\u003e GetAsync\u003cTResult\u003e(string uri, TimeSpan? cacheExpiration = null)\n{\n    var stopwatch = new Stopwatch();\n    stopwatch.Start();\n\n    TResult result;\n    var caching = cacheExpiration.HasValue;\n    if (caching \u0026\u0026 this.memoryCache.TryGetValue(uri, out result))\n    {\n        stopwatch.Stop();\n        this.tracer.Debug($\"{nameof(this.GetAsync)} for Uri '{uri}' finished in {stopwatch.Elapsed.ToSecondsString()} (caching=true)\");\n        return result;\n    }\n\n    var httpResponseMessage = await this.HandleRequest(() =\u003e this.httpClient.GetAsync(uri));\n    var jsonResponse = await this.HandleResponse(httpResponseMessage);\n    result = await Task.Run(() =\u003e JsonConvert.DeserializeObject\u003cTResult\u003e(jsonResponse, this.serializerSettings));\n\n    if (caching)\n    {\n        this.memoryCache.Set(uri, result, cacheExpiration.Value);\n    }\n    else\n    {\n        this.memoryCache.Remove(uri);\n    }\n\n    stopwatch.Stop();\n    this.tracer.Debug($\"{nameof(this.GetAsync)} for Uri '{uri}' finished in {stopwatch.Elapsed.ToSecondsString()}\");\n    return result;\n}\n```\n\n#### Using InMemoryCacheHandler\nHttpClient allows to inject a custom http handler. In the follwing example, we inject an HttpClientHandler which is nested into an InMemoryCacheHandler where the InMemoryCacheHandler is responsible for maintaining and reading the cache.\n```C#\nstatic void Main(string[] args)\n{\n    const string url = \"http://worldtimeapi.org/api/timezone/Europe/Zurich\";\n\n    var httpClientHandler = new HttpClientHandler();\n    var cacheExpirationPerHttpResponseCode = CacheExpirationProvider.CreateSimple(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(5));\n    var handler = new InMemoryCacheHandler(httpClientHandler, cacheExpirationPerHttpResponseCode);\n    using (var client = new HttpClient(handler))\n    {\n        // HttpClient calls the same API endpoint five times:\n        // - The first attempt is called against the real API endpoint since no cache is available\n        // - Attempts 2 to 5 can be read from cache\n        for (var i = 1; i \u003c= 5; i++)\n        {\n            Console.Write($\"Attempt {i}: HTTP GET {url}...\");\n            var stopwatch = Stopwatch.StartNew();\n            var result = client.GetAsync(url).GetAwaiter().GetResult();\n\n            // Do something useful with the returned content...\n            var content = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();\n            Console.WriteLine($\" completed in {stopwatch.ElapsedMilliseconds}ms\");\n\n            // Artificial wait time...\n            Thread.Sleep(1000);\n        }\n    }\n\n    Console.WriteLine();\n\n    StatsResult stats = handler.StatsProvider.GetStatistics();\n    Console.WriteLine($\"TotalRequests: {stats.Total.TotalRequests}\");\n    Console.WriteLine($\"-\u003e CacheHit: {stats.Total.CacheHit}\");\n    Console.WriteLine($\"-\u003e CacheMiss: {stats.Total.CacheMiss}\");\n    Console.ReadKey();\n}\n```\n\nConsole output:\n```\nAttempt 1: HTTP GET http://worldtimeapi.org/api/timezone/Europe/Zurich... completed in 625ms\nAttempt 2: HTTP GET http://worldtimeapi.org/api/timezone/Europe/Zurich... completed in 48ms\nAttempt 3: HTTP GET http://worldtimeapi.org/api/timezone/Europe/Zurich... completed in 1ms\nAttempt 4: HTTP GET http://worldtimeapi.org/api/timezone/Europe/Zurich... completed in 1ms\nAttempt 5: HTTP GET http://worldtimeapi.org/api/timezone/Europe/Zurich... completed in 1ms\n\nTotalRequests: 5\n-\u003e CacheHit: 4\n-\u003e CacheMiss: 1\n```\n\n### Cache keys\n\nBy default, requests will be cached by using a key which is composed with http method and url (only HEAD and GET http methods are supported).\nIf this default behavior isn't enough **you can implement your own ICacheKeyProvider** wich provides **cache key** starting **from HttpRequestMessage**.\n\nThe following example show how use a cache provider of type MethodUriHeadersCacheKeysProvider.\nThis cache key provider is already implemented and evaluates http method, specified headers and url to compose a cache key.\nwith InMemoryCacheHandler.\n```C#\nstatic void Main(string[] args)\n{\n    const string url = \"http://worldtimeapi.org/api/timezone/Europe/Zurich\";\n\n    var httpClientHandler = new HttpClientHandler();\n    var cacheExpirationPerHttpResponseCode = CacheExpirationProvider.CreateSimple(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(5));\n    // this is a CacheKeyProvider which evaluates http method, specified headers and url to compose a key\n    var cacheKeyProvider = new MethodUriHeadersCacheKeysProvider(new string[] { \"FIRST-HEADER\", \"SECOND-HEADER\" });\n    var handler = new InMemoryCacheHandler(\n        innerHandler: httpClientHandler,\n        cacheExpirationPerHttpResponseCode: cacheExpirationPerHttpResponseCode,\n        cacheKeysProvider: cacheKeyProvider\n    );\n    \n    using (var client = new HttpClient(handler))\n    {\n        // HttpClient calls the same API endpoint five times:\n        // - The first attempt is called against the real API endpoint since no cache is available\n        // - Attempts 2 to 5 can be read from cache\n        for (var i = 1; i \u003c= 5; i++)\n        {\n            Console.Write($\"Attempt {i}: HTTP GET {url}...\");\n            var stopwatch = Stopwatch.StartNew();\n            var result = client.GetAsync(url).GetAwaiter().GetResult();\n\n            // Do something useful with the returned content...\n            var content = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();\n            Console.WriteLine($\" completed in {stopwatch.ElapsedMilliseconds}ms\");\n\n            // Artificial wait time...\n            Thread.Sleep(1000);\n        }\n    }\n\n    Console.WriteLine();\n\n    StatsResult stats = handler.StatsProvider.GetStatistics();\n    Console.WriteLine($\"TotalRequests: {stats.Total.TotalRequests}\");\n    Console.WriteLine($\"-\u003e CacheHit: {stats.Total.CacheHit}\");\n    Console.WriteLine($\"-\u003e CacheMiss: {stats.Total.CacheMiss}\");\n    Console.ReadKey();\n}\n```\n\n### Links\n[How-to: HTTP Caching for RESTful \u0026 Hypermedia APIs](https://www.apiacademy.co/articles/2015/12/how-to-http-caching-for-restful-hypermedia-apis)\n\n### License\nThis project is Copyright \u0026copy; 2022 [Thomas Galliker](https://ch.linkedin.com/in/thomasgalliker). Free for non-commercial use. For commercial use please contact the author.\n","funding_links":["https://patreon.com/user?u=21232884"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomasgalliker%2Fhttpclient.caching","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomasgalliker%2Fhttpclient.caching","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomasgalliker%2Fhttpclient.caching/lists"}