POST requests with $http seems overly complicated

3,481 views
Skip to first unread message

Dustin Martin

unread,
Sep 20, 2012, 2:30:48 PM9/20/12
to ang...@googlegroups.com
Hey guys, 

I'm new to Angular and trying out the $http and $resource services. In my experiments, GET requests seems to be fairly simple but POST requests seem quite complicated and I hope I'm missing something.

The following code is what I'm using to do a POST.

$http.post('api/projects.cfm', project).
success(function(data, status, headers, config) {
// Only append to the projects array if this is a new project
if( !project.id ){
// Add the project to the local project list cache
projects.push( data );
}

// Refresh the projects lists
refresh();
}).
error(function(data, status, headers, config) {
alert("Error saving project");
});

Since this is a POST I'd like to get the data sent as form data to the server instead of as a query string. The only way I've managed to do that though is by adding the following transformRequest and header.

$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';

$httpProvider.defaults.transformRequest = function(data){
if( data ){
return $.param(data);
}
};

This seems a bit complicated and I'd expect Angular to handle this kind of thing automatically. Is there a simpler way to handle this?

Also, I was trying to get this to work with the $resource service but it appears to ignore any headers added to the $httpProvider or manually to the $resource action definitions as documented here: http://docs.angularjs.org/api/ngResource.$resource. It is also sending the parmeters as a query string instead of as form data. Is there any way to get this working?

Thanks,
Dustin

Michael Bielski

unread,
Sep 20, 2012, 3:51:08 PM9/20/12
to ang...@googlegroups.com
The $resource not handling the custom headers apparently has yet to make it into production. There was a pull request on it.

Angular is more attuned to JSON than to wholesale posting of form data. I don't know what to tell you as to how to get that to work with Cold Fusion.

Dustin Martin

unread,
Sep 20, 2012, 4:33:44 PM9/20/12
to ang...@googlegroups.com
Thanks for the response, Michael.

With $resource, how do set it up to send the data to the server as JSON rather than as a query string?

My resource definition below:

var Project = $resource("api/projects.cfm",
{ "projectID": "@projectID", 
 "name" : "@name",
 "isFavorite": "@isFavorite" },
{ "query": { method: "get", isArray: true },
 "get": { method: "get" },
 "save": { method: "post" },
 "destroy": { method: "delete" } }
);

Michael Bielski

unread,
Sep 20, 2012, 6:15:51 PM9/20/12
to ang...@googlegroups.com
I don't understand $resource enough to use it, so I use $http instead. Sorry, just a failing of my brain somewhere :-P . Anyway, you can pass your data as JSON like this using $http:

