CordovaHttp and Angular Post - no response - angularjs

I'm using CordovaHTTP with Angular and injected the Cordova HTTP into a service. I haven't found many examples on how to implement a POST so below is what I did so far. The issue I'm having is that the post block never reaches the success or error blocks and my debug statements are not getting printed.
Does this look correct?
Calling function:
this.authenticate = function ( code, data, callback ) {
try {
// Build url
var url = o.buildServerUrl(o.loginUrl, code);
RestService.post(url, data, function(response) {
if (response.status === o.HTTP_STATUS_OK) {
...
}
callback(response);
});
}
catch(err) {
var response = o.createServerErrorResponse(o.MSG_SERVER_ERROR);
callback(response);
}
}
Service:
oApp.service( 'RestService', function( cordovaHTTP ) {
this.post = function ( url, data, callback ) {
try {
// Build url
if (o.debug) console.log('Cordova REST: '+url);
cordovaHTTP.post( url, data, {}, function(response) {
if (o.debug) console.log('Rest ok');
// Success
var response = o.processServerResponse(response);
callback(response);
}, function(response) {
if (o.debug) console.log('Response error');
var response = o.processCordovaServerResponse(response);
callback(response);
});
}
catch(err) {
var response = o.createExceptionResponse(err.message);
callback(response);
}
}
});

Related

Angularjs interceptor: Interceptor in not work

Hi this below is my code from angularjs
The first part of the code shows exactly how I make the request to php which returns me data when there is code with an error when there is a problem.
When it returns me an error code, for example 401, the frontend explodes with an error and everything breaks.
As far as I understand, I have to use an interceptor so that I can catch the error number and not make an error.
I can't do this interception.
app.js
app.factory('httpRequester', function ($http, $route) {
var promise;
var myService = {
async: function (myData) {
//Create my obj for request
var req = {
method: myData.Method,
url: 'https://**********/' + url,
headers: {
'X-API-KEY': '**************',
'KEY': myData.Authentication.AccessToken
},
data: myData
};
promise = $http(req).then(function (response) {
if (response.data === 'permission denied') {
window.location.href = '#/';
} else {
return response;
}
});
return promise;
}
};
return myService;
});
//set interceptor in app.js
var interceptorApp = angular.module('interceptorApp', []);
interceptorApp.config(function($httpProvider) {
$httpProvider.interceptors.push('genericInterceptor');
});
Please help me with my problem, how to set interceptor work.
//Function for set error and success after request
var testInterceptor = function ($q)
{
return {
request: function (config)
{
//console.log('request started...');
},
requestError: function (rejection)
{
console.log(rejection);
// Contains the data about the error on the request and return the promise rejection.
return $q.reject(rejection);
},
response: function (result)
{
// console.log('data for ' + result.data.name + ' received');
//If some manipulation of result is required before assigning to scope.
result["testKey"] = 'testValue';
console.log('request completed');
return result;
},
responseError: function (response)
{
// console.log('response error started...');
//Check different response status and do the necessary actions 400, 401, 403,401, or 500 eror
return $q.reject(response);
}
}
}

How to intercept convenience shortcuts of $http in AngularJS

