Angular JS and Cross Origin Requests - javascript

I am just playing around with angular JS and wiring it up to some restful web services I have implemented. I have moved onto using the $resource module provided by angular and have a simple question (I hope). In my code below I am making a request to a spring boot micro service I have written and am wanting to know the best way of accessing the URL.
So is there another way of calling the resource that is cross origin rather than having to write the line below. Is there something like /customer/greeting I could use but then how would I specify the different port as my angular app resides on localhost:8000?
http://localhost\:9001/customer/greeting //this is a spring boot application
My full code for the service.js is below this resides on localhost:8000 and is a node JS server.
'use strict';
/* Services */
var phonecatServices = angular.module('phonecatServices', ['ngResource']);
phonecatServices.factory('Phone', ['$resource',
function($resource) {
return {
pDetail: $resource('phones/:phoneId.json', {}, {
query: {method: 'GET', params: {phoneId: 'phones'}, isArray: true}
}),
cDetail: $resource('http://localhost\:9001/customer/greeting', {}, {
query: {method: 'GET'}
})
};
}]);
When people normally implement do they have lots of http://balh blah when it goes cross origin? Is there a pattern that can be applied here?
Thanks in advance.

You can do this way:
Make function
var getCrossOriginUrl=function(portNo){
var crossOriginPath="http://localhost\:"
return crossOriginPath+portNo+"/";
}
So now you can call this way:
$resource(getCrossOriginUrl(9001)+'customer/greeting',{}, {
query: {method: 'GET'}
})

Related

Send file based dummy data from Angularjs REST resource

I am trying to let UI development proceed without needing to be coupled to a backend. My the normal REST api is being built like:
a.factory('Sample', ['$resource',
function($resource){
return $resource(baseUrl() + '/sample/:id', {}, {
query: {method:'GET', params:{id:''}, isArray:true, cache:false},
update: { method:'PUT' },
remove: { method:'DELETE'}
});
}]);
This is fine when there is an actual backend. However, for development purposes (NOT Testing), canned data from a file is desired. This can be achieved like:
['$scope', '$http',
function($scope, $http) {
$http.get('data/sampleList.json').success(function(data) {
$scope.sampleData = data;
});
}]
Obviously, I'm no expert here, but I am wondering if there is an easy way to combine these two approaches such that the $resource REST instance can return (for GET requests anyway), canned data from a file?
Why didn't you say this was such a newb question or that you never RTFM? Oh wait, this is my own question! So, sorry folks, didn't realize this was quite so trivial - there is a 'URL' parameter available on every such method. While this will only work for Mockups or UI dev before the backend is done, for the above, all I needed to do was:
a.factory('Sample', ['$resource',
function($resource){
return $resource(baseUrl() + '/sample/:id', {}, {
query: {url: 'data/sampleList.json', method:'GET', params:{id:''}, isArray:true, cache:false},
update: { method:'PUT' },
remove: { method:'DELETE'}
});
}]);
qed

Globals across Angular services?

Can I define global variables or functions that can be accessed by all of my Angular service modules?
For example, I have several services that make http requests to various endpoints of the same url root. I would like to be able to change this root.
My service modules look like this.
var host = 'http://my-api.com';
return {
get: function(id) {
return $http({
url: host + '/contacts/' + id,
method: 'GET'
});
},
...
switchHost: function(url){
host = url;
}
});
So I can call ContactService.switchHost('http://new-url') in my Controllers to update this particular Service.
I would like to have some sort of Root Service where I coul define host and switchHost globally.
Note: The use case here is that some of our clients will be accessing our company API, and others will be self-hosting their resources.
i suggest you to create an interceptor which will digest an angular value like this.
angular.module('...')
.value('HOST', 'http://www.fu.bar.com/')
.factory('InterceptorFactory', function (HOST) {
return {
request: function(config) {
config.url = HOST + config.url;
return config;
}
};
})
.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('InterceptorFactory');
}]);
Whenever you call the $http service, the factory will be called and will add your host.
If you want to update the host, you just need to inject in any block the value and update it.
Thanks for the responses. I just did something like this.
angular.module('app.services').factory('RootService', function($http) {
return {
switchHost: function(url){
this.host = url;
},
host: 'http://app.apiary-mock.com'
}
});
This way the client can just type in a url on the settings page and the root will change, which is what I want.

Angular & Twitter REST API

I've been hunting for a few hours now and can't seem to find any information specific to my setup so here goes.
I'm using the MEAN stack and wanting to use the Twitter API in my angular app. I have all the required keys and trigger a twitter api authentication on the server side using Node, then pass the token I get in response to my angular pages. I was hoping to be able to use this token to make requests to the api from an angular service. The request I'm trying to get working the moment is to fetch a given user's profile object. I've attached my service method below. The error I get when I run it is a 405 method no allowed, no access-control-allow-origin header is present.
angular.module('tms.system').factory('Twitter', ['$log', '$q', '$http', '$window', 'twitter', 'Global', function($log, $q, $http, $window, twitter, Global) {
return {
findProfile: function(handle) {
var deferred = $q.defer();
var config = {
timeout:3000,
headers: {
'Authorization': 'Bearer ' + Global.twitterToken,
'X-Testing' : 'testing'
}
};
$http.get('https://api.twitter.com/1.1/users/show.json?screen_name=' + handle, config).
success(function(data) {
$log.info(data);
deferred.resolve(data);
}).
error(function(status) {
$log.error(status);
});
return deferred.promise;
}
};
}]);
Just for future reference, as stated in the comments of maurycy's answer {and being myself trying to get tweets just from Angular without succes}, the best approach for this would be to get them from some backend.
I believe you should use $http.jsonp with a JSON_CALLBACK to get it to work, it's not going to happen with $http.get for sure

