Issue sending JSON in POST request to Play App - angularjs

i am working on a project using Play Framework 2.4 using a simple Java template, as in many documentation i have seen, i just wrote a simple controller with my business methods and put the necessary paths on my route's file.
Now i am writing a client in Angular.js to invoke the logic written in the play app. It had work perfectly with GET methods, but when i try to do a POST from angular using the next lines:
$http({
method: 'POST',
url : rootURL + '/user/company',
data : {id : '123456' , name: 'xxxxxx'}
});
I receive a 404 Error. After several hours of forums searching, i found that the Play App is expecting application/x-www-form-urlencoded in the request content-type header.
So i modify my Angular call to the following:
$http({
method: 'POST',
url : rootURL + '/user/company',
data : {id : '123456' , name: 'xxxxx'},
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("&");
}
})
};
And the POST works perfectly on this way, but i wonder, is there any way so i can make a POST request to my play app where the content-type is set to application/json? How can achieve this?
UPDATE
Here is my controller code:
public class MyController extends Controller {
public Result myAction(){
//do funny stuffs
}
}
And my route file has the following
POST /path/action controllers.MyController.myAction()

Have you added #BodyParser.Of(BodyParser.Json.class) in your action method?
Also when you are doing POST to a different domain, first check you request to see if it's a POST, I don't know about AngularJS, but with Polymer, it sent a OPTION first during ajax POST to different domain, and I met very similar issue (wrong content type and 404) which is resolved by implementing an OPTION on server which accept POST.
To Fix that, you need to manually implement an OPTION, here's what to do:
First, In your routes file, add an entry to accept OPTION
OPTIONS /*url controllers.Application.optionCheck(url)
Then implement the optionCheck method which accept everything
public Result optionCheck(String url){
response().setHeader("Access-Control-Allow-Origin", "*");
response().setHeader("Access-Control-Allow-Methods", "POST");
response().setHeader("Access-Control-Allow-Headers", "accept, origin, Content-type, x-json, x-prototype-version, x-requested-with");
return ok();
}
And that's it

Related

angular: pass additional data with form submit

At the moment i am using below code and it is working fine.
$scope.processForm = function($scope.formData) {
$http({
method : 'POST',
url : 'process.php',
data : $.param($scope.formData), // pass in data as strings
headers : { 'Content-Type': 'application/x-www-form-urlencoded' } // set the headers so angular passing info as form data (not request payload)
})
Now i have one small requirement. I need to pass user id(uid) along with the form data.
In Jquery it is quite simple but I am new to angular and don't have much experience.
Any advise how i can pass the additional data in Angular.
Thanks
You can encapsulate your formData with your uid parameter togheter in an object expl :
var allData={'formData': $scope.formData, 'uid': $scope.uid}
And then pass in the allData Object to your post method data : allData.
If you encounter any problem getting data in you process.php (undefined data for example), you should let Angular pass data with content type application/json (default behavior of angularjs) and in your process.php you do like this :
$postContent= file_get_contents("php://input");
$req= json_decode($postContent);
$formData= $req->formData;
$uid= $req->uid;
Note that it will be great if you test with some static values first ( 'uid' : 2 ) to make sure that it works fine
you can send an object (associative array in php ) in $http data parameter:
$scope.processForm = function($scope.formData) {
console.log($scope.formData);
$http({
method : 'POST',
url : 'process.php',
data : {'data': $scope.formData,'userid':userid}, // no need to use $.param, even never see it in angular
headers : { 'Content-Type': 'application/x-www-form-urlencoded' } // set the headers so angular passing info as form data (not request payload)
})
see https://scotch.io/tutorials/submitting-ajax-forms-the-angularjs-way for details on how to get data in php side

How to post a string to a rest service using angularJS

Have been using $resource.save(object); to post my objects to the rest service but this time I don't want the object to be marshalled. I need it to be posted as text/plain.
Is there an easy way of doing this using angularJS?
Solved my problem.
Used the config parameters in the .save or .post functions to set the header content-types.
var config = {
headers : {
'Content-Type': 'text/plain'
}
};
$http.post(theURL, theBody, config)

AngularJS $resource GET params appear in URL

My REST backend [ based on NodeJS/express/mongojs] is complaining 404 (not found) when Params are attached as part of URL. Backend rest interface is coded as below;
var express = require('express');
var router = express.Router();
router.get('/login', auth.signin); //auth.signin is code to verify user
Above REST service is consumed by AngularJS based frontend through $resource as below;
Definition:
angular.module('myapp').factory('signinmgr', function($resource) {
return $resource("http://localhost:3000/login", {}, {
'get': {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
}});
Usage:
signinmgr.get({'username':'myname', 'password':'mypass'}, function(data){
//success
}, function(x){
//failed
});
Problem:
Frontend code above produces a URL to consume REST service where parameters are part of URL i.e. http://localhost:port/login?username=myname&password=mypass [if I use GET method, POST is OK]. I wanted my front end to keep URL as http://localhost:port/login and post any parameters through body as backend is using req.body.paramName to read those. [Actual Solution]
If (1) cannot be done, and my frontend is sending params as part of URL, I needed help as to know how to equip my backend to allow this URL with parameters so that backend doesnt return 404 as the base URL http://localhost:port/login is already there.
PS: for (1), I tried this thread with data:{username:'',password:''} but of no use. Please help if I am missing something very obvious or some concept.
Try the $http service instead:
angular.module('myapp').factor('signinmgr', function($http) {
return {
login: function (username, password) {
$http.post("http://localhost:3000/login", {
username: username,
password: password
}
}
};
});
signinmgr.login('myname', 'mypass').then(function(data){
//success
}, function(x){
//failed
});
Each request that my nodejs/expressjs backend receives has three places for passed attributes;
params{}
query{}
body{}
My problem (1) cannot be fixed in case I want to use GET method since with GET request parameters are visible as part of URL i.e. http://localhost:port/login?username=myname&password=mypass. To send my username/password I had to use POST that sends parameters as part of body{}.
My problem (2) was that I was using GET and mistakenly looking for parameters in body{} of request. Instead, parameters passed as part of URL in GET request are added to query{} of the request.

Including placeholder changes ngResource save() method from POST to GET

a really simple example. I have a RESTful api and I setup my resource the following way.
app.factory('apiFactory' , ['$resource', 'GLOBALS',
function($resource, GLOBALS){
return {
Discounts: $resource(GLOBALS.apiPath + 'discounts/:id', {id:'#id'}, {update:{method: 'PUT'}})
}
}
])
And then I call it in a Controller like so
var discountResponse = apiFactory.Discounts.save($scope.discount);
Everything works fine until I add '/:id' to my URL. I do this so that my delete method passes the id along. Like so 'discounts/6'.
The issue that I have is that as soon as I add the placeholder my save() method sends off a GET instead of a POST.
Request URL:http://local:8089/api/discounts
Request Method:GET
Status Code:200 OK
If I remove the placeholder I get
Request URL:http://local:8089/api/discounts
Request Method:POST
Status Code:200 OK
And everything works great, accept for the delete request, which now does not map the placeholder, as it no longer exists.
I have absolutely no idea why. I'm pretty new to $resource, so I am very sure I am not understanding something.
The answer was provided on a differently formulated question and I thought I'd share it.
return {
Discounts: $resource(GLOBALS.apiPath + 'discounts/:id', {id:'#id'} ,{
save: {
method: 'POST', url: GLOBALS.apiPath + "discounts"
},
update: {
method: 'PUT', url: GLOBALS.apiPath + "discounts/:id"
}
})
}
It would seem that for the save() to POST properly I had to define a path in the customConfig object. I'm not sure why this didn't work for me out of the box.
The answer was provided here. Many Thanks!
ngResource save() strange behaviour

Slim, Postman and AngularJs : $app->request->getBody() vs $app->request->post()

I'm a beginner. I've written a test application made of an AngularJs GUI on the client side and a PHP API on the server side.
This is the angular service handling the requests
myApp.factory('Book', ['$resource', 'API_URL', function($resource, API_URL){
return $resource(API_URL + '/books/:bookId', {bookId: '#bookId'}, {
get: { method: 'GET', isArray:true },
update: { method: 'PUT'},
save: { method: 'POST'},
delete: {method:'DELETE'},
});
}]);
When I submit a book from the Angular app I can catch the POST in Slim by using
$post_a = json_decode($app->request->getBody());
//$post_b = $app->request->post(); //this would be empty
When I use Postman and I perform a POST I can catch the POST in Slim by using
//$post_a = json_decode($app->request->getBody()); // this would be empty
$post_b = $app->request->post();
I don't get why there is this difference. Could you please explain?
Am I not meant to catch the post just with $app->request->post(); in both the cases? Why the post coming from Angular can be caught only with $app->request->getBody()?
The $app->request->post() method retrieves key/value data submitted in a application/x-www-form-urlencoded request. If the request uses a different content-type (e.g. application/json), you can retrieve the raw request body with the $app->request->getBody() method and decode it as necessary. Let me know if you have further questions.
You could still use
$post_b = $app->request->post()
in Slim.
As long as you call this REST service from html form (AngularJS) by passing the data as form value formatted instead of as JSON.
If in AngularJS you have the data in JSON format, you have to translate it first into form. Below is the example how to invoke this REST service:
Object.toparams = function ObjecttoParams(obj) {
var p = [];
for (var key in obj) {
p.push(key + '=' + encodeURIComponent(obj[key]));
}
return p.join('&');
};
$http({
method: 'POST',
url: url,
data: Object.toparams(myobject),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
myobject is the data in JSON format that is going to be created
Thanks Josh..Your answers works for me.
Steps to follow:
1.You need to send request in json format under raw tab like this:
{"username":"admin","password":"admin"}
2.You need to set Content-Type to application/json in the headers.
That's it and it will work.

Resources