var myPost = $http({ method: "POST", url: "yourUrl", data: { "projectID":"7","name":"blah","isFavorite":"false" } });
myPost.then(function(data,status,headers,config){//success things},function(data,status,headers,config){//error things});

You can also pass a string that is formatted like JSON like this:

data: '{ "projectID":"@projectID","name":"@name","isFavorite":"@isFavorite" }'

The main thing about doing it this way is that you have to have the JSON parts enclosed in double quotes for it to be valid JSON. I don't know if your server-side API is configured to check for valid JSON, but I pass mine like this just to be safe. Pawel or one of the other more advanced users could probably help you more with using $resource, but this is how I get things to work. 

Dan Doyon

unread,
Sep 21, 2012, 2:07:03 AM9/21/12
to ang...@googlegroups.com
If you are doing a CRUD application, I would highly encourage you to use $resource. I use it pretty extensively and it simplifies things. 

I recommend inspecting the resource docs  http://docs.angularjs.org/api/ngResource.$resource really closely. And do the phonecat tutorial. 
http://docs.angularjs.org/tutorial . Section 11 gives you a start to using ngResource. Be ware that you need to include a separate js file. 

I use resource quite extensively, gets, posts, puts, deletes. It is not a perfect implementation but pretty damn good. I'll give you a brief description to show its power. 

let say you have foo objects on your server and a foo has two attributes 'id', and 'bar'. 

Your service definition might look like: 

factory('FooSvc', function ($resource) {
    return $resource('_app/svc/foo.php'); 
}
// NOTE: a predefined set of operations are already setup for you and you can add more or redefine. 

function FooCtrl($scope,Foo) {
   var foo = Foo.get({ 'id' : 123 }); // GET is used to retrieve foo with id 123 
   foo.bar = 'blah'; 
   foo.$save(); // POST is used to send to server

};
FooCtrl.$inject('$scope','FooSvc');

Devil is in details, but realize that angular is handling the async nature of http for you. 

Simple? 

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+u...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular?hl=en.
 
 

Dustin Martin

unread,
Sep 21, 2012, 11:29:24 AM9/21/12
to ang...@googlegroups.com
Don,

Thanks for the example. I'd really like to use $resource if possible but I'm hung up on the way data is sent to the server. In comments on the documentation (http://docs.angularjs.org/api/ngResource.$resource) Brandon Bernal at the bottom of the page notes that all params are passed as a query string in the URL. I'd like to send it as POST fields/form data or as raw JSON. Is there any way to do this?

Dustin 

Dustin Martin

unread,
Sep 21, 2012, 12:21:25 PM9/21/12
to ang...@googlegroups.com
After looking further into this it appears I may have found the problem. 

When defining a resource, if you do not have the HTTP method in uppercase (POST vs post) it doesn't work properly but doesn't throw any errors letting you know of a problem.

var Project = $resource("api/projects.cfm/:projectID",
{ "projectID": "@projectID" },
{ "query": { method: "GET", isArray: true },
 "get": { method: "GET" },
 "save": { method: "POST" },
 "destroy": { method: "DELETE" } }
);

If I change POST to post a couple things happen. 

1.) Any params not defined in the paramDefaults object seem to be ignored completely.

2.) All params are only ever sent as a URL query string. 

So since case matters, I think a change should be made in Angular to auto uppercase the HTTP method or throw an error to let the developer know of a problem.

Dustin

dart...@gmail.com

unread,
Sep 21, 2012, 8:26:03 PM9/21/12
to ang...@googlegroups.com
You can set the headers like this:

var Project = $resource("api/projects.cfm/:projectID",
{ "projectID": "@projectID" },
{ "query": { method: "GET", isArray: true },
  "get": { method: "GET" },
  "save": {method: 'POST', headers: 'Content-Type : application/x-www-form-urlencoded;'},
  "destroy": { method: "DELETE" } }
);

but, how you can set data from post|put method???

Dan Doyon

unread,
Sep 21, 2012, 9:22:20 PM9/21/12
to ang...@googlegroups.com
Just a quick note, you don't need to provide method args unless you like the explicitness or want to change the defaults. If I have time I'll see about addressing your other concerns/ ques this weekend

Sent from my iPhone

dart...@gmail.com

unread,
Sep 21, 2012, 11:39:05 PM9/21/12
to ang...@googlegroups.com
I have try to make this: 
on services.js

angular.module('MyApp', ['ngResource'])
.factory('userR', function($resource){
return $resource('api/users', {}, 
{
"save": {method: "POST", headers: "Content-Type : application/x-www-form-urlencoded;"},
});
});

and called the services from users.js from this way:

function UserCreateCtrl($scope, $http, $location, userResource){
 $scope.save = function(){
  var newUser = new userResource();
 // test user
  newUser.nickname = 'user1';
  newUser.email = 'te...@test.com';
  newUser.key = 'user1pwd';
  
  newUser.save(); //error because no exist method 'save'

dart...@gmail.com

unread,
Sep 21, 2012, 11:41:28 PM9/21/12
to ang...@googlegroups.com
should be say
.factory('userResource', function($resource){
...

Dan Doyon

unread,
Sep 22, 2012, 12:39:55 PM9/22/12
to ang...@googlegroups.com
you are missing $ should be     newUser.$save()

dart...@gmail.com

unread,
Sep 22, 2012, 5:39:17 PM9/22/12
to ang...@googlegroups.com
why $save, if i have "save" no "$save", $save is a default method of Angularjs.

HEADERS:
  1. Request URL:
  2. Request Method:
    POST
  3. Status Code:
    400 Bad Request
  4. Request Headersview source
    1. Accept:
      application/json, text/plain, */*
    2. Accept-Charset:
      ISO-8859-1,utf-8;q=0.7,*;q=0.3
    3. Accept-Encoding:
      gzip,deflate,sdch
    4. Accept-Language:
      es-419,es;q=0.8
    5. Connection:
      keep-alive
    6. Content-Length:
      59
    7. Content-Type:
      application/json;charset=UTF-8
    8. Host:
      localhost:9000
    9. Origin:
    10. Referer:
    11. User-Agent:
      Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1
    12. X-Requested-With:
      XMLHttpRequest
  5. Request Payload
    1. {"nickname":"user1","email":"te...@test.com","key":"user1pwd"}
  6. Response Headersview source
    1. Content-Length:
      1942
    2. Content-Type:
      text/html; charset=utf-8

  7. Content-type don't change with $save =/.

dart...@gmail.com

unread,
Sep 24, 2012, 9:38:45 PM9/24/12
to ang...@googlegroups.com, dart...@gmail.com
any idea how to do this?

Dan Doyon

unread,
Sep 25, 2012, 1:52:49 AM9/25/12
to ang...@googlegroups.com
If you are using class methods then you don't need the $, 

From the doc.

"When the data is returned from the server then the object is an instance of the resource type and all of the non-GET methods are available with $ prefix. This allows you to easily support CRUD operations (create, read, update, delete) on server-side data."

This is so that it will not conflict with attributes you may have put on the object from server side. 

Having these methods available on the instances, makes it simpler. 

Make sense? 

Based on your error 400, seems like angular is calling the backend. Are you using relative or absolute urls for your service? If you can call your service using $http or even jquery, you should be able to do same thing with $resource. Also note, I'm using PHP and for resource I need to get data out using a method like below and not using $_REQUEST or the like. 

function get_input() {
    $json = new Services_JSON();
    $jsonInput = file_get_contents('php://input');
    $input = $json->decode($jsonInput);
    return $input;
  }

hope this helps

--dan


Reply all
Reply to author
Forward
0 new messages