How to intercept $resource requests - angularjs

Would there be a way to intercept requests in the $resource call?
I want to add an OAUTHv2 header to it, instead of specifying this for every resource model.
Currently I'm only able to intercept the response, as stated in the docs:
...
interceptor - {Object=} - The interceptor object has two optional
methods - response and responseError. Both response and responseError
interceptors get called with http response object. See $http
interceptors.
I know you can push a global interceptor on $http, but I don't want to include my Bearer token in any request outside API calls (security...)
Anybody who is doing OAUTHv2 must have come across this problem. A pity there is no standard way in Angular.JS...

Though, it's not obvious, there is a way to intercept $resource request.
Here is an example:
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Intercept resource request</title>
  <style type="text/css">.ng-cloak { display: none; }</style>
  <script src="angular.js"></script>
  <script src="angular-resource.js"></script>
  <script>
angular.module("app", ["ngResource"]).
  factory(
    "services",
    ["$resource", function ($resource)
    {
      return $resource(
"http://md5.jsontest.com/",
        {},
{
          MD5:
{
method: "GET",
params: { text: null },
then: function(resolve)
{
this.params.text = "***" + this.params.text + "***";
this.then = null;
resolve(this);
}
}
});
}]).
  controller(
    "Test",
    ["services", function (services)
    {
      this.value = "Sample text";
      this.call = function()
      {
        this.result = services.MD5({ text: this.value });
      }
    }]);
  </script>
</head>
<body ng-app="app" ng-controller="Test as test">
  <label>Text: <input type="text" ng-model="test.value" /></label>
  <input type="button" value="call" ng-click="test.call()"/>
  <div ng-bind="test.result.md5"></div>
</body>
</html>
How it works:
$resource merges action definition, request params and data to build a config parameter for an $http request.
A config parameter passed into an $http request is treated as a promise like object, so it may contain then function to initialize config.
Action's then function may transform request as it wishes.
The demo can be found at transform-request.html
Elsewhere I've already shown a similar approach used to cancel $resource request.
See also: Intercept angularjs resource request

You can use $http interceptors
You can pass them in options for $resource methods
Article on that: http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/

Related

How to bind a return from an AJAX call to an Angular $scope variable

I'm building an ASP.NET MVC application with an Angular front-end. I. Successfully call the Angular GetData() function on page load and I've traced to confirm that Home/DataRefresh is returning data in the correct format.
However when I use data to populate a table in the view nothings shows up, no errors, the calls all complete, the table is just empty.
I have a suspicion that it has something to do with how you have to access the Angular $scope within a non-angular function but I'm too new to angularjs to know for sure. I've read through all the documentation I could find to no avail.
EDIT: Per suggestion here's the $http call and the Angular it's contained in.
app.controller("processModel", function ($scope) {
$scope.sortType = 'SchedWk';
$scope.sortReverse = false;
$scope.GetData = function() {
$scope.LoadData();
};
$scope.LoadData = function() {
//$.ajax({
// type: "GET",
// url: 'Home/DataRefresh',
// dataType: "json",
// success: function (data) {
// $scope.data = data;
// },
// error: function (a, b, c) {
// alert("The jqXHR object: " + a + " Error Type: " + b + " Error Description: " + c);
// }
//});
$http({
method: "GET",
url: '/Home/DataRefresh'
}).then(function success(data) {
$scope.data = data;
}, function error(errResponse) {
alert("y u break it tho");
});
};
});
Unlike jQuery AJAX, the $http service returns a response object, of which data is attached as a property of that object:
$http({
method: "GET",
url: '/Home/DataRefresh'
}).then(function success( ̶d̶a̶t̶a̶ response) {
̶$̶s̶c̶o̶p̶e̶.̶d̶a̶t̶a̶ ̶=̶ ̶d̶a̶t̶a̶;̶
$scope.data = response.data;
}, function error(errResponse) {
alert("y u break it tho");
});
From the Docs:
$http Returns
A Promise that will be resolved (request success) or rejected (request failure) with a response object.
The response object has these properties:
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
xhrStatus – {string} – Status of the XMLHttpRequest (complete, error, timeout or abort).
-— AngularJS $http Service API Reference - Returns.
You probably need to call $scope.apply() in the handler after setting the data only the scope because that handler after the ajax call is happening outside of Angular. If you used Angular's $http service instead of $.ajax, you wouldn't need to handle that manually.
You need to inject the $http service first:
app.controller("processModel", function($scope, $http) {
$scope.sortType = 'SchedWk';
$scope.sortReverse = false;
$scope.GetData = function() {
$scope.LoadData();
};
$scope.LoadData = function() {
$http({
method: "GET",
url: '/HomeDataRefresh'
}).then(function success(data) {
$scope.data = data;
}, function error(errResponse) {
alert("y u break it tho");
});
};
});
You lost the reference to $scope in your AJAX call because it is nested into several JavaScript Functions. When using a JQuery AJAX call you can force AngularJS to pick it up by calling $scope.$apply() which will run the digest loop again.
You really should be using $http or $q to take advantage of Promises in JavaScript. They are very powerful and simplify the use of asynchronous operations in JavaScript.
This link should get you up and running quickly:
https://docs.angularjs.org/api/ng/service/$http
To include $http in your controller you'll need to inject it as in my example.
var app = angular.module('app', []);
app.controller('postController', ['$scope', '$http', function($scope, $http){
$scope.data = 'empty';
$scope.runAJAX = function(){
$http.get('https://jsonplaceholder.typicode.com/posts').then(function(response){
$scope.data = response.data;
}).catch(function(error){
console.log(error);
});
}
}]);
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body ng-controller="postController">
<div>
{{data}}
</div>
<div>
<input type="button" value="run AJAX" ng-click="runAJAX()"/>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<script src="script.js"></script>
</body>
</html>

How to make a download button with the AngularJS $http service

Is it possible to trigger a download via a button and using AngularJS $http service in the background to provide the file?
I want to have a simple button, which starts a file download without opening a new window. Furthermore I have to set the Authorization inside of the header.
So far I did this:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
$scope.startDownload = function() {
var auth = ""; //username + password
$http.defaults.headers.common['Authorization'] = auth;
//this will return a file
$http({
method: 'GET',
url: 'https://upload.wikimedia.org/wikipedia/commons/1/1c/FuBK_testcard_vectorized.svg?download'/*,
headers: {
'Authorization': 'Basic efhjwefjfbweikabfkwhfb'
}*/
}).then(function(){
//????
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<button type="button" ng-click="startDownload()">Download</button>
</div>
change this :
ng-click="startDownload()"
After reading a ton of questions regarding similar problems, it emphasizes that it is not possible to save a file on the users disk, which comes from an AJAX request.
See this question on Stackoverflow.

How send POST request in angularjs with _csrf?

I want sending POST requests to service under spring security.
if i using form submission (with hidden input) - it's working fine.
But i don't know, how send request with $http.post.
my form:
<form method="POST" type="submit" action="http://localhost:8888/rest/post1" />
<button>345</button>
<input name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form>
it work.
I save params:
<script>
var a = "${_csrf.parameterName}";
var b = "${_csrf.token}";
</script>
My ng-click function:
$http.post('http://localhost:8888/rest/post1', {"_csrf": b}, {
"headers" :
{ "_csrf" : b }
}).success(function(data) {
console.log("ok");
}).error(function(){
console.log("no");
});
It always writing "no".
I think, that need to send variable as webForm, but how do it in $http.post?
Fiddler write: Content-Type is 'application/json'; this inspector supports 'x-www-form-urlencoded' only
Server console:
org.springframework.web.servlet.PageNotFound handleHttpRequestMethodNotSupported
WARNING: Request method 'POST' not supported
Help me, please!
You might need to set "_csrf" in the request header. You are passing it as an argument for a method. This won't work. I am not much aware of angular.js but you must need to pass it as request header or request payload. I found some reference. Here it goes:
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
Hope this helps you to solve your problem. Cheers !!!
I solved this)
var request = $http({
method: "post",
url: "http://localhost:8888/rest/post1",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(){return a+"="+b;},
data: {}
});

Can't perform CORS request using Angularjs

I am writing my first AngularJS app and have run into a problem with CORS in Angular. Have tried what was mentioned in this reply but still have no luck solving this.
The JS code (main.js) is as follows:
var app = angular.module("app",[]);
app.config(['$httpProvider', function($httpProvider) {
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
app.controller("AppCtrl", function ($scope, $http) {
var postData = "username=demo&password=demo";
$http.post("http://sampleurl.com/login",postData).success(function(data, status, headers, config)
{
console.log(data);
});
});
The Index.html file is:
<!DOCTYPE html>
<html>
<head>
<title>Sample Angular App</title>
</head>
<body ng-app="app" ng-controller="AppCtrl as app">
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>
<script src="main.js"></script>
</body>
</html>
The error I get is the following:
Failed to load resource: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
I have tried running this request using Ajax and it works just fine with Ajax. For some reason can't get this to work using Angular. Any idea as to what I am doing wrong here?
Thanks!
Ok. So basically after alot of tinkering around I realised that params were not getting passed using
$http.post("http://sampleurl.com/login",postData)
So I rewrote this request as follows:
$http({
url:'http://sampleurl.com/api',
method:"POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: postData
})
This got the request to go through just fine. Hope this is of help to someone else.
Cheers!
There are two things you will need to do, in the .config you need to put this:
$httpProvider.defaults.withCredentials = true;
And then on your server you need to return an an Access-Control-Allow-Origin header, it would possibly look like this:
Access-Control-Allow-Origin:http://URL_OF_SITE_YOUR_ANGULARJS_APP_IS_AT
If you want to send a CORS request 'withCredentials', you'll need to have your server reply with 'Access-Control-Allow-Origin':'http(s)://your.request.origin'. A wildcard (*) reply will be refused by most browsers for requests with credentials.
I was stuck with the same issue, then I read from this link:
http://better-inter.net/enabling-cors-in-angular-js/
and things worked like a charm :)
var myApp = angular.module('myApp', [
'myAppApiService']);
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
I had the same problem before and I resolve it by adding this header:
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},

change Content-type to "application/json" POST method, RESTful API

I am new at AngularJS and I needed your help.
All I need just need is to POST my json to the API and recieve the proper response.
Here's my JSON where i don't know where to code this.
JSON
{
"userId" :"testAgent2",
"token" :"testAgent2",
"terminalInfo":"test2",
"forceLogin" :"false"
}
NOT SURE IF I'm doing this right.
CONTROLLER.JS
function UserLoginCtrl($scope, UserLoginResource) {
//Save a new userLogin
$scope.loginUser = function() {
var loggedin = false;
var uUsername = $scope.userUsername;
var uPassword = $scope.userPassword;
var uforcelogin = 'true';
UserLoginResource.save();
}
}
SERVICES.JS
angular.module('UserLoginModule', ['ngResource'])
.factory('UserLoginResource', function($resource, $http) {
$http.defaults.useXDomain = true;
delete $http.defaults.headers.common['X-Requested-With'];
$http.defaults.headers.post["Content-Type"] = "application/json"; //NOT WORKING
return $resource('http://123.123.123.123\\:1234/SOME/LOCATION/THERE', {}, {
save: {
method:'POST',
headers: [{'Content-Type': 'application/json'}]
} //NOT WORKING EITHER
});
});
INDEX.HTML
<html ng-app>
<head>
<script src="js/lib/angular/angular.js"></script>
<script src="js/lib/angular/angular-resource.js"></script>
</head>
<body ng-controller="UserLoginCtrl">
<form class="form-horizontal" name="form-horizontal" ng-submit="loginUser();">
<div class="button-login">
<!-- start: button-login -->
<button class="btn btn-primary" type="submit">Login</button>
</div>
</form>
</body>
</html>
I kept on getting a response like Unsupported Media Type. I don't know, what else to do.
Assuming you are able to use one of the more recent "unstable" releases, the correct syntax to change the header is.
app.factory('BarService', function ($resource) {
var BarService = $resource('/foo/api/bars/:id', {}, {
'delete': {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
}
}
});
return BarService;
});
I find the $resource service is a tremendously powerful tool for building applications and has matured to a point that you do not need to fall back to $http as much. Plus its active record like patterns are damn convenient.
Posting a JSON object is quite easy in Angular. All you need to do is the following:
Create a Javascript Object
I'll use your exact properties from your code.
var postObject = new Object();
postObject.userId = "testAgent2";
postObject.token = "testAgent2";
postObject.terminalInfo = "test2";
postObject.forceLogin = "false";
Post the object to the API
To post an object to an API you merely need a simple $http.post function. See below:
$http.post("/path/to/api/", postObject).success(function(data){
//Callback function here.
//"data" is the response from the server.
});
Since JSON is the default method of posting to an API, there's no need to reset that. See this link on $http shortcuts for more information.
With regards to your code specifically, try changing your save method to include this simple post method.
The right way to set 'Content-Type': 'application/json' is setting a transformRequest function for the save action.
angular.module('NoteWrangler')
.factory('NoteNgResource', function NoteNgResourceFactory($resource) {
// https://docs.angularjs.org/api/ngResource/service/$resource
return $resource("./php/notes/:id", {}, {
save : { // redefine save action defaults
method : 'POST',
url : "./php/notes", // I dont want the id in the url
transformRequest: function(data, headers){
console.log(headers);
headers = angular.extend({}, headers, {'Content-Type': 'application/json'});
console.log(headers);
console.log(data);
console.log(angular.toJson(data));
return angular.toJson(data); // this will go in the body request
}
}
});
});
It seems there isn't a method to clear query parameters, the request will have both...

Resources