I have the following function (credit) that wraps an AngularJS $http function in a way that it invokes browser XHR when running on desktop, but invokes cordova-plugin-advanced-http if on mobile.
It seems that this works when I use $http({method:'get/post'}...) but doesn't work if I call the convenience shortcuts like $http.get(...)
Can someone suggest what modification I need to make?
$provide.decorator('$http', ['$delegate', '$q', function($delegate, $q) {
// create function which overrides $http function
var $http = $delegate;
var wrapper = function () {
var url = arguments[0].url;
var method = arguments[0].method;
var isOutgoingRequest = /^(http|https):\/\//.test(url);
if (window.cordova && isOutgoingRequest) {
console.log ("**** -->"+method+"<-- using native HTTP with:"+url);
var d = $q.defer();
var options = {
method: method,
data: arguments[0].data,
headers: arguments[0].headers,
timeout: arguments[0].timeout
};
cordova.plugin.http.sendRequest(url,options,
function (succ) {
console.log ("*** Inside native HTTP success with:"+JSON.stringify(succ));
try {
if (options.headers && options.headers['x-parse']=='text')
d.resolve({"data":succ.data});
else
d.resolve({"data":JSON.parse(succ.data)});
return d.promise;
}
catch (e) {
d.resolve({"data":succ.data});
return d.promise;
}
},
function (err) {
console.log ("*** Inside native HTTP error");
d.reject(err);
return d.promise;
});
return d.promise;
}
else {
console.log ("**** "+method+" using XHR HTTP for "+url);
return $http.apply($http, arguments);
}
};
Object.keys($http).filter(function (key) {
return (typeof $http[key] === 'function');
}).forEach(function (key) {
wrapper[key] = function () {
// Apply global changes to arguments, or perform other
// nefarious acts.
// console.log ("KEY="+key);
return $http[key].apply($http, arguments);
};
});
return wrapper;
}]);
If I understood your intent correctly, the way you're assigning the HTTP methods that hang off wrapper won't invoke the contents of your wrapper function.
Note that the parameters of the $http convenience functions vary.
Examples:
GET is described as: get(url, [config])
POST is described as: post(url, data, [config])
With the above in mind, here's one way of delegating back to your wrapper function that switches between XHR and the Cordova plugin when the $http convenience methods are used:
wrapper[key] = function () {
var url = arguments[0];
if (['get', 'delete', 'head', 'jsonp'].indexOf(key) !== -1) {
// arguments[1] == config
return wrapper(Object.assign({
method: key,
url: url,
}, arguments[1]));
} else {
// POST, PUT, PATCH
// arguments[1] == data
// arguments[2] == config
return wrapper(Object.assign({
data: arguments[1],
method: key,
url: url,
}, arguments[2]));
}
};
Here is a working solution I eventually arrived at.
// Wraps around $http that switches between browser XHR
// or cordova-advanced-http based on if cordova is available
// credits:
// a) https://www.exratione.com/2013/08/angularjs-wrapping-http-for-fun-and-profit/
// b) https://gist.github.com/adamreisnz/354364e2a58786e2be71
$provide.decorator('$http', ['$delegate', '$q', function($delegate, $q) {
// create function which overrides $http function
var $http = $delegate;
var wrapper = function () {
var url;
var method;
url = arguments[0].url;
method = arguments[0].method;
var isOutgoingRequest = /^(http|https):\/\//.test(url);
if (window.cordova && isOutgoingRequest) {
console.log ("**** -->"+method+"<-- using native HTTP with:"+encodeURI(url));
var d = $q.defer();
var options = {
method: method,
data: arguments[0].data,
headers: arguments[0].headers,
timeout: arguments[0].timeout,
responseType: arguments[0].responseType
};
cordova.plugin.http.sendRequest(encodeURI(url),options,
function (succ) {
// automatic JSON parse if no responseType: text
// fall back to text if JSON parse fails too
if (options.responseType =='text') {
// don't parse into JSON
d.resolve({"data":succ.data});
return d.promise;
}
else {
try {
d.resolve({"data":JSON.parse(succ.data)});
return d.promise;
}
catch (e) {
console.log ("*** Native HTTP response: JSON parsing failed for "+url+", returning text");
d.resolve({"data":succ.data});
return d.promise;
}
}
},
function (err) {
console.log ("*** Inside native HTTP error: "+JSON.stringify(err));
d.reject(err);
return d.promise;
});
return d.promise;
}
else { // not cordova, so lets go back to default http
console.log ("**** "+method+" using XHR HTTP for "+url);
return $http.apply($http, arguments);
}
};
// wrap around all HTTP methods
Object.keys($http).filter(function (key) {
return (typeof $http[key] === 'function');
}).forEach(function (key) {
wrapper[key] = function () {
return $http[key].apply($http, arguments);
};
});
// wrap convenience functions
$delegate.get = function (url,config) {
return wrapper(angular.extend(config || {}, {
method: 'get',
url: url
}));
};
$delegate.post = function (url,data,config) {
return wrapper(angular.extend(config || {}, {
method: 'post',
url: url,
data:data
}));
};
$delegate.delete = function (url,config) {
return wrapper(angular.extend(config || {}, {
method: 'delete',
url: url
}));
};
return wrapper;
}]);

400 bad request on post

Trying to send a request from my factory to the back end, getting the following error: POST http://localhost:8000/messages/58d4b22d57f49028608f7bf9 400 (Bad Request)
Factory:
app.factory('usersFactory', function ($http) {
var factory = {};
var current_user = [];
factory.login = function (data, callback) {
$http.post('/users/', data)
.then(function (response) {
current_user.push(response.data);
callback(current_user);
})
};
factory.getUser = function (callback) {
callback(current_user);
}
factory.destroy = function (callback) {
current_user = [];
callback();
}
factory.writePost = function (data) {
console.log(data);
console.log(current_user[0]._id)
$http.post('/messages/' + current_user[0]._id, data)
.then(function (response) {
$location.url('/wall');
})
}
return factory;
});
Server routes:
var wall = require('./../controllers/serverController.js')
module.exports = function(app){
app.post('/users/', function (request, response) {
wall.login(request, response);
});
app.post('/messsage/:id', function (request, response) {
wall.writeMessage(request, response);
})
}
Sever controller:
module.exports =
{
writeMessage: function (request, response) {
User.findOne({ _id: request.params.id }, function (err, user) {
var message = new Message({ message: request.body, _user: request.params.id });
message.save(function (err) {
user.messages.push(message);
user.save(function (err) {
if (err) {
response.json(err);
}
})
})
})
}
}
this is error of sever side not angular, try to check logs of server.
Also you are using Message , do have schema imported in that ?

Validation on POST request

I am using angular and talking to an express backend. I can retrieve data from my .get, but my .post is generating a validation error
Client-controller:
$scope.addFriend = function()
{
friendsFactory.addFriend($scope.newFriend, function (data)
{
$location.url('/friends' + data._id);
});
}
Client-factory:
factory.addFriend = function (newFriendData, callback) {
$http.post('/friends', newFriendData)
.then(function(response)
{
console.log(response.data);
//callback(response.data);
})
}
Server-route:
app.post('/friends', function (request, response) {
console.log('routes')
friends.create(request, response);
})
Server-controller:
create: function(request, response)
{
console.log('request');
var friendInstance = new Friend();
friendInstance.first_name = request.params.fname;
friendInstance.last_name = request.params.lname;
friendInstance.b_day = request.params.bday;
friendInstance.save(function(err,data)
{
if (err)
{
response.json(err);
}
else {
rewponse.json(data);
}
})
Error on console:
Object {errors: Object, message: "Friend validation failed", name: "ValidationError"}
this is most likely a mongoose error, the document that you're trying to persist does not follow the Friend schema.

How do I get the HTTP response status code in AngularJS 1.2

Using ngResource in AngularJS 1.2rc(x), how do I get the status code now?
RestAPI.save({resource}, {data}, function( response, responseHeaders ) {
});
where RestAPI is my ngResource.
The response has the $promise object and the resource returned from the server but not a status anymore. The responseHeaders() function only has a status if the server injects the status code into the header object, but not the true returned status code. So some servers may serve it and some might not.
You can use the promiss callbacks then, catch and finally after the $resource call.
For example. If you want to catch an error after a call, you would do something like this:
RestAPI.save({resource}, {data}, callbackFunction).$promise.catch(function(response) {
//this will be fired upon error
if(response.status == 500) alert('Something baaad happend');
}).then(function() {
//this will be fired upon success
});
The response object will have status and the statusText properties. status being an integer status code and statusText the text. You'll also have the data property containing the server response.
edit: as suggested, it was response.status
You must add an interceptor inside your resource declaration. Like this:
var resource = $resource(url, {}, {
get: {
method: 'GET'
interceptor: {
response: function(response) {
var result = response.resource;
result.$status = response.status;
return result;
}
}
}
});
Usage:
resource.get(params, function(result) {
console.log(result.$status)
});
IMO status code should have been provided by default.
There is an issue for this https://github.com/angular/angular.js/issues/8341
For anyone using a newer version of Angular, looks like we've had access to the status code as a 3rd param to the transformResponse function since angular 1.3, but it was never documented properly in the $resource docs.
I agreed responseHeaders() function will only return response's header,but you can custom it and it's useful anyway.
1.
To solve you problem. With the following:($$service is my $resource instance.)
var serve = new $$service();
serve.id = "hello_wrongPath"; // wrong path,will return 404
serve.$get()
.then(function (data) {
console.log("~~~hi~~~");
console.log(data);
return data;
})
.catch(function (error) {
console.log("~~~error~~~");
console.log(error);
console.log(error.status); // --> 404
console.log(error.statusText); // --> "Not Found"
console.log(error.config.timeout); // --> 5000
console.log(error.config.method); // --> GET
console.log(error.config.url); // --> request url
console.log(error.headers("content-type"));// --> "text/plain"
return error.$promise;
})
.finally(function(data){
console.log("~~~finally~~~");
console.log(data); // --> undefined
});
In this way,u can only catch status,statusText,timeout,method,headers(same with responseHeaders) in ERROR response.
2.
If you want to see response details in success response,I used a interceptor like this:
ng.module("baseInterceptor", [])
.factory("baseInterceptor", ["$q", function ($q) {
return {
'request': function (config) {
console.info(config);
//set timeout for all request
config.timeout = 5000;
return config;
},
'requestError': function (rejection) {
console.info(rejection);
return $q.reject(rejection);
},
'response': function (response) {
console.log("~~interceptor response success~~");
console.log(response);
console.log(response.status);
console.log(response.config.url);
return response;
},
'responseError': function (rejection) {
console.log("~~interceptor response error~~");
console.log(rejection);
console.log(rejection.status);
return $q.reject(rejection);
}
};
}]);
and then add interceptor to module:
.config(["$httpProvider", function ($httpProvider) {
$httpProvider.interceptors.push("baseInterceptor");
}])
You can get response status like this:
$http.get(url).then(function(response){
console.log(response.status); //successful status like OK
}, function(response){
console.log(response.status); //error status like 400-Bad Request
})
I'm using AngularJS v1.5.6, and I do it like this (in my case I put the "getData" method inside a service):
function getData(url) {
return $q(function (resolve, reject) {
$http.get(url).then(success, error);
function success(response) {
resolve(response);
}
function error(err) {
reject(err);
}
});
}
then in the controller (for example), call that like this:
function sendGetRequest() {
var promise = service.getData("someUrlGetService");
promise.then(function(response) {
//do something with the response data
console.log(response.data);
}, function(response) {
//do something with the error
console.log('Error status: ' + response.status);
});
}
As documentation says, 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.
See https://docs.angularjs.org/api/ng/service/$http
Hope it helps!
I think the right answer is a combination of Bardiel's and Ara's answers.
After adding an interceptor inside your resource declaration. Like this:
var resource = $resource(url, {}, {
get: {
method: 'GET'
interceptor: {
response: function(response) {
var result = response.resource;
result.$status = response.status;
return result;
}
}
}
});
Use it as below:
RestAPI.save()
.query(function(response) {
// This will return status code from API like 200, 201 etc
console.log(response.$status);
})
.$promise.catch(function(response) {
// This will return status code from server side like 404, 500 etc
console.log(response.status);
});
I had faced the similar problem.I looked into the angular lib and added a few lines to have status returned in the response itself.In this file,find where promise is being returned.
Replace code block starting with
var promise = $http(httpConfig).then(function(response)
with the following
var promise = $http(httpConfig).then(function(response) {
var data = response.data,
promise = value.$promise;
if (data) {
// Need to convert action.isArray to boolean in case it is undefined
// jshint -W018
if ( angular.isArray(data) !== (!!action.isArray) ) {
throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' +
'response to contain an {0} but got an {1}',
action.isArray?'array':'object', angular.isArray(data)?'array':'object');
}
// jshint +W018
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
value.push(new Resource(item));
});
} else {
copy(data, value);
value.$promise = promise;
}
}
value.status = response.status;
value.$resolved = true;
response.resource = value;
return response;
}, function(response) {
value.status = response.status;
value.$resolved = true;
(error||noop)(response);
return $q.reject(response);
});
or you can add this line
value.status = response.status;
and then access status in code like reponse.status.Though,this is kind of hack but worked for me.I also had to make changes in the minified version.

Resources