Using Angular.js - how to serve binary data from a backend that requires authentication?

In my angularjs application I am communicating with a backend server that requires basic access authentication via http header. I have implemented the authentication mechanism on the client side as described here.
angular.module('myAuthModule')
.config(['$httpProvider', '$stateProvider',
function ($httpProvider, $stateProvider) {
$httpProvider.interceptors.push('securityInterceptor');
}])
.factory('securityInterceptor', ['$location', '$window', '$q',
function ($location, $window, $q) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers['Auth-Key'] = $window.sessionStorage.token;
}
return config;
},
response: function (response) {
if (response.status === 401 || response.status === 403) {
$location.path('/login');
}
return response || $q.when(response);
}
};
}
]);
So far so good, handling xhr requests within the angular app works as expected.
The problem is that I need to provide a download link for pdf documents. My backend server has a /Document/Pdf/:id resource that serves a application/pdf response with ContentDisposition: attachment which also requires authentication. I understand that I cannot initiate a download using xhr, however both providing a link to the document download via ngHref and calling a function that does for example $window.open('/Document/Pdf/13') lead to a 401 Unauthorized response by the server.
What am I missing here?
Having explored the possibilities given by #Geoff Genz with the addition of a fourth - data-uri option, which unfortunately does not allow defining filenames - I decided to go for a different approach.
I added a method to the API which generates a one-time download link based on a normally authenticated request and download it straight away. The angular handler becomes very simple
.factory('fileFactory', ['$http', '$window',
function ($http, $window) {
return {
downloadFile: function (fileId) {
return $http(
{
method: "POST",
data: fileId,
url: '/api/Files/RequestDownloadLink',
cache: false
}).success(function (response) {
var url = '/api/File/' + response.downloadId;
$window.location = url;
});
}
};
}]);
This is not perfect but I feel is least hack-ish. Also this works for me because I have full control of the front- and back-end.
There is not a simple solution to this. You've already discovered that you cannot download via Ajax, so you can't set a custom header that way. Nor can you set a custom header on a browser generated GET (like an href) or POST (like a form submit). I can suggest three different approaches, all of which will require some modifications on your server:
(1) Use Basic or Digest auth on your web page, so the browser will generate and send the Authorization header with those credentials.
(2) Set the token in "authorization" cookie that will be passed with the request and validate the token server side.
(3) Finally, the way we've implemented this is to use a POST request instead of a GET for the download. We POST to a hidden IFrame on the same page and have the server set the appropriate Content-Disposition header such as "attachment; filename="blah.pdf"" on the response. We then send the authorization token as a hidden field in the form.
None of these are ideal, and I know our solution feels kind of hacky, but I've not seen any more elegant approaches.

Angularjs $resource url intercept url encoding

I'm working on a sort of file-manager application that connects to a RESTFUL file api.
On the angular app, each file and directory is an instance of angular $resource using the file-object property relativePathName as resource id .
js
var File = $resource(url + '/:type/:id', {id: '#relativePathName', type: '#type'}, {…});
The problem is, when updating a file resource, the relativePathName parameter gets url encoded, e.g. / becomes %2F which causes the server to intercept the request before it hits the actual API (I assume the server treats this as a physical address and of returns a 404 response). The API is capable of treating whole url segments as a single param, so basically it'd treat path/to/file as a uri parameter of http://myapp.com/api/files/create/path/to/file and not as a different uri.
My question is, is there a way to modify the request url after it's being generated by the private Router instance inside of the resource constructor? If so, how (found nothing on this in the docs)?. What would be a possible solution? passing relativePathName as a parameter instead of declaring it as the resource id (which would require modifying the API)?
Thanks in advance.
Thomas
Using $resource is not the one stop shop for RESTful service calls, it is merely a convenience service for api's that are structured in a certain way. If $resource cannot do what you need, just create your own service using a mix of $resource and $http that that fits the api you are trying to call.
In our app we wanted a different URL for getByName requests, so we override the resource address with the URL parameter of the action getByName like so:
myapp.factory('ListGroup', ['$resource',
function($resource) {
return $resource(
'/API/List/:Id',
{
Id:'#Id',
Name:'#Name'
},
{
getByName: {method: 'GET', url: '/API/List/Group/:Name', isArray: true}
}
);
}
]);
My question is, is there a way to modify the request url after it's being generated by the private Router instance inside of the resource constructor?
I'm not sure about inside of the resource constructor but you can use interceptors to programatically alter route urls after they have been generated by $resource.
structure
hooks.config.js
hooks.factory.js
hooks.module.js
hooks.module.js
module.exports = angular
.module("app.hooks", [])
.factory("hooks", require("./hooks.factory.js"))
.config(require("./hooks.config.js"));
hooks.config.js
module.exports = ["$httpProvider", function($httpProvider) {
$httpProvider.interceptors.push("hooks");
}];
hooks.factory.js
module.exports = ["$q", "$location", function($q, $location) {
var basePrefix = "/api/v1";
return {
//invoked on every http request
request: function(request) {
request.url = interpret(request.url);
return $q.when(request);
}
};
function interpret(str) {
//if requesting an html template, don't do anything
if (str.indexOf(".html") > -1)
return str;
//if you're accessing the api, append the base prefix globally here
else
return basePrefix + str;
}
}];

Categories