angularjs clearing the cache from a particular request - angularjs

All the http requests I'm making are cached by default. Now, how do I clear the cache of a particular request?
Here is a situation to explain better. I have a REST API that sends two different kinds of data based on the authentication. Whenever the User has authenticated I need to clear the old data and make new request with the authentication. My controller gets refreshed but the API call is not being made as the call is already cached. It returns the old data.

You can us a custom cache object using Angular's built-in $cacheFactory
E.g
// cache the HTTP response
$http.get('myurl',{
cache: true
}
// this object can now be retrieved using $cacheFactory
var httpCache = $cacheFactory('$http');
// to remove the value from the cache, get the default $http cache, call the remove function and pass in the url
var httpCache = $cacheFactory.get('$http');
httpCache.remove('myurl');

Telling our $http requests to make requests through our own custom cache is simple. Instead of passing a boolean true with the request, we can pass the instance of the cache.
var myCache = $cacheFactory.get('myCache');
$http({
method: 'GET',
url: '/api/users.json',
cache: myCache
});
// Or, using the .get helper
$http.get('/api/users.json', {
cache: myCache
});
Now, instead of using the default cache, $http will use our custom cache.

Related

$http service cache when the method is post

when I set the $http to cache requests, I still see duplicate requests (with the same url and same data) sent to the server from browser network,
$http.post(url, data, {cache:true} ).success(function(response) {
I have following questions:
Is this a right behaviour?
Can we cache post requests?
Is this the right way to do so or should I be doing it manually with the $cachefactory ?
From the docs:
Only GET and JSONP requests are cached.
If you want to cache POST-requests you would have to do it manually. You will need to make a service/factory that caches responses and serves as a layer before $http. You can use $cacheFactory or just a plain object.
function cacheService($http, $q){
var cache = {};
this.callSomething = function(postData){
let deferred = $q.defer();
let hash = angular.toJson(postData);
if(cache[hash]){
deferred.resolve(cache[hash]);
} else {
$http.post('path/to/resource', postData).then(function(response){
cache[hash] = response;
deferred.resolve(response);
});
}
return deferred.promise;
}
}
This is a simple example, you could of course use the same principle and make a more generalized service that takes an URL, postData and a cache object and returns a function that does the request and caches it.
I am not sure about cache working. But you can use $cacheFactory for same.
app.factory('Cache', function ($cacheFactory) {
return $cacheFactory('Cache');
});
app.controller('MyController', function ($scope, $http, Cache) {
$http.post(url, data, {cache:Cache} ).success(function(response) {}
});
EDIT:
Only GET and JSONP requests are cached.
The cache key is the request URL including search parameters; headers are not considered.
Cached responses are returned asynchronously, in the same way as responses from the server.
If multiple identical requests are made using the same cache, which is not yet populated, one request will be made to the server and remaining requests will return the same response.
A cache-control header on the response does not affect if or how responses are cached.
AngularJS documentation mentions that:
Only GET and JSONP requests are cached.
$http.get(url, {cache: true}) caches the HTTP response in the default cache object (created with $cacheFactory).
Items on the $cachefactory are stored as key-value pairs. The url specified on the $http object is used as the key for the cached value (to be returned). This is one of the reasons it works well with GET which only depends on the URL being hit.
In case of a POST request, the data being sent will also affect the response besides the URL being hit which makes caching a POST request much more complex (since the request will also have to become a part of the key). From the W3 specs:
The actual function performed by the POST method is determined by the
server and is usually dependent on the Request-URI.
The action performed by the POST method might not result in a resource
that can be identified by a URI.
Responses to this method are not cacheable, unless the response
includes appropriate Cache-Control or Expires header fields.
If your data is simple, this SO link might prove a bit useful.

Can I create an $http request and not submitting it (yet)?

I have a generic component in my system that deals with submitting HTTP requests in chunks. It takes care of some application-common headers that I need to attach and updating all the necessary places in the GUI.
This component is shared between couple other components for various use-cases. I'm trying to delegate the task of creating HTTP request from a given input to the callers but found out that when the caller does:
var req = $http.get("url", {})
It's already submitting the request.
I'd like the callers to provide a method to generate a list of request objects from the input, and my component will deal with that later (add some headers for example, add success() and error() methods, or submit the requests in batches).
How can I only create the HTTP Request object, without sending it?
You can create wrapper function for "delayed" http request, that would return you preconfigured ready to call function, and use it instead of $http.
Maybe like this:
function dHttp(config) {
return function(options) {
angular.merge(config, options);
return $http(config);
}
}
var req = dHttp({
url: '/some',
data: {test: 21}
});
// later send it
req().then(function(data) {
console.log(data);
});
A plain AJAX can be useful here
xmlhttp.open("GET","a-url",true);
xmlhttp.send();
$http.get() is a declarative form of an ajax open and send.
var reqArr = [];
reqArr.push(xmlhttp.open("GET","a-url",true));
reqArr.push(xmlhttp.open("GET","b-url",true));
reqArr.push(xmlhttp.open("GET","c-url",true));
so you can change the objects as needed and send the ajax later on.

AngularJS - Setting default http headers dynamically

To overcome csrf attack, I have to send in csrf-token value in a header for every request by picking in the value from cookie as described here. Since this is to be done at every request, I am setting the default headers for $http in the main module's run function.
Now, If a new tab is opened for the same website, a new csrf token (in cookie) is issued by the server. Since the run function is run only once, the default header for csrf will be old one (for old tab), while the new csrf cookie will be sent to server, resulting in csrf-mismatch.
How to overcome this at a global level?
I want somehow to create a function which will be run everytime the $http is called, so that then I'll override the default headers.
Note: I do not want to set this header value for every $http request.
(Not that I think that it's relevant, but I'm using ui-router)
Edit
This is not just limited to csrf-token, I want to set some other headers too based on the logged in user, which has to be done dynamically (say when one user logs in, and logs out, then another user logs in).
you need to use http interceptor to do this on every request. read more about http interceptors here
below is one such example
module.factory('xsrfTokenInterceptor', function ($q, $http) {
return {
'response': function (response) {
var cookies = response.headers("Set-Cookie");
var token = someCrazyParsing(cookies);
$http.defaults.headers.common["X-CSRFToken"]=token;
return response || $q.when(response);
}
};
});
module.config(function($httpProvider){
$httpProvider.interceptors.push('xsrfTokenInterceptor')
})
How about headers $http(config) parameter.
$scope.getWithHeader = function(){
$http({
method: 'GET',
url: 'http://fiddle.jshell.net',
headers: {
'CustomHeader': 'HelloWorld'
}
}).success(function(){
console.log("success");
});
};
sample code on jsFiddle

AngularJS load data from server

I have the following scenario, a page that will show different widgets with different data, the back-end is ASp.NET Web API 2 with SQL Server and EF + Repository Pattern + Unit Of Work.
If I have to show quite some data, including user profile and other information on top of the widgets information, what will you recommend:
make one big $http.get request that will return a big json and bind that one to the UI
or
each controller (service) when it loads will make it's unique call to back-end and get's the data it needs to display, that means each widget will make a call to back-end and retrieve it's values.
I just want to know what do you recommend as a best practice.
IMHO the best way is to separate every request into single service methods that way you can reuse just a part of it and not make server calls to load to whole data, check the angular-resource $resource to have a clean reusable service of server calls and not a bunch of $https arround your code:
example:
A service that points some url of your backend server
.factory('ClientService', ['$resource', function($resource){
return $resource('http://some_url/:controller/:method', null, {
"agents": { method: 'GET', params: { controller: 'agent', method: 'search' }, cache: false },
"query": { method: 'GET', params: { controller: 'client', method: 'search' }, cache: false },
"save": { method: 'POST', params: { controller: 'client', method: 'save' } },
"delete": { method: 'POST', params: { controller: 'client', method: 'delete' } }
})
}])
The use in the controller (Injecting ClientService as dependency)
// If i want to query the agents into a scope element
// that will call the url = http://some_url/agent/search
$scope.agents = ClientService.agents();
// If i want to query a single client i cant send adtional params
// as is a get request it will call http://some_url/client/search?id=5
$scope.client = ClientService.query({id:5});
// and you can event manage callbacks if you want to
// This will send the client object to the url = http://some_url/client/save
ClientService.save($scope.client).$promise.then(function(response){ alert(response) })
As you can see this way you can access just the things you need from the backend server not having to do all the callback response if you dont need to and in a reusable cleaner way
Info Angular Resource Docs
I think it depends...
If performance might be a problem you should think about what is best for your User... Will the overhead of making 4 HTTP requests affect the user experience in anyway? Also, would a one big request take too much time to retrieve info from the database?
However if you want just to use a developer perspective of the problem, I'd prefer doing 1 generic API call then calling it 4 times in Angular with different parameters for each Widget.
It is likely that making 4 requests will actually be faster. Not to mention the data can start being populated on the screen as it comes back, instead of needing to wait for the slowest service.
For the max number of concurrent AJAX requehttp://www.coderanch.com/t/631345/blogs/Maximum-concurrent-connection-domain-browsers

Is it suitable to use $httpBackend in production to abstract data service requests?

I have a data service in my application that is responsible for retrieving information for my controllers. This information might come from local storage, window or an ajax request. The problem I am facing is the $q promise responses don't look like $http responses.
this.getContactDetails = function(data) {
// The first time this method is called, we expect contact details to be preloaded on the page.
// We want to read and return that object then remove it from the page so subsequent requests are to the server.
if(typeof $window.preloadData.contact !== 'undefined') {
var contactDetails = JSON.parse(JSON.stringify($window.preloadData.contact));
delete $window.preloadData.contact;
// Since the method call should always have the same return type, we manually create a deferred object and set the resolution using the $q service.
var deferred = $q.defer();
deferred.resolve(contactDetails);
return deferred.promise;
}
var request = requests.contactDetails.get;
return $http(request);
};
The $q service does a nice job here but it resolves as the object it was given. I wouldn't really expect it to wrap the response. I know $httpBackend could accomplish this.
$httpBackend.whenGET(request).respond(contactDetails);
But the service is used in the MockE2E library and I doubt this was its intended use. I am not sure how to call this off afterwards or what would happen if I used it twice on the same request but I can figure out these questions. My other concern is that there doesn't seem to be a way to pass the same config object to $httpBackend as I do to $http. $httpBackend only accepts a method, url, body and headers, while $http config allows me to specify parameters.
Currently my work-around is simply to create and $http-like wrapper myself.
var contactDetails = JSON.parse(JSON.stringify({
data: $window.preloadData.contact
}));
But I don't find this very elegant. Is there a better/correct way to do this?
You can implement your storage layer as a $cacheFactory and add it to $httpProvider during the configuration phase.
From the docs:
When the cache is enabled, $http stores the response from the server in the specified cache. The next time the same request is made, the response is served from the cache without sending a request to the server.
Hence, if you provide your own implementation of a cache with the following methods:
{object} info() — Returns id, size, and options of cache.
{{*}} put({string} key, {*} value) — Puts a new key-value pair into the cache and returns it.
{{*}} get({string} key) — Returns cached value for key or undefined for cache miss.
{void} remove({string} key) — Removes a key-value pair from the cache.
{void} removeAll() — Removes all cached values.
{void} destroy() — Removes references to this cache from $cacheFactory.
You can return values read from localStorage, session cookies, etc. and they will be treated as there were data sent from the server, just without the AJAX request.

Resources