Why is /GetScripts not cachable and/or cached?
See original GitHub issueHi,
- ASP.NET 4.6.1
- ABP 3.7.2
- MVC without ZERO (ie permissions, menu etc). Basically we GetScripts() for generating service proxies
Now while I realise it is generated at runtime, in my case it is 100% the same all of the time. And as a result it is returned for every single page request.
So why can it not be cached internally with a circuit breaker as needed?
We use the following circuit breaker code to deal with physical files on disk where we hash the last changed file time:
/// <summary>
/// <para>
/// Use this class as follows to create cache busing URL's:
/// </para>
/// <para>
/// <link rel="stylesheet" href="@OzCpCacheBusterHelper.FingerPrint('/css/mainStyle.css')" />
/// </para>
/// </summary>
/// <remarks>
/// <para>
/// By adding this snippet of XML to the web.config’s
/// <system.webServer>
/// section, we instruct IIS 7+ to intercept all URLs with a
/// folder name containing “v=[md5Hash]” and rewrite the URL to the original file path.
/// </para>
/// <para>
/// <!-- Rule to support OzCpCacheBusterHelper -->
/// <rule name="fingerprint" stopProcessing="true">
/// <match url="(.*)(v-[a-zA-Z0-9]+/)([\S]+)" />
/// <action type="Rewrite" url="{R:1}/{R:3}" />
/// </rule>
/// </para>
/// <para>
/// You need to run the AppPool in Integrated Pipeline mode for the <system.webServer> section to have any effect.
/// </para>
/// </remarks>
public static class OzCpCacheBusterHelper
{
/// <summary>
/// Constructs a cache busting path in the form of /original/path/to/file.css/v-xxx where
/// xxx is the MD5 hash of the last write timestamp of the physical file located at
/// \WebRoot\original\path\to\file.css
/// </summary>
/// <param name="aRootRelativePath">Relative path to the file.</param>
/// <param name="aCdnPath">Path to the CDN version of the file.</param>
/// <returns>Returns a valid cache busting path to the original resource.</returns>
public static string FingerPrint(string aRootRelativePath, string aCdnPath = "")
{
//Return the CDN path if we have one assigned and we are not debugging
if (!string.IsNullOrEmpty(aCdnPath) && !HttpContext.Current.IsDebuggingEnabled)
{
return aCdnPath;
}
//Should the relative path not already exist in the cache then lets add it
if (HttpRuntime.Cache[aRootRelativePath] == null)
{
//Determine both absolute and relative paths
string relative = VirtualPathUtility.ToAbsolute("~" + aRootRelativePath);
string absolute = HostingEnvironment.MapPath(relative);
if (!File.Exists(absolute))
{
throw new FileNotFoundException("File not found", absolute);
}
//Construct a file path in the form {relative}/v-{Md5 Of Last Write Time}/ to ensure we have unique path
DateTime fileLastWriteTime = File.GetLastWriteTime(absolute);
int index = relative.LastIndexOf('/');
string result = relative.Insert(index, "/v-" + Md5($"{fileLastWriteTime:yyy-MM-dd_hh:mm:ss.fff}"));
HttpRuntime.Cache.Insert(aRootRelativePath, result, new CacheDependency(absolute));
}
return HttpRuntime.Cache[aRootRelativePath] as string;
}
/// <summary>
/// Computes an MD5 hash of the input value
/// </summary>
/// <param name="aInput">String value to hash.</param>
/// <returns>Returns an MD5 hash of the input as a string.</returns>
private static string Md5(string aInput)
{
//Compute the hash
MD5 md5 = MD5.Create();
byte[] inputBytes = Encoding.ASCII.GetBytes(aInput);
byte[] hash = md5.ComputeHash(inputBytes);
//Convert byte array to string
StringBuilder sb = new StringBuilder();
foreach (byte hashCharacter in hash)
{
sb.Append(hashCharacter.ToString("X2"));
}
return sb.ToString();
}
}
That way clients receive a cached copy until the file is updated. I would think something similair could be applied to the generated contents of GetScripts() ie. An MD5 of its contents and if unchanged then the content is returned from a HttpRuntime.Cache[]
entry.
The time to perform the above MD5 and cache inspection would be well worth it as I am guessing that would be less than the time it takes to deliver GetScripts() on the wire as well as the saved bytes on the wire.
Thoughts?
Issue Analytics
- State:
- Created 5 years ago
- Comments:28 (25 by maintainers)
Top GitHub Comments
@ismcagdas You guys already cache it at the server level, so how is it any different if you were to cache the requests lol. Just invalidate it when someone changes a permission or localization in the UI. You already do it now for the local cache. The other option is to break out things that can be cached and what can not. I assume the app service js stuff can be.
https://stackoverflow.com/questions/1167890/how-to-programmatically-clear-outputcache-for-controller-action-method
As we don’t have permissions issues we did end up caching it in our MVC applications by changing our templates to call:
Our Layout controller then has /layout/getscripts:
And we have a common method to minify it after retrieving it from the original end point /abscripts/getscripts:
Note: The SSL complication can be avoided ultimately by either not using SSL, not using Flurl, Using Flurl and implementing a custom certificate handler.
@hikalkan Hopefully you can add a module configuration that allows minification of any dynamically generated scripts as well.