mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 15:10:50 +08:00 
			
		
		
		
	Fix/issue666 (#889)
* cache key now can generate from query string for request with Get Methods and request content for requests with post methods * MD5Helper Added. OutputCacheMiddleware now can generate cache key using method, url and content * unit test created for CacheKeyGenerator * CacheKeyGenerator Registered in OcelotBuilder as singletone
This commit is contained in:
		@@ -33,6 +33,10 @@
 | 
			
		||||
            builder.Services.RemoveAll(typeof(IOcelotCache<FileConfiguration>));
 | 
			
		||||
            builder.Services.AddSingleton<ICacheManager<FileConfiguration>>(fileConfigCacheManagerOutputCache);
 | 
			
		||||
            builder.Services.AddSingleton<IOcelotCache<FileConfiguration>>(fileConfigCacheManager);
 | 
			
		||||
 | 
			
		||||
            builder.Services.RemoveAll(typeof(ICacheKeyGenerator));
 | 
			
		||||
            builder.Services.AddSingleton<ICacheKeyGenerator, CacheKeyGenerator>();
 | 
			
		||||
 | 
			
		||||
            return builder;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								src/Ocelot/Cache/CacheKeyGenerator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Ocelot/Cache/CacheKeyGenerator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Middleware;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Cache {
 | 
			
		||||
    public class CacheKeyGenerator : ICacheKeyGenerator {
 | 
			
		||||
        public string GenerateRequestCacheKey(DownstreamContext context) {
 | 
			
		||||
            string hashedContent = null;
 | 
			
		||||
            StringBuilder downStreamUrlKeyBuilder = new StringBuilder($"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}");
 | 
			
		||||
            if (context.DownstreamRequest.Content != null) {
 | 
			
		||||
                string requestContentString = Task.Run(async () => await context.DownstreamRequest.Content.ReadAsStringAsync()).Result;
 | 
			
		||||
                downStreamUrlKeyBuilder.Append(requestContentString);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            hashedContent = MD5Helper.GenerateMd5(downStreamUrlKeyBuilder.ToString());
 | 
			
		||||
            return hashedContent;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								src/Ocelot/Cache/ICacheKeyGenerator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Ocelot/Cache/ICacheKeyGenerator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
using Ocelot.Middleware;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Cache {
 | 
			
		||||
    public interface ICacheKeyGenerator {
 | 
			
		||||
        string GenerateRequestCacheKey(DownstreamContext context);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/Ocelot/Cache/MD5Helper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/Ocelot/Cache/MD5Helper.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Cache {
 | 
			
		||||
    public static class MD5Helper {
 | 
			
		||||
        public static string GenerateMd5(byte[] contentBytes) {
 | 
			
		||||
            MD5 md5 = MD5.Create();
 | 
			
		||||
            byte[] hash = md5.ComputeHash(contentBytes);
 | 
			
		||||
            StringBuilder sb = new StringBuilder();
 | 
			
		||||
            for (int i = 0; i < hash.Length; i++) {
 | 
			
		||||
                sb.Append(hash[i].ToString("X2"));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return sb.ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string GenerateMd5(string contentString) {
 | 
			
		||||
            byte[] contentBytes = Encoding.Unicode.GetBytes(contentString);
 | 
			
		||||
            return GenerateMd5(contentBytes);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -7,19 +7,23 @@
 | 
			
		||||
    using Ocelot.Logging;
 | 
			
		||||
    using Ocelot.Middleware;
 | 
			
		||||
    using System.IO;
 | 
			
		||||
    using System.Text;
 | 
			
		||||
 | 
			
		||||
    public class OutputCacheMiddleware : OcelotMiddleware
 | 
			
		||||
    {
 | 
			
		||||
        private readonly OcelotRequestDelegate _next;
 | 
			
		||||
        private readonly IOcelotCache<CachedResponse> _outputCache;
 | 
			
		||||
        private readonly ICacheKeyGenerator _cacheGeneratot;
 | 
			
		||||
 | 
			
		||||
        public OutputCacheMiddleware(OcelotRequestDelegate next,
 | 
			
		||||
            IOcelotLoggerFactory loggerFactory,
 | 
			
		||||
            IOcelotCache<CachedResponse> outputCache)
 | 
			
		||||
            IOcelotCache<CachedResponse> outputCache,
 | 
			
		||||
            ICacheKeyGenerator cacheGeneratot)
 | 
			
		||||
                :base(loggerFactory.CreateLogger<OutputCacheMiddleware>())
 | 
			
		||||
        {
 | 
			
		||||
            _next = next;
 | 
			
		||||
            _outputCache = outputCache;
 | 
			
		||||
            _cacheGeneratot = cacheGeneratot;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Invoke(DownstreamContext context)
 | 
			
		||||
@@ -31,10 +35,11 @@
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var downstreamUrlKey = $"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}";
 | 
			
		||||
            string downStreamRequestCacheKey = _cacheGeneratot.GenerateRequestCacheKey(context);
 | 
			
		||||
 | 
			
		||||
            Logger.LogDebug($"Started checking cache for {downstreamUrlKey}");
 | 
			
		||||
 | 
			
		||||
            var cached = _outputCache.Get(downstreamUrlKey, context.DownstreamReRoute.CacheOptions.Region);
 | 
			
		||||
            var cached = _outputCache.Get(downStreamRequestCacheKey, context.DownstreamReRoute.CacheOptions.Region);
 | 
			
		||||
 | 
			
		||||
            if (cached != null)
 | 
			
		||||
            {
 | 
			
		||||
@@ -61,12 +66,13 @@
 | 
			
		||||
 | 
			
		||||
            cached = await CreateCachedResponse(context.DownstreamResponse);
 | 
			
		||||
 | 
			
		||||
            _outputCache.Add(downstreamUrlKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region);
 | 
			
		||||
            _outputCache.Add(downStreamRequestCacheKey, cached, TimeSpan.FromSeconds(context.DownstreamReRoute.CacheOptions.TtlSeconds), context.DownstreamReRoute.CacheOptions.Region);
 | 
			
		||||
 | 
			
		||||
            Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void SetHttpResponseMessageThisRequest(DownstreamContext context, DownstreamResponse response)
 | 
			
		||||
        private void SetHttpResponseMessageThisRequest(DownstreamContext context, 
 | 
			
		||||
                                                       DownstreamResponse response)
 | 
			
		||||
        {
 | 
			
		||||
            context.DownstreamResponse = response;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -107,6 +107,7 @@ namespace Ocelot.DependencyInjection
 | 
			
		||||
            Services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
 | 
			
		||||
            Services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
 | 
			
		||||
            Services.TryAddSingleton<IDelegatingHandlerHandlerFactory, DelegatingHandlerHandlerFactory>();
 | 
			
		||||
            Services.TryAddSingleton<ICacheKeyGenerator, CacheKeyGenerator>();
 | 
			
		||||
 | 
			
		||||
            // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
 | 
			
		||||
            // could maybe use a scoped data repository
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ namespace Ocelot.Request.Middleware
 | 
			
		||||
            Headers = _request.Headers;
 | 
			
		||||
            AbsolutePath = _request.RequestUri.AbsolutePath;
 | 
			
		||||
            Query = _request.RequestUri.Query;
 | 
			
		||||
            Content = _request.Content;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public HttpRequestHeaders Headers { get; }
 | 
			
		||||
@@ -37,6 +38,8 @@ namespace Ocelot.Request.Middleware
 | 
			
		||||
 | 
			
		||||
        public string Query { get; set; }
 | 
			
		||||
 | 
			
		||||
        public HttpContent Content { get; set; }
 | 
			
		||||
 | 
			
		||||
        public HttpRequestMessage ToHttpRequestMessage()
 | 
			
		||||
        {
 | 
			
		||||
            var uriBuilder = new UriBuilder
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user