so I am creating Google Chrome Extension and at first I was injecting my html, angular and javascripts via content script. No I need to inject it by my own. I made that! But the problem is that when it was injected via content script my login method worked just fine in return I got token (that's what I needed), but when injected by myself my login function does not work anymore and it throws this error:
XMLHttpRequest cannot load http://www.cheapwatcher.com/api/Authenticate. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://anywebsitename.com' is therefore not allowed access. The response had HTTP status code 400.
This is my login method (I haven't changed anything since changed the injection type):
angular.module('app').controller('LoginController', LoginController);
LoginController.$inject = ['$scope', '$http', '$location', '$state'];
function LoginController($scope, $http, $location, $state) {
$scope.login = function (user) {
user.grant_type = 'password';
return $http({
method: 'POST',
url: 'http://www.cheapwatcher.com/api/Authenticate',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Access-Control-Allow-Origin': '*'
},
transformRequest: function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: user
}).success(function (result) {
console.log(result);
$(".cheap-watcher").fadeOut(1500, function () {
$(".cheap-watcher").fadeIn($state.go("logout"), {}, { location: false }).delay(2000);
})
}).error(function (result) {
console.log(result);
});
};
};
Can I do something without making CORS on server side? Because as I said injecting via content script works just fine
UPDATE!
So I changed my login method from $http to $.ajax. Everything is the same just instead of $http I wrote $.ajax and removed headers section. In chrome source control the error is the same, but now in fiddler I can see that my request was successful. How is that possible?
now login method looks like this:
angular.module('app').controller('LoginController', LoginController);
LoginController.$inject = ['$scope', '$http', '$location', '$state'];
function LoginController($scope, $http, $location, $state) {
$scope.login = function (user) {
user.grant_type = 'password';
return $.ajax({
url: "http://www.cheapwatcher.com/api/Authenticate",
crossDomain: true,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
method: 'POST',
transformRequest: function (obj) {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: user
}).success(function (result) {
console.log(result);
$(".cheap-watcher").fadeOut(1500, function () {
$(".cheap-watcher").fadeIn($state.go("logout"), {}, { location: false }).delay(2000);
})
}).error(function (result) {
console.log(result);
});
};
};
UPDATE NR. 2!
I saw that error is coming from http://anywebsitename.com 's index page. So I assume that my login request is running not from my extension but from website content. Are there any communication possible from injected script to background script?
Take a look at Requesting cross-origin permissions, you can add http://www.cheapwatcher.com/api/Authenticate to permissions sections.
By adding hosts or host match patterns (or both) to the permissions section of the manifest file, the extension can request access to remote servers outside of its origin.
To use CORS within Angular, we need to tell Angular that we’re using CORS. We use the
.config() method on our Angular app module to set two options.
We need to tell Angular to use the XDomain and
We must remove the X-Requested-With header from all of our requests
angular.module('myApp')
.config(function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers
.common['X-Requested-With'];
});
Related
I get the bellow error when I try to use the JSONP method in angularJS.
Uncaught SyntaxError: Unexpected token : http://example.com/getSomeJson?format=jsonp&json_callback=angular.callbacks._0
What am I doing wrong here, this is my AngularJs controller with the http request:
UPDATED QUESTION DETAILS
See below with code snipit which reproduces my problem, I've commented some of the .js to illustrate what I've tried so far.
var app = angular.module('app', []);
app.controller('mainController', ['$http', 'mainService', function($http, mainService){
mainCtrl = this;
mainCtrl.test = "If you can see this the mainController works"
var promise = mainService.getJson();
promise.then(function (data)
{
mainCtrl.json = data;
});
}]);
app.service("mainService", function ($http, $q)
{
var deferred = $q.defer();
/*
// Method to Grab JSON that has CORs enabled:
// JSON resource with CORs enabled
var url = 'https://jsonplaceholder.typicode.com/posts/1';
$http({
method: 'GET',
cache: true,
url: url,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
}).
success(function(response) {
//your code when success
deferred.resolve(response);
console.log('HTTP CORS SUCCESS!');
}).
error(function(response) {
//your code when fails
console.log('HTTP CORS ERROR!');
});
*/
/* */
// Method to Grab JSON that has CORs enabled:
// JSON resources without CORs enabled
var url = 'http://run.plnkr.co/plunks/v8xyYN64V4nqCshgjKms/data-1.json' // does not work?
// var url = 'http://samcroft.co.uk/json-data/sample' // this one works
$http({
method: 'jsonp',
url: url + '?callback=JSON_CALLBACK',
}).
success(function(response) {
//your code when success
deferred.resolve(response);
console.log('JSONP SUCCESS!');
}).
error(function(response) {
//your code when fails
console.log('JSONP ERROR!');
});
this.getJson = function ()
{
return deferred.promise;
};
});
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular-route.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="mainController as mainCtrl">
<p>{{mainCtrl.test}}</p>
<hr />
<p>You should also see the JSON obeject below:</p>
{{mainCtrl.json}}
</body>
</html>
ORIGIONAL QUESTION DETAILS
app.controller('testController', ['$scope', '$http', function($scope, $http){
var url = 'http://example.com/getSomeJson';
$http({
method: 'JSONP',
url: url,
params: {
format: 'jsonp',
json_callback: 'JSON_CALLBACK'
}
}).
success(function(data) {
//your code when success
$scope.data = data;
console.log('SUCCESS!');
}).
error(function(status) {
//your code when fails
console.log('ERROR!');
});
}]);
When I look at the json in the chromes sources panel I see where the error is highlighted.
Any idea what I'm doing wrong? Or could it be an issue with how the API service is configured?
Here you go :-)
The code you tried with the jsonp request looks good but the url you used is not supporting the jsonp request, that's why you got an error.
If you try the same url with $http.get, it will work fine.
To support the jsonp call, the response should be wrapped with the JSON_CALLBACK () as below
JSON_CALLBACK ({ /* JSON */ })
Hence, I changed this to valid jsonp url and it worked!
https://angularjs.org/greet.php?callback=JSON_CALLBACK
You can try this url in the browser and see the response, how it is wrapped with JSON_CALLBACK ().
But if you try the below url, you can just see the json without any wrapping.
http://run.plnkr.co/plunks/v8xyYN64V4nqCshgjKms/data-1.json?callback=JSON_CALLBACK
That's the difference to find whether the api supports jsonp.
Also, I have changed the service below with the same syntax as in another SO question answer,
https://stackoverflow.com/a/41030976/7055233
Working snippet:
var app = angular.module('app', []);
app.controller('mainController', ['$http', 'mainService', function($http, mainService){
mainCtrl = this;
mainCtrl.test = "If you can see this the mainController works"
var promise = mainService.getJson();
promise.then(function (data)
{
mainCtrl.json = data;
});
}]);
app.service("mainService", function ($http, $q)
{
var deferred = $q.defer();
var url = 'https://angularjs.org/greet.php';
//var url = 'http://run.plnkr.co/plunks/v8xyYN64V4nqCshgjKms/data-1.json';
/*
// Method to Grab JSON that has CORs enabled:
// JSON resource with CORs enabled
var url = 'https://jsonplaceholder.typicode.com/posts/1';
$http({
method: 'GET',
cache: true,
url: url,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
}).
success(function(response) {
//your code when success
deferred.resolve(response);
console.log('HTTP CORS SUCCESS!');
}).
error(function(response) {
//your code when fails
console.log('HTTP CORS ERROR!');
});
*/
/* */
// Method to Grab JSON that has CORs enabled:
// JSON resource without CORs enabled
function getJson() {
// $http.jsonp(url + "?callback=JSON_CALLBACK"). // this does not work either
$http.jsonp(url + '?callback=JSON_CALLBACK').
then(function(response) {
//your code when success
deferred.resolve(response);
console.log('JSONP SUCCESS!');
}, function(response) {
//your code when fails
console.log('JSONP ERROR!');
deferred.reject(response);
});
return deferred.promise;
}
this.getJson = getJson;
});
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular-route.js"></script>
<!--<script src="app.js"></script>-->
</head>
<body ng-controller="mainController as mainCtrl">
<p>{{mainCtrl.test}}</p>
<hr />
<p>You should also see the JSON obeject below:</p>
{{mainCtrl.json}}
</body>
</html>
JSONP callback must be specified by jsonpCallbackParam
BREAKING CHANGE
You can no longer use the JSON_CALLBACK placeholder in your JSONP requests.
Instead you must provide the name of the query parameter that will pass the callback via the jsonpCallbackParam property of the config object, or app-wide via the $http.defaults.jsonpCallbackParam property, which is "callback" by default.
-- Added to AngularJS v1.6.0-rc.2
UPDATE
The OPs example code does not work because the http://run.plnkr.co API does not support JSONP.
JSONP is available only on some sites with an API that pre-date ACCESS-CONTROL headers.
For more information, see JSONP Demystified
I have an html template that displays data from an Angular factory. The problem is that the factory's get method performs call to the backend only when the page is loaded for the first time. When repeatedly opening the page I only see results from the first call to the backend. It looks like the factory somehow caches the results.
With using Chrome debug tools I see that Angular makes GET requests with the same resource id on each page load.
This is the factory definition:
.factory('Company', ['$resource', '$routeParams', function ($resource, $routeParams) {
return $resource('/companies/getCompany/:companyId', {}, {
get: {
method: 'GET',
url: '/companies/getCompany/:companyId',
params: {
'companyId': $routeParams.companyId
}
},
save: {
method: 'POST',
url: '/companies/updateCompany'
},
insert: {
method: 'POST',
url: '/companies/createNewCompany'
}
});
}])
This is the controller code
.controller('MyController', ['$scope', '$location', 'Company',
function ($scope, $location, Company) {
Company.get(function (data) {
$scope.company = data;
});
}]);
I'm using ng-click to open the page
<tr ng-repeat="company in companies"
ng-click="redirectToCompanyForm(company.id)">
$scope.redirectToCompanyForm = function (companyId) {
$location.url('/updateCompany/' + companyId);
}
I set a breakpoint on the factory - app pauses only the first time when I access the page.
Why is my factory called only once and how can I solve this?
From the Angular docs:
Note: All services in Angular are singletons. That means that the injector uses each recipe at most once to create the object. The injector then caches the reference for all future needs.
So you are right, all services are only created once and then cached by Angular.
Edit: better answer for your situation below:
$resource caching
You can disable caching the resource by adding the options to the $resource call:
return $resource('/companies/getCompany/:companyId', {}, {
get: {
method: 'GET',
params: {
'companyId': $routeParams.companyId
},
cache: false // THIS LINE
}
}
Edit 2: according to the docs, the first parameter of $resource is not optional and must be an url.
Maybe you can use low level $http methods in your controller.
$resource is a fantastic utility but in your case you don't want persist the data.
Try the $http.get method..
Or try the query() method.
$scope.myData = DataFactory.query();
Finally fixed the issue. Factory use was incorrect. This is the factory module
.factory('Company', ['$resource', function ($resource) {
return $resource('/companies/getCompany/:id', null, {
save: {
method: 'POST',
url: '/companies/updateCompany'
},
insert: {
method: 'POST',
url: '/companies/createNewCompany'
}
});
}])
And this is how the factory should be called
Company.get({id: $routeParams.companyId}, function (data) {
$scope.company = data;
});
Now the correct data is shown everytime the page is loaded.
I have a problem when you submit parameters using $ http.post in angular.
I assume it's some sort of error itself have little knowledge of angular , because in jquery work fine.
Request jquery'javascript
var user = $('#usuariotxt').val();
var pass = $('#passwordtxt').val();
var login= {
Usuario : user,
Password : pass
};
$.ajax({
type: 'POST',
url: 'http://190.109.185.138/Apipedro/api/login',
data: login,
datatype: 'json'
}).done(function(data) {
console.log(data);
});
Request Angular-Javascript
var app;
app = angular.module('AppUPC',[]);
app.controller('Formulario',['$scope', '$http', function ($scope, $http){
$scope.login = function(){
var login = {
Usuario: $scope.usuariotxt,
Password: $scope.passwordtxt
};
console.log(login);
var url, method;
url = 'http://190.109.185.138/Apipedro/api/login';
method = 'POST';
$http.post("http://190.109.185.138/Apipedro/api/login", {},
{params: {Usuario:$scope.usuariotxt, Password:$scope.passwordtxt}
}).success(function (data, status, headers, config) {
$scope.persons = data;
console.log($scope.persons);
}).error(function (data, status, headers, config) {
$scope.status = status;
console.log($scope.status);
});
};
}]);
I have also used many other forms , including the most common
$http({
url: url,
method: method,
data: login,
headers :{'Content-Type':'application/json'}
})
Errors that occur to me are the following
Short answer: If you want to send the same data as the jQuery example, use this
app.controller('Formulario', ['$scope', '$http', '$httpParamSerializer', function ($scope, $http, $httpParamSerializer) {
// snip
$http.post(url, $httpParamSerializer(login), {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function success(response) {
$scope.persons = response.data;
}, function error(response) {
$scope.status = response.status;
});
}]);
This is because jQuery by default sends POST data as an x-www-form-urlencoded string, ie
Usuario=dfvides&Password=dfvids
Using the code above, Angular will send an identical request to jQuery.
Angular by default sends POST data as JSON with the Content-Type header set to application/json, ie
{"Usuario":"dfvides","Password":"dfvids"}
Is your API even set up to handle a JSON payload?
The reason your Angular version was triggering a pre-flight OPTIONS request (which it appears that your API is not equipped to handle) was because the header Content-Type: application/json makes the request non-simple...
A simple cross-site request is one that:
Only uses GET, HEAD or POST. If POST is used to send data to the server, the Content-Type of the data sent to the server with the HTTP POST request is one of application/x-www-form-urlencoded, multipart/form-data, or text/plain.
Does not set custom headers with the HTTP Request (such as X-Modified, etc.)
(function(){
app = angular.module('mySupport',['ngRoute']).controller('knwCenterCtrl');
var config = {
headers : {
'Accept': 'application/vnd.*********+json',
'Accept-Encoding': 'gzip,deflate',
'custom-header': '12345',
'Access-Control-Request-Headers': 'Accept, custom-header'
},
responseType: 'json'
};
app.controller('knwCenterCtrl', function($scope, $http) {
$http.get("https://abcdefg.com/support/pub/navigation/products", config)
.success(function(response)
{
$scope.prodFam = response.productFamily;
// console.log($scope.mycases);
});
});
})();
What am I missing in this get request? I was able to successfully invoke another APi from the same server using the same code. But for some reason, the code is not making any call to the server. Why is that? Please help.
Thanks,
I want to call a particular function: GetSession() at the beginning of my application load. This function makes a $http call and get a session token: GlobalSessionToken from the server. This session token is then used in other controllers logic and fetch data from the server. I have call this GetSession()in main controller: MasterController in $routeChangeStart event but as its an asynchronous call, my code moves ahead to CustomerController before the $http response.
Here is my code:
var GlobalSessionToken = ''; //will get from server later
//Define an angular module for our app
var myApp = angular.module('myApp', ['ngRoute']);
//Define Routing for app
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/customer', {
templateUrl: 'partials/customer.html',
controller: 'CustomerController',
resolve: {
loadData: function($q){
return LoadData2($q,'home');
}
}
}).
otherwise({
redirectTo: '/home'
});
}]);
//controllers start here and are defined in their each JS file
var controllers = {};
//only master controller is defined in app.js, rest are in separate js files
controllers.MasterController = function($rootScope, $http){
$rootScope.$on('$routeChangeStart', function(){
if(GlobalSessionToken == ''){
GetSession();
}
console.log('START');
$rootScope.loadingView = true;
});
$rootScope.$on('$routeChangeError', function(){
console.log('ERROR');
$rootScope.loadingView = false;
});
};
controllers.CustomerController = function ($scope) {
if(GlobalSessionToken != ''){
//do something
}
}
//adding the controllers to myApp angularjs app
myApp.controller(controllers);
//controllers end here
function GetSession(){
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
}).error(function (data, status, headers, config) {
console.log(data);
});
}
And my HTML has following sections:
<body ng-app="myApp" ng-controller="MasterController">
<!--Placeholder for views-->
<div ng-view="">
</div>
</body>
How can I make sure this GetSession() is always called at the very beginning of my application start and before any other controller calls and also called only once.
EDIT: This is how I added run method as per Maxim's answer. Still need to figure out a way to wait till $http call returns before going ahead with controllers.
//Some initializing code before Angular invokes controllers
myApp.run(['$rootScope','$http', '$q', function($rootScope, $http, $q) {
return GetSession($http, $q);
}]);
function GetSession($http, $q){
var defer = $q.defer();
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
defer.resolve('done');
}).error(function (data, status, headers, config) {
console.log(data);
defer.reject();
});
return defer.promise;
}
Even though some of the solutions here are perfectly valid, resolve property of the routes definition is the way to go, in my opinion. Writing your app logic inside session.then in every controller is a bit too much , we're used such approach too in one of the projects and I didn't work so well.
The most effective way is to delay controller's instantiation with resolve, as it's a built-in solution. The only problem is that you have to add resolve property with similar code for every route definition, which leads to code duplication.
To solve this problem, you can modify your route definition objects in a helper function like this:
function withSession(routeConfig) {
routeConfig.resolve = routeConfig.resolve || {};
routeConfig.resolve.session = ['getSessionPromise', function(getSessionPromise) {
return getSessionPromise();
}]
return routeConfig;
}
And then, where define your routes like this:
$routeProvider.when('/example', withSession({
templateUrl: 'views/example.html',
controller: 'ExampleCtrl'
}));
This is one of the many solutions I've tried and liked the most since it's clean and DRY.
You can't postpone the initialisation of controllers.
You may put your controller code inside a Session promise callback:
myApp.factory( 'session', function GetSession($http, $q){
var defer = $q.defer();
$http({
url: GetSessionTokenWebMethod,
method: "POST",
data: "{}",
headers: { 'Content-Type': 'application/json' }
}).success(function (data, status, headers, config) {
GlobalSessionToken = data;
defer.resolve('done');
}).error(function (data, status, headers, config) {
console.log(data);
defer.reject();
});
return defer.promise;
} );
myApp.controller( 'ctrl', function($scope,session) {
session.then( function() {
//$scope.whatever ...
} );
} );
Alternative: If you don't want to use such callbacks, you could have your session request synchronous, but that would be a terrible thing to do.
You have not provided any details related to GetSession. For scenarios like this you should use the resolve property while defining your routes in $routeProvider. I see you are using resolve already.
What you can do now is to wrap the GlobalSessionToken into a Angular service like GlobalSessionTokenServiceand call it in the resolve to get the token before the route loads. Like
resolve: {
loadData: function($q){
return LoadData2($q,'home');
},
GlobalSessionToken: function(GlobalSessionTokenService) {
return GlobalSessionTokenService.getToken() //This should return promise
}
}
This can then be injected in your controller with
controllers.MasterController = function($rootScope, $http,GlobalSessionToken){