Caching HTTP requests with Angular
angular.module('MyApp').factory('UrlCacheInterceptor', | |
function () { | |
'use strict'; | |
/** | |
* Returns value indicating whether required | |
* features are supported by the browser. | |
*/ | |
var featuresSupported = function () { | |
return ['URL', 'Blob', 'WeakMap'].every(function (feature) { | |
return typeof (window[feature]) !== 'undefined'; | |
}); | |
}; | |
return featuresSupported() ? (function () { | |
var cache = { | |
/* Stores cache rules. */ | |
rules: { | |
/** | |
* Returns cache rule that corresponds to the given HTTP request. | |
* @param config {object} HTTP request configuration. | |
*/ | |
get: function (config) { | |
return cache.rules.contents.filter(function (rule) { | |
return config.url === rule.url && | |
config.method.toLowerCase() === | |
(rule.method || 'get').toLowerCase(); | |
}).pop(); | |
}, | |
/* Stores cache rule contents. */ | |
contents: [ | |
{ url: '/profile' }, | |
{ url: '/logout', method: 'post', onInsert: function () { | |
/* On logout, clearing the entire cache. */ | |
cache.clear(); | |
/* No need to cache this HTTP response. */ | |
return true; | |
} } | |
] | |
}, | |
/* Stores cache contents. */ | |
contents: new WeakMap(), | |
/** | |
* Returns cache data that corresponds to the given rule. | |
* @param rule {object} Cache rule. | |
*/ | |
get: function (rule) { | |
return rule ? cache.contents.get(rule) : null; | |
}, | |
/** | |
* Inserts cache data that corresponds to the given rule. | |
* @param rule {object} Cache rule. | |
* @param data {object} Cache data. | |
*/ | |
set: function (rule, data) { | |
/* Binding to the document via in-memory URL. */ | |
var url = URL.createObjectURL( | |
new Blob([ | |
JSON.stringify(data) | |
], { type: 'application/json' } | |
) | |
); | |
cache.contents.set(rule, url); | |
return url; | |
}, | |
/** | |
* Removes cache data that corresponds to the given rule. | |
* @param rule {object} Cache rule. | |
*/ | |
remove: function (rule) { | |
var url = cache.contents.get(rule); | |
if (url) { | |
URL.revokeObjectURL(url); | |
cache.contents.delete(rule); | |
} | |
return url; | |
}, | |
/** | |
* Clears all the data from the cache. | |
*/ | |
clear: function () { | |
cache.rules.contents.forEach(cache.remove); | |
} | |
}; | |
return { | |
/** | |
* Intercepts the given HTTP request. | |
* @param config {object} HTTP request configuration. | |
*/ | |
request: function (config) { | |
/* Getting the in-memory URL that corresponds to cached data. */ | |
var url = cache.get(cache.rules.get(config)); | |
if (url) { | |
/* Overriding request URL - no need to go to the server. */ | |
config.url = url; | |
} | |
return config; | |
}, | |
/** | |
* Intercepts the given HTTP response. | |
* @param response {object} HTTP response. | |
*/ | |
response: function (response) { | |
/* Getting cache rule for the given HTTP request. */ | |
var rule = cache.rules.get(response.config); | |
/* Not caching same request multiple times. */ | |
if (rule && !cache.get(rule)) { | |
/* Allowing custom logic to execute before insertion. */ | |
if (!rule.onInsert || !rule.onInsert(response)) { | |
/* Caching response data. */ | |
cache.set(rule, response.data); | |
} | |
} | |
} | |
}; | |
})() : {}; | |
} | |
); |
This comment has been minimized.
This comment has been minimized.
Hey @szymko, just noticed your comment ;-) In this particular case, rules will not be GC'ed since they're being held by I agree that if you want your rules to be dynamically populated, Cheers! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
hey, that's cool, I just have one question though. Why is
WeakMap
used here? Is there any chance that one ofrules
will be garbage collected?