I am trying to get information from a fantasy data API using AngularJS. I am using $resource to perform my get request in my controller, but I haven't been able to figure out how to correctly include the API key. Do I need to include it as a header? Thanks.
nflApp.controller('mainController', ['$scope','$resource','$routeParams', function($scope, $resource, $routeParams) {
$scope.fantasyAPI = $resource("https://api.fantasydata.net/nfl/v2/JSON/DailyFantasyPlayers/2015-DEC-28", { callback: "JSON_CALLBACK" }, { get: { method: "JSONP"}});
console.log($scope.fantasyAPI);
}]);
Below is the http request info from the site.
You should set a header with the API key, AngularJS will send them with every request in the following case:
$http.defaults.headers.common["Ocp-Apim-Subscription-Key"] = key;
When adding '.common' you are telling angular to send this in every request so you do not need to add it to every resource that hits the API.
A easy way to do that is by creating your own interceptors from $httpProvider at "config" fase.
To do that, just write something like:
mymodule.config(['$httpProvider', function($httpProvider){
$httpProvider.interceptors.push(function ($q) {
return {
'request': function (config) {
config.headers['Ocp-Apim-Subscription-Key'] = SomeUserClass.AuthToken();
return config;
},
'response': function (response) {
return response;
}
};
});
});
You need to modify request header in JSONP. Unfortunately it is not possible. As the browser is responsible for header creation and you just can't manipulate that when using JSONP method.
how to change the headers for angularjs $http.jsonp
Set Headers with jQuery.ajax and JSONP?
From that link - https://johnnywey.wordpress.com/2012/05/20/jsonp-how-does-it-work/
Why NOT To Use JSONP?
Deciding against using JSONP is directly related to how it works. First of all, the only HTTP method you can use is GET since that is the only method script tags support. This immediately eliminates the use of JSONP as an option to interact with nice RESTful APIs that use other HTTP verbs to do fun stuff like CRUD. And while we’re on the subject of GET, note that using anything other than URL parameters to communicate with the server API (e.g. sending some JSON over) is also not possible. (You could encode JSON as a URL parameter, but shame on you for even thinking that.)
If they only work with header manipulation you will need to do that call from your server side.
Related
There's a webapp that makes a request (let's call it /api/item). This request returns a json body with a field called itemData which is normally hidden from the user, but I want to make that shown.
So how do I make a userscript that listens for the request at /api/item and displays the itemData field?
For reference the way the webapp is making the request is:
return Promise.resolve(new Request(e,r)).then(sendCookies).then(addLangParam).then(addCacheParam).then(addXsrfKey).then(checkZeroRating).then(function(e) {
return fetch(e)
}).then(checkStatus).then(checkApiVersionMismatch).then(checkApiResponse)
Most of that is irrelevant, but the important part is Request (I think).
This webapp is not using XMLHttpRequest, but the Fetch API.
You can use the fetch-intercept npm module to intercept fetch requests. Example code:
import fetchIntercept from 'fetch-intercept'
fetchIntercept.register({
response(response) {
console.log(response)
return response
}
})
Do you have access to the promise returned ?
If so, then you may add another "then".
Otherwise, you may overwrite "checkApiResponse"
I am trying to query the App Store for information on a given app, however I keep getting the following error.
XMLHttpRequest cannot load https://itunes.apple.com/lookup?id=<some-app-id>.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://www.<some-website>.co.uk' is therefore not allowed access. The response had HTTP status code 501.
The code I'm using to execute the request is as follows.
Does anyone know where I may be going wrong?
var config = {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With',
}
};
$http.get("https://itunes.apple.com/lookup?id=<some-app-id>", config).success(
function(data) {
// I got some data back!
}
);
You can use $http.jsonp,
$http.jsonp("https://itunes.apple.com/lookup", {
params: {
'callback': 'functionName',
'id': 'some-app-id'
}
});
Where functionName is the name of your globally defined function in string form. You can redefine it in your module so that it has access to $scope.
Documentation
Edit: here's a plunker showing my successful approach roughly getting it into an AngularJS app:
http://plnkr.co/edit/QhRjw8dzK6Ob4mCu6T6Z?p=preview
Adding those headers to your server won't change what is happening. The cross origin headers need to be added by the iTunes API.
That is not going to happen, so what you need to do instead is to use JSONP style callbacks in your webpage. There is an example on the iTunes search API page.
http://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html
Note: When creating search fields and scripts for your website, you
should use dynamic script tags for your xmlhttp script call requests.
For example:
<script src="https://.../search?parameterkeyvalue&callback="{name of JavaScript function in webpage}"/>
Note the 'callback' parameter there. That is a function defined globally in your javascript on the page that will get called with the response from the request to the url in 'src'. That function puts the data into your page, or application. You'll have to figure out how.
It's a shame that the language used in this documentation is not clearer, because you must do some kind of JSONP style workaround since they don't have CORS enabled on their API.
If you need to dynamically add a script tag (fetching data once is not enough) you can try this tutorial:
Dynamically add script tag with src that may include document.write
The API in general is probably intended for use by backends (not affected by cross origin issues), not for client side fetching.
Using angular 2:
constructor(private _jsonp: Jsonp) {}
public getData(term: string): Observable<any> {
return this._jsonp.request(itunesSearchUrl)
.map(res => {
console.log(res);
});
}
I have a JS/HTML5 Project based on angularjs where I protect the api with an authorization token set in the http header. Now I also want to protect the access to images from the server.
I know how to do it on the server side, but how can I add HTTP Headers to image requests in angular or javascript? For api request we have already added it to the services ($ressource) and it works.
In Angular 1.2.X
There are more than a few ways to do this. In Angular 1.2, I recommend using an http interceptor to "scrub" outgoing requests and add headers.
// An interceptor is just a service.
app.factory('myInterceptor', function($q) {
return {
// intercept the requests on the way out.
request: function(config) {
var myDomain = "http://whatever.com";
// (optional) if the request is heading to your target domain,
// THEN add your header, otherwise leave it alone.
if(config.url.indexOf(myDomain) !== -1) {
// add the Authorization header (or custom header) here
config.headers.Authorization = "Token 12309123019238";
}
return config;
}
}
});
app.config(function($httpProvider) {
// wire up the interceptor by name in configuration
$httpProvider.interceptors.push('myInterceptor');
});
In Angular 1.0.X
If you're using Angular 1.0.X, you'll need to set the headers more globally in the common headers... $http.defaults.headers.common.Authentication
EDIT: For things coming from
For this you'll need to create a directive, and it's probably going to get weird.
You'll need to:
Create a directive that is either on your <img/> tag, or creates it.
Have that directive use $http service to request the image (thus leveraging the above http interceptor). For this you're going to have to examine the extension and set the proper content-type header, something like: $http({ url: 'images/foo.jpg', headers: { 'content-type': 'image/jpeg' }).then(...)
When you get the response, you'll have to take the raw base64 data and set the src attribute of your image element to a data src like so: <img src="data:image/jpeg;base64,9hsjadf9ha9s8dfh...asdfasfd"/>.
... so that'll get crazy.
If you can make it so your server doesn't secure the images you're better off.
As said here you can use angular-img-http-src (bower install --save angular-img-http-src if you use Bower).
If you look at the code, it uses URL.createObjectURL and URL.revokeObjectURL which are still draft on 19 April 2016. So look if your browser supports it.
In order to use it, declare 'angular.img' as a dependency to your app module (angular.module('myapp', [..., 'angular.img'])), and then in your HTML you can use http-src attribute for <img> tag.
For example: <img http-src="{{myDynamicImageUrl}}">
Of course, this implies that you have declared an interceptor using $httpProvider.interceptors.push to add your custom header or that you've set statically your header for every requests using $http.defaults.headers.common.MyHeader = 'some value';
we have the same issue and solve it using a custom ng-src directive ..
basically a secure-src directive which does exactly what ng-src does (its a copy basically) BUT it extends the url with a query parameter which includes the local authentication header.
The server code returning the resources are updated to not only check the header but also the query parameters. of course the token added to the query parameter might be authenticated slightly differently.. e.g. there might be a time window after after a normal rest request in which such a request is allowed etc .. since the url will remain in the browser history.
From oficial documentation at: http://docs.angularjs.org/api/ngResource/service/$resource
Usage
$resource(url, [paramDefaults], [actions]);
[...]
actions: Hash with declaration of custom action that should extend the
default set of resource actions. The declaration should be created in
the format of $http.config:
{action1: {method:?, params:?, isArray:?, headers:?, ...},
action2: {method:?, params:?, isArray:?, headers:?, ...}, ...}
More about $http service:
http://docs.angularjs.org/api/ng/service/$http#usage_parameters
As pointed out Here FIrefox supports httpChannel.setRequestHeader :
// adds "X-Hello: World" header to the request
httpChannel.setRequestHeader("X-Hello", "World", false);
In the example code above we have a variable named httpChannel which
points to an object implementing nsIHttpChannel. (This variable could
have been named anything though.)
The setRequestHeader method takes 3 parameters. The first parameter is
the name of the HTTP request header. The second parameter is the value
of the HTTP request header. And for now, just ignore the third
parameter, and just always make it false.
However this seems to be only available on Firefox (link)
You can either use Cookies to pass the value and retrieve it as a cookie instead of a HttpHeader or use ajax to retrieve the image with a custom header.
More links :
link1
link2
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.
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;
}
}];