How to pass Request Body from AngularJS with API - javascript

I am trying to use json using API to retrieve data on a Google Map in AngularJS.
This is my code in Angular:
$scope.loadData = function () {
var map_info = {
'ApiKey': '1iVuQy3FGK39d51',
'ProjectId': '11'
};
var url = "http://localhost:63411/api/clientportal/?action=mapjson";
return $http.post(url, map_info).then(function (response) {
return response.data.MapData;
});
};
But, when I run the code, it shown 'Cross-Origin Reuest Block' error.
Then, I search from the internet about this error, I come out with a solution to change $http.post to $http.jsonp.
$scope.loadData = function () {
var map_info = {
'ApiKey': '1iVuQy3FGK39d51',
'ProjectId': '11'
};
var url = "http://localhost:63411/api/clientportal/?action=mapjson";
return $http.jsonp(url, map_info).then(function (response) {
return response.data.MapData;
});
};
It does go to the URL, however the Request Body is empty.
Since I created the API using C#, the 'Request.InputStream' is null.
string jsonPosted = new StreamReader(Request.InputStream).ReadToEnd();
So the 'jsonPosted' is null.
I did try on Postman, and it works. But not in Angular.
How do I send the Request Body properly?
I want the 'ApiKey' and 'ProjectId' to be included on Request Body.
Thank You.

Related

How to send JSON data correctly using Axios to a rails server, to match the required rails params hash correctly?

I am making a GET request to a rails server, and the parameter should look like:
{"where"=>{"producer_id"=>["7"]}
I am making the request from the frontend application which is in Vue, and using Axios for making the request. I am making the request like this:
const data = await this.axios.get('http://localhost:3000/data.json', {
headers: {
'X-User-Token': this.$store.getters.authToken,
'X-User-Username': this.$store.getters.user.username
},
params: {
where: {
producer_id: data.producers
}
}
})
However, in the rails server output it shows that the params were sent like this:
{"where"=>"{\"producer_id\":[\"7\"]}"}
And I don't get the correct data back because of it.
How can I solve this? Why is the second level in params (the where object) being sent as a string?
Turns out that in this case the params have to be serialized https://github.com/axios/axios/issues/738
I used the paramsSerializer function as well to get over this
const data = await this.axios.get('http://localhost:3000/data.json', {
headers: {
'X-User-Token': this.$store.getters.authToken,
'X-User-Username': this.$store.getters.user.username
},
params: {
where: {
producer_id: data.producers
}
},
paramsSerializer: function (params) {
return jQuery.param(params)
}
})
EDIT:
I am now using qs instead of jQuery:
axios.defaults.paramsSerializer = (params) => {
return qs.stringify(params, {arrayFormat: 'brackets'})
}

How should I extract value from Url in Node Js

I recently started programming on nodeJs.
I am using Angular JS, resource to call API's as
demoApp.factory('class', function ($resource) {
return $resource('/class/:classId', { classId: '#_classId' }, {
update: { method: 'PUT' }
});
});
And in Controller, I have delete method as;
// The class object, e {classId: 1, className: "Pro"}
$scope.deleteClass = function (class) {
var deleteObj = new Class();
deleteObj.classId = class.classId;
deleteObj.$delete({classId : deleteObj.classId}, function() {
growl.success("Class deleted successfully.");
$location.path('/');
},function () {
growl.error("Error while deleting Class.");
}
);
};
Using browser, I verified call goes to :
http://localhost:3000/class/1
Now in node Js, How should I extract value from Url,
In server.js
app.use('/class', classController.getApi);
In classController.js
exports.getApi = function(req, resp){
switch(req.method) {
case 'DELETE':
if (req) {
// how to extract 1 from url.
}
else {
httpMsgs.show404(req, resp);
}
break;
I have tried ,
console.log(req.params);
console.log(req.query);
But no luck.
I am seeing
console.log(req._parsedUrl);
query: null,
pathname: '/class/1',
path: '/class/1',
Any help appreciated.
This should be a get call right ? You can use angular $http service, with method as get. Replace your app.use('/class') with app.get('/class', function). Then you can use req.param('classId') to retrieve data. I think it should work.
Try updating your app.use to app.use('/class/:classId'), then try req.params.classId
Try using req.hostname as in:
`http://host/path'
Check this answer.
Tldr;
var url = require('url');
var url_parts = url.parse(request.url, true);
var query = url_parts.query;
Also read the docs on node url.

Unable to POST data in AngularJS

I am building a small application for fetching data from one JSON file and posting it to another JSON file. I am using AngularJS and ngResource and $http services to achieve the functionality.
I was able to read data using GET method, but POST is not posting data to my another JSON file.
Below are my controller and services definitions:
'use strict';
/* Controllers */
var boookbAppControllers = angular.module('boookbAppControllers', []);
boookbAppControllers.controller('boookbAppCtrl', ['$scope','$http','Book',
function($scope,$http,Book) {
$scope.way=["Normal","$http","RestFul"]
$http.get('data/books.json').success(function(data) {
$scope.b1 = data;
$scope.addBook1=function(){
$http.post('data/newbooks.json',{ "catalogRefIds" : $scope.books[0].title, "productId" : $scope.books[0].author}).then(function(response,status){
$scope.a=status;
alert($scope.a);
});
}
$scope.b2 = Book.query();
$scope.newBookName="";
$scope.userBook="";
$scope.userBookAuthor="";
$scope.newBookAuthor="";
$scope.bookData={};
$scope.addBook=function(){
$scope.userBook=$scope.newBookName;
$scope.userBookAuthor=$scope.newBookAuthor;
Book.add({},$scope.bookData);
}
});
}]);
'use strict';
/* Services */
var boookbAppServices = angular.module('boookbAppServices', ['ngResource']);
boookbAppServices.factory('Book', ['$resource',
function($resource){
return $resource('data/:bookId.json', {}, {
query: {method:'GET', params:{bookId:'books'}, isArray:true},
add: {method:'POST',params:{bookId:'books'},isArray: true}
});
}]);
I checked traces of POST data and no data was being sent.
I don't know what you are trying to do by POSTing to a .json file. That being said, assuming you are actually handling the POST request server side and not seeing any data, this is most likely the issue:
Angular posts data differently than jQuery, for example - it sends it over as application/json and not application/x-www-form-urlencoded. So, in PHP for example, if you check $_POST['key'] it won't find it. Your options there are either handle it server side:
$_JSON = file_get_contents("php://input");
$_JSON = json_decode($_JSON, TRUE);
echo $_JSON['key'];
Or you can transform it to form-data it client-side:
boookbAppControllers.config(function($httpProvider) {
$httpProvider.defaults.headers.post = {'Content-Type': 'application/x-www-form-urlencoded'};
$httpProvider.defaults.transformRequest = function(data) {
if (data === undefined) return data;
var clonedData = clone(data);
for (var property in clonedData)
//you could do this with hasOwnProperty probably,
//but this works unless you prepend variable keys
//with $
if (property.substr(0, 1) == '$')
delete clonedData[property];
return $.param(clonedData);
};
});
Please have a look at $http.post syntax: we need to pass data with the keyword data encapsulating actual data.
$http.post('data/newbooks.json', data : { "catalogRefIds" : $scope.books[0].title, "productId" : $scope.books[0].author}).then(function(response,status){
$scope.a=status;
alert($scope.a);
});
When posting JSON data use Content-Type as text/plain, tried and tested in angular 1.5.11
$http({
method : 'POST',
url : 'http://example.com',
headers: {
'Content-Type': 'text/plain',
},
data : JSON.stringify({data})
})

AngularJS $resource 'GET' accesses the correct API, but 'PUT' and 'POST' do not

Follow up from AngularJS $resource calls the wrong API URL when using method:POST
My controller is set up like this, with Angular's $resource:
$scope.updateProduct = $resource('/api/updateProduct/:product/:param/:value',{},{
query: {method:'GET'},
post: {method:'POST'},
save: {method:'PUT', params: {brand: '#brand', param:'#param', value:'#value'}},
remove: {method:'DELETE'}
});
$scope.updateProduct.save({
product : $scope.post._id,
param: 'likes',
value: $scope.user._id
});
My server runs on NodeJS and ExpressJS. In my console, when the save operation is called, I can see:
POST /api/updateBrand/<productid>/likes/fun,%20quirky%20loud,%20boho,%20hippy 200 22ms - 2.31kb
However, my API is not being correctly accessed. For instance, if I go to the above URL in my browser, the API function is called, and my database is updated (and it is reported in my server's console). Yet when Angular does a PUT on this URL, nothing happens at all.
Interestingly, when I change $scope.updateProduct.save() to $scope.updateProduct.get(), the API is correctly called and everything works fine.
Any ideas what's going on here?
EDIT: Here's the server setup:
ExpressJS API setup:
app.get('/api/updateProduct/:product/:param/:value', api.updateProduct);
API code
exports.updateProduct = function (req, res) {
console.log("TEST")
var product = req.params.product;
var param = req.params.param;
var value = req.params.value;
var props = { $push: {} };
if(param == 'userTags'){
var oldVal = value;
value = oldVal.match(/[-'"\w]+/g);
props.$push[param];
props.$push[param] = {$each: []};
props.$push[param].$each = value;
}else{
var props = { $push: {} };
props.$push[param] = value;
}
db.products.update({"_id": ObjectId(product)}, props, function (err, record) {
if (err || !(record)) {
console.log("Lookup Error: " + err);
} else{
console.log("Updated " + product + " with " + param);
console.log(record);
res.json({obj:record})
}
});
};
It seems that your server is not waiting for a POST or PUT request, but a GET request as per your configuration.
app.get('/api/updateProduct/:product/:param/:value', api.updateProduct);
According to the ExpressJS API (http://expressjs.com/api.html), you should be able to replace the get with any valid http verb.
app.VERB(path, [callback...], callback)
app.post('/api/updateProduct/:product/:param/:value', api.updateProduct);
app.put('/api/updateProduct/:product/:param/:value', api.updateProduct);

AngularJS: How to send auth token with $resource requests?

I want to send an auth token when requesting a resource from my API.
I did implement a service using $resource:
factory('Todo', ['$resource', function($resource) {
return $resource('http://localhost:port/todos.json', {port:":3001"} , {
query: {method: 'GET', isArray: true}
});
}])
And I have a service that stores the auth token:
factory('TokenHandler', function() {
var tokenHandler = {};
var token = "none";
tokenHandler.set = function( newToken ) {
token = newToken;
};
tokenHandler.get = function() {
return token;
};
return tokenHandler;
});
I would like to send the token from tokenHandler.get with every request send via the Todo service. I was able to send it by putting it into the call of a specific action. For example this works:
Todo.query( {access_token : tokenHandler.get()} );
But I would prefer to define the access_token as a parameter in the Todo service, as it has to be sent with every call. And to improve DRY.
But everything in the factory is executed only once, so the access_token would have to be available before defining the factory and it cant change afterwards.
Is there a way to put a dynamically updated request parameter in the service?
Thanks to Andy Joslin. I picked his idea of wrapping the resource actions. The service for the resource looks like this now:
.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
var resource = $resource('http://localhost:port/todos/:id', {
port:":3001",
id:'#id'
}, {
update: {method: 'PUT'}
});
resource = tokenHandler.wrapActions( resource, ["query", "update"] );
return resource;
}])
As you can see the resource is defined the usual way in the first place. In my example this includes a custom action called update. Afterwards the resource is overwritten by the return of the tokenHandler.wrapAction() method which takes the resource and an array of actions as parameters.
As you would expect the latter method actually wraps the actions to include the auth token in every request and returns a modified resource. So let's have a look at the code for that:
.factory('TokenHandler', function() {
var tokenHandler = {};
var token = "none";
tokenHandler.set = function( newToken ) {
token = newToken;
};
tokenHandler.get = function() {
return token;
};
// wrap given actions of a resource to send auth token with every
// request
tokenHandler.wrapActions = function( resource, actions ) {
// copy original resource
var wrappedResource = resource;
for (var i=0; i < actions.length; i++) {
tokenWrapper( wrappedResource, actions[i] );
};
// return modified copy of resource
return wrappedResource;
};
// wraps resource action to send request with auth token
var tokenWrapper = function( resource, action ) {
// copy original action
resource['_' + action] = resource[action];
// create new action wrapping the original and sending token
resource[action] = function( data, success, error){
return resource['_' + action](
angular.extend({}, data || {}, {access_token: tokenHandler.get()}),
success,
error
);
};
};
return tokenHandler;
});
As you can see the wrapActions() method creates a copy of the resource from it's parameters and loops through the actions array to call another function tokenWrapper() for every action. In the end it returns the modified copy of the resource.
The tokenWrappermethod first of all creates a copy of preexisting resource action. This copy has a trailing underscore. So query()becomes _query(). Afterwards a new method overwrites the original query() method. This new method wraps _query(), as suggested by Andy Joslin, to provide the auth token with every request send through that action.
The good thing with this approach is, that we still can use the predefined actions which come with every angularjs resource (get, query, save, etc.), without having to redefine them. And in the rest of the code (within controllers for example) we can use the default action name.
Another way is to use an HTTP interceptor which replaces a "magic" Authorization header with the current OAuth token. The code below is OAuth specific, but remedying that is a simple exercise for the reader.
// Injects an HTTP interceptor that replaces a "Bearer" authorization header
// with the current Bearer token.
module.factory('oauthHttpInterceptor', function (OAuth) {
return {
request: function (config) {
// This is just example logic, you could check the URL (for example)
if (config.headers.Authorization === 'Bearer') {
config.headers.Authorization = 'Bearer ' + btoa(OAuth.accessToken);
}
return config;
}
};
});
module.config(function ($httpProvider) {
$httpProvider.interceptors.push('oauthHttpInterceptor');
});
I really like this approach:
http://blog.brunoscopelliti.com/authentication-to-a-restful-web-service-in-an-angularjs-web-app
where the token is always automagically sent within the request header without the need of a wrapper.
// Define a new http header
$http.defaults.headers.common['auth-token'] = 'C3PO R2D2';
You could create a wrapper function for it.
app.factory('Todo', function($resource, TokenHandler) {
var res= $resource('http://localhost:port/todos.json', {
port: ':3001',
}, {
_query: {method: 'GET', isArray: true}
});
res.query = function(data, success, error) {
//We put a {} on the first parameter of extend so it won't edit data
return res._query(
angular.extend({}, data || {}, {access_token: TokenHandler.get()}),
success,
error
);
};
return res;
})
I had to deal with this problem as well. I don't think if it is an elegant solution but it works and there are 2 lines of code :
I suppose you get your token from your server after an authentication in SessionService for instance. Then, call this kind of method :
angular.module('xxx.sessionService', ['ngResource']).
factory('SessionService', function( $http, $rootScope) {
//...
function setHttpProviderCommonHeaderToken(token){
$http.defaults.headers.common['X-AUTH-TOKEN'] = token;
}
});
After that all your requests from $resource and $http will have token in their header.
Another solution would be to use resource.bind(additionalParamDefaults), that return a new instance of the resource bound with additional parameters
var myResource = $resource(url, {id: '#_id'});
var myResourceProtectedByToken = myResource.bind({ access_token : function(){
return tokenHandler.get();
}});
return myResourceProtectedByToken;
The access_token function will be called every time any of the action on the resource is called.
I might be misunderstanding all of your question (feel free to correct me :) ) but to specifically address adding the access_token for every request, have you tried injecting the TokenHandler module into the Todo module?
// app
var app = angular.module('app', ['ngResource']);
// token handler
app.factory('TokenHandler', function() { /* ... */ });
// inject the TokenHandler
app.factory('Todo', function($resource, TokenHandler) {
// get the token
var token = TokenHandler.get();
// and add it as a default param
return $resource('http://localhost:port/todos.json', {
port: ':3001',
access_token : token
});
})
You can call Todo.query() and it will append ?token=none to your URL. Or if you prefer to add a token placeholder you can of course do that too:
http://localhost:port/todos.json/:token
Hope this helps :)
Following your accepted answer, I would propose to extend the resource in order to set the token with the Todo object:
.factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) {
var resource = $resource('http://localhost:port/todos/:id', {
port:":3001",
id:'#id'
}, {
update: {method: 'PUT'}
});
resource = tokenHandler.wrapActions( resource, ["query", "update"] );
resource.prototype.setToken = function setTodoToken(newToken) {
tokenHandler.set(newToken);
};
return resource;
}]);
In that way there is no need to import the TokenHandler each time you want to use the Todo object and you can use:
todo.setToken(theNewToken);
Another change I would do is to allow default actions if they are empty in wrapActions:
if (!actions || actions.length === 0) {
actions = [];
for (i in resource) {
if (i !== 'bind') {
actions.push(i);
}
}
}

Categories

Resources