$http.post('http://localhost:7001/v1/sessions', {
data: {
username: $scope.user.username,
password: $scope.user.password,
type: 'sessions'
}
})
.then(function(response) {
if(response.data.data.token) {
$http.defaults.headers.common.Authorization = response.data.data.token;
$state.go('app.dashboard');
} else {
$scope.authError = response;
}
}, function(x) {
$scope.authError = 'Server Error';
});
I can confirm that the if condition gets called and a response.data.data.token is present.
It goes to the app.dashboard state but is intercepted by my ui-router:
$stateProvider.state('app', {
abstract: true,
url: '/app',
templateUrl: 'tpl/app.html',
resolve: {
current_user: ['$http', function($http) {
return $http.get('http://localhost:7001/v1/users/4/entities');
}]
}
})
That call, however, does not have anything set in the header. I thought that $http.defaults would set a default value in the header. What am I doing incorrectly?
You must set the default headers in the config method and not in your service.
Example:
myApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.headers.common['Content-Type'] = 'application/json; charset=utf-8';
}]);
Only in config you can configure the httpProvider. If you try to do that inside your service, it won't affect the $httpProvider service at all.
EDIT:
You must make use Interceptors in this scenario.
For purposes of global error handling, authentication, or any kind of
synchronous or asynchronous pre-processing of request or
postprocessing of responses, it is desirable to be able to intercept
requests before they are handed to the server and responses before
they are handed over to the application code that initiated these
requests.
Refer Angular Docs Interceptor section
Just some sample code:
app.service('APIInterceptor', function($rootScope, UserService) {
var service = this;
service.request = function(config) {
// check if the token is available. Once the token is available get it here from the UserService.
var access_token = UserService.getToken() || "unauthorized";
if (access_token) {
config.headers.authorization = access_token;
}
return config;
};
service.responseError = function(response) {
return response;
};
})
In your config
$httpProvider.interceptors.push('APIInterceptor');
I would prefered you to one service to use sharable data.
Code
app.service(dataService, function(){
this.data = {}
this.getData = function(){
return data;
};
this.setTokenData = function(token){
data.token = token;
}
});
Now your code would be while setting token you could use dataService
if(response.data.data.token) {
dataService.setTokenData(response.data.data.token);
$http.defaults.headers.common.Authorization = dataService.data.token; //dataService.getData().token;
$state.go('app.dashboard');
} else {
$scope.authError = response;
}
Then from service resolve you could use
$stateProvider.state('app', {
abstract: true,
url: '/app',
templateUrl: 'tpl/app.html',
resolve: {
current_user: ['$http', 'dataService', function($http, dataService) {
$http.defaults.headers.common.Authorization = dataService.getData().token;
return $http.get('http://localhost:7001/v1/users/4/entities');
}]
}
})
Related
I have 2 modules that should be connected.
The main module, called mainPage has the second module, called router, injected. They are in separate files. I want to use my service called userPropagatorService in mainPage and in router.
This service should be used to get and set currently logged in user.
I tried to inject service to router module, but I get errors.
How can I achieve this?
mainPage file:
var app = angular.module('mainPage',['reg','router']);
//Returns a promise which generates our user.
app.factory('userLoginService',['$http',function ($http){
return{
loginService: function(username,password){
var info = {username:username, password:password}
var user={};
//Returning http request because of promises
return $http({
url: 'webapi/users/login',
method: 'POST',
data: info,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
transformRequest: function(obj) {
var str = [];
for(var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data:{username: info.username, password: info.password}
}).then(function (response)
{
user = response.data
return user;
});
}
}
}]);
app.service('userPropagatorService', function(){
return{
get:function(){
return this.u;
},
set:function(user){
this.u = user;
}
}
});
router file:
var r = angular.module('router',['ngRoute'])
.config(['$routeProvider', '$locationProvider','userPropagatorService',
function($routeProvider, $locationProvider, userPropagatorService) {
$locationProvider.html5Mode(true);
$routeProvider
.when("/home",{
templateUrl: "pages/MainPage.html",
controller:"homeController"
})
.when("/forums",{
templateUrl: "pages/forumsPage.html",
controller:"forumController"
})
.when("/topics",{
templateUrl: "pages/topicsPage.html",
controller:"topicsController"
})
.when("/comments",{
templateUrl: "pages/commentsPage.html",
controller:"commentsController"
})
.otherwise({
redirectTo:"/home"
});
}])
.controller("homeController",['$scope','$http',function($scope,$http){
/*$http({
url: "webapi/forums/all",
method:"GET"
})
.then(function(response){
console.log("YEA!");
console.log(response.data);
},
function(response){
console.log("NO:(");
})*/
$scope.username = "visitor!"
$scope.user = userPropagatorService.get();
if($scope.user != null){
$scope.username=$scope.user.username + "!";
}
}])
.controller("forumController",['$scope','$http',function($scope,$http){
$scope.username = "visitor!"
}])
.controller("commentsController",['$scope','$http',function($scope,$http){
$scope.username = "visitor!"
}]);
If you want to use the userLoginService in the router module, it needs to be broken out of the main app.
var app = angular.module('mainPage',['reg','router']);
angular.module("services",[])
.factory('userLoginService',['$http',function ($http){
return{
//Service code here
};
}]);
Then add it as a dependency in the router module:
var r = angular.module('router',['ngRoute','services'])
You cant inject Service "userPropagatorService" in config block.
Make it a provider with $method and return function from there .
ang.provider('userPropagatorService', function(){
return{
get:function(){
console.log("in get");
},
set:function(user){
},
$get: function(){
return {
meth1: function(){
}
}
}
}
});
ang.config(function(userPropagatorServiceProvider){
console.log(userPropagatorServiceProvider.meth1())
})
I`m trying to make a request to an API server with $resource.
I want to make a post but angular turns post method into options and give an error like
OPTIONS http: / /l ocalhost/API.DWS/api/v1/user/login
XMLHttpRequest cannot load http:/ / localhost/API.DWS/api/v1/user/login. Response for preflight has invalid HTTP status code 405
var objectMethods = {
get: { method: 'GET' },
update: { method: 'PUT' },
create: { method: 'POST' },
remove: { method: 'DELETE' },
patch: { method: 'PATCH' }
};
var apiUrl = "http://localhost/API.DWS";
angular.module('nurby.version.services', [])
.config(function ($httpProvider) {
})
.factory('LoginService', ['$resource', '$http', function ($resource, $http) {
return $resource(apiUrl + "/api/v1/user/login", {},objectMethods);
}])
.controller('LogInController', ['$scope', '$rootScope', '$location','LoginService', '$http', function ($scope, $rootScope, $location, LoginService, $http) {
$scope.login = function (model) {
var loginObject = { Username: model.username, Password: model.password };
$http.defaults.useXDomain = true;
$http.defaults.headers['Content-Type'] = 'application/json';
$http.defaults.headers['Access-Control-Allow-Origin'] = '*';
LoginService.create({}, loginObject, function (data) {
if (data) {
toastr.success("itworks");
}
else {
toastr.error("not working")
}
})
}
}]);
you can define service.js and use it like below:
var APP_NAME = 'app';
angular.module(APP_NAME).service('WebService', ["$http", function ($http) {
this.login = function (parameters,callbackFunc)
{
$http({
url: 'api/login',
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: $.param(parameters)
}).success(function (data) {
callbackFunc(data);
}).error(function (data) {
callbackFunc([]);
});
};
and use it in your controller like below:
LoginController = ['$scope', '$http', '$location', 'WebService','$window', function ($scope, $http, $location,$WebService,$window) {
$scope.login = function(admin){
var data = {email:admin.email,password:admin.password};
$WebService.login(data,function(result){
if(result.success){
$window.location.replace("index");
}
else{
$scope.loginError = result.fail;
}
});
}
}];
The problem here is that you are specifying a complete URL beginning "http://localhost/API.DWS" and you haven't loaded the web page from the same domain (maybe you used a different port?).
This means the browser sees your request as a Cross-Domain request. It therefore sends an OPTIONS request first to ask the server whether it will permit you to send the POST. You could configure your server to respond correctly to these requests, or change your code so the web page and the api are on the same domain.
How to configure your server will depend on which server you are running. Search for CORS and your web server and you should find useful information.
Inside my controller this worked for me
var resource = $resource(
"your_api_url",
{
callback: "JSON_CALLBACK"
},
{
getData: {
method: "JSONP",
isArray: false
}
}
);
function loadRemoteData() {
$scope.isLoading = true;
resource.getData().$promise.then(
function( friends ) {
$scope.isLoading = false;
},
function( error ) {
// If something goes wrong with a JSONP request in AngularJS,
// the status code is always reported as a "0". As such, it's
// a bit of black-box, programmatically speaking.
alert( "Something went wrong!" );
}
);
}
$scope.searchResources = function() {
$scope.isLoading = true;
resource.getData().$promise.then(
function( friends ) {
$scope.isLoading = false;
},
function( error ) {
// If something goes wrong with a JSONP request in AngularJS,
// the status code is always reported as a "0". As such, it's
// a bit of black-box, programmatically speaking.
alert( "Something went wrong!" );
}
);
};
I have following controller
1) introCtrl
2) ArticleCtrl
3) articleService (Service)
Now I am sending an http request from introCrtl
.controller('IntroCtrl', function($scope, articleService) {
articleService.getArticles();
});
and AricleCtrl is
.controller('ArticleCtrl', function($scope,$rootScope,articleService) {
$scope.articles = articleService.fetchArticles();
})
and my Service is
.service('articleService', function ($http, $q) {
var articleList = [];
var getArticles = function() {
$http({
url: "muylink,co,",
data: { starLimit: 0, endLimit: 150,created_date: 0 },
method: 'POST',
withCredentials: true,
}).success(function (data, status, headers, config) {
articleList.push(data);
}).error(function (err) {
console.log(err);
})
};
var fetchArticles = function() {
return articleList[0];
}
return {
getArticles: getArticles,
fetchArticles: fetchArticles
};
});
Which is also working fine. Now Problem is that
Sometimes my http request sending respone late and i got nothing in
$scope.articles.
Can we implement watch here. How i need to implement $watch here. I dont want to implement promise. because i want to run http request behind the scene.
Thanks
It would be better if you switch to a state based setup with ui-router that way you can do this :
$stateProvider.state('myState', {
url: 'the/url/you/want',
resolve:{
articleService: 'articleService' // you are dependency injecting it here,
articles: function (articleService) {
return articleService.getArticles.$promise;
}
},
controller: 'IntroCtrl'
})
// then your controller can just inject the articles and they will be resolved before your controller loads so you it will always be fetched prior
.controller('IntroCtrl', function($scope, articles) {
$scope.articles = articles;
});
for more information take a look at this
ui-router info
All to do is set watch on articleList and provide maintaining function.
As you are watching array, it's good to change it to string.
Create function in watch which results array.
$scope.$watch( function() {
return JSON.stringify($scope.articleList);
}, function(newVal,oldVal){
//provide logic here
});
If your service result is asynchron (like http requests) you should return promises from your service.
.controller('ArticleCtrl', function($scope,$rootScope,articleService) {
articleService.fetchArticles().then(function(articles) {
$scope.articles = articles;
});
})
Service
// not sure about your service logic... simplified:
.service('articleService', function ($http, $q) {
var articleListPromise ;
var getArticles = function() {
articleListPromise = $http(/* ...*/);
};
var fetchArticles = function() {
return articleListPromise.then(function(data) {
return data[0];
});
}
return {
getArticles: getArticles,
fetchArticles: fetchArticles
};
});
I'm not quite sure what I'm doing wrong, but it seems that my profile doesn't resolve by the time we get to the MainCtrl. The user however does, resolve. Am I, perhaps not fetching the profile information properly in the Auth Service?
Router:
angular.module('app')
.config(function ($stateProvide) {
$stateProvider
.state('main', {
url: '/main',
templateUrl: 'app/main/main',
controller: 'MainCtrl',
resolve: {
user: function (Auth) {
return Auth.getUser();
},
profile: function (user) {
return Auth.getProfile();
}
}
});
});
Controller:
angular.module('app')
.controller('MainCtrl', function ($scope, user, profile) {
$scope.user = user;
$scope.profile = profile; <- DOESNT RESOLVE
});
Auth Service:
angular.module('app')
.factory('Auth', function ($firebaseSimpleLogin, $firebase, FBURL) {
var ref = new Firebase(FBURL);
var auth = $firebaseSimpleLogin(ref);
var Auth = {
user: {},
getUser: function () {
return auth.$getCurrentUser();
},
getProfile: function(uid) {
return $firebase(ref.child('users').child(uid)).$asObject();
}
};
return Auth;
});
Something like
auth.$getCurrentUser()
returns a promise so you need a
.then(function(user) {
event before your callback complete
In your case you may just resolve on the then, something like
Auth.getUser().then(function(user){ return user; });
Also $asObject() needs $loaded() for it's promise
var obj = $firebase(ref).$asObject();
obj.$loaded()
.then(function(data) {})
Try this structure for your promises:
var fetchSomething = function (action, params) {
var promise = $http({
method: 'POST',
url: 'someurl to the firebase',
data: params,
headers: {
'Access-Control-Allow-Origin': true,
'Content-Type': 'application/json'
}
}).success(function (data, status, headers, config) {
return data;
});
return promise;
};
i want to implement an authentication/authorization system in angularjs, i found a couple of tutorials, and a lot of them using $routeChangeStart event and testing for a specific pages, and then ask a service to test if this user is authenticated by sending the token to the api.
and that's what i have done, but i didn't get the results that i need.
app.js
var app = angular.module("KhbyraApp", ['ngRoute', 'ngCookies']);
app.config(function ($routeProvider, $locationProvider, $httpProvider) {
$routeProvider
.when("/Register", {
controller: "RegisterController",
templateUrl: "/app/views/register.html",
authenticate : false
})
.when("/Login", {
controller: "LoginController",
templateUrl: "app/views/login.html",
authenticate: false,
})
.when("/Articles", {
controller: "ArticlesController",
templateUrl: "app/views/article.html",
authenticate: true
})
.otherwise({ redirectTo: '/Login' });
$locationProvider.html5Mode(true);
});
app.run(function ($rootScope, $location, $cookieStore, AuthService) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
if (next.authenticate) {
if (!AuthService.isAuthenticated()) {
$location.url("/Login");
};
};
});
AuthService.js
app.factory('AuthService', function ($http, $q, $window, $cookieStore) {
var factory = {};
var loginUrl = 'http://localhost:2399/Token';
var authUrl = 'http://localhost:2399/Authenticate';
var email;
var token;
factory.Authenticate = function (email, password) {
console.log("AuthService -" + email);
var deferred = $q.defer();
var user = {
email: window.btoa(email),
password: window.btoa(password)
};
$http({
method: 'POST',
url: loginUrl,
data: user
}).success(function (data) {
console.log("AuthService - " + data);
token = data.replace('"', '').replace('"', '');
email = user.email;
deferred.resolve(token);
$cookieStore.put('token', token);
$cookieStore.put('email',user.email);
}).error(function () {
deferred.reject();
});
return deferred.promise;
};
factory.Email = email;
factory.Token = token;
factory.isAuthenticated = function () {
var request = $http({
method: 'GET',
url: authUrl,
headers: { 'Authorization': 'Token '+ $cookieStore.get('email') + ":"+ $cookieStore.get('token') }
}).then(function () {
return true;
}
,function () {
return false;
});
};
return factory;
});
the problem here is in the routeChangeStart even if the AuthService.isAuthenticated() returns true, in the if statements something goes wrong, i think it's about $http returns a promise.
It's normal that the route is loaded even if you're condition fails : Angular doesn't rollback and go back to the previous page. The RouteChangeStart event is actually called just after the URL is changed, when angular detects it.. but the redirection has already been made (there is actually no BeforeRouteChange event)
So you'll have to handle it yourself depending on your needs. For example, in this case, you'll typically force a redirection to the login page. YOu could also display a login popup on top of your page and wait for the login to be successfull to re-execute the previously failed request (which should now works because you're are now logged again). This behavior is exaclty the one of the http-auth-interceptor.
See also angular-app which implements something similar (based on http-auth-interceptor also)