-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Add hasBody to ngResource action configuration #12181
Conversation
I don't think custom http methods are necessarily recommended for REST services. This does need some consideration |
I thought that the decision of enabling custom methods in ngResource has already been made as it is possible right now. The current behavior of silently dropping the content body for custom requests is just weired (it took me several hours of debugging to locate the problem in angularjs). For me it feels like a bug and not a feature. Do you think that the inclusion of this patch could be harmfull to the functionality of ngResource? |
Custom methods and custom http methods are different things, are there any other methods you want a body for? I know people have asked for bodies on DELETE requests |
I would not include a body in the DELETE request as I currently have no use for it but i would like to include a body in a LOGIN request which carries the user information in a JSON (not only username an pasword). Some other custom http methods where i would like to include a body are: CLOCKIN, CLOCKOUT, STORNO, ASSIGN, NOTIFY, DISABLE but they are all specific to the project I'm working on. That's why I would like to have a generic attribute on the action specification. |
I created a modified bower-angular-resource version that includes the requested fix in version 1.4.1 https://github.com/yathos/bower-angular-resource If someone would like to test it. |
Please tell me if there is anything that I could do to get this pull request included. |
Honestly, I don't think custom HTTP methods are a big enough use case to warrant inclusion into the angular core. Especially since $resource is pretty much built for "normal" REST interfaces. If you are doing something special, the recommendation has always been to built your own wrapper around $http. |
+1 for this feature. We have built a client on top of $resource and we consider DELETE with bodies to be part of "normal" rest interfaces. The use case is bulk relationship delete. |
I'm still interested in getting this pull request integrated. |
Let's consider this after 1.6.0 is released and decide what direction to go (either merge or close). |
src/ngResource/resource.js
Outdated
@@ -640,7 +642,7 @@ angular.module('ngResource', ['ng']). | |||
}; | |||
|
|||
forEach(actions, function(action, name) { | |||
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method); | |||
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method) || action.hasBody === true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would find something like the following more intuitive:
var hasBody = action.hasBody === true || (action.hasBody !== false && /^(POST|PUT|PATCH)$/i.test(action.method));
Am I missing any reason why we shouldn't allow people to overwrite hasBody
for POST, PUT, PATCH? (Not that they should.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with both versions of the statement. Should I modify the patch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless you can think of a reason not to 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to like twice to see the error but the correct statement would be:
var hasBody = action.hasBody === true || (action.hasBody !== true && /^(POST|PUT|PATCH)$/i.test(action.method));
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually meant it the way I wrote it above. Your version is the same as:
var hasBody = action.hasBody === true || /^(POST|PUT|PATCH)$/i.test(action.method);
...which is not what we want.
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for the commit author(s). If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. |
1 similar comment
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for the commit author(s). If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. |
CLAs look good, thanks! |
1 similar comment
CLAs look good, thanks! |
Is there anything more that I can do to get this feature included? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also need tests for this.
src/ngResource/resource.js
Outdated
@@ -193,6 +193,8 @@ function shallowClearAndCopy(src, dst) { | |||
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods - | |||
* `response` and `responseError`. Both `response` and `responseError` interceptors get called | |||
* with `http response` object. See {@link ng.$http $http interceptors}. | |||
* - **`hasBody`** - `{boolean}` - allows to specify if a request body should be included or not. | |||
* If not specified only POST,PUT and PATCH will have a request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Missing whitespace after ,
.
@kaufholdr, feel free to ping me once you've made the requested changes, so I can take another look. |
…PATCH have a request body by default.
Sure. I have added the required test cases. Are they sufficient? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also need a test for POST/PUT/PATCH methods with hasBody: false
.
src/ngResource/resource.js
Outdated
@@ -193,6 +193,8 @@ function shallowClearAndCopy(src, dst) { | |||
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods - | |||
* `response` and `responseError`. Both `response` and `responseError` interceptors get called | |||
* with `http response` object. See {@link ng.$http $http interceptors}. | |||
* - **`hasBody`** - `{boolean}` - allows to specify if a request body should be included or not. | |||
* If not specified only POST, PUT and PATCH will have a request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only POST, PUT and PATCH will have a request --> only POST, PUT and PATCH requests will have a body
test/ngResource/resourceSpec.js
Outdated
@@ -97,6 +97,43 @@ describe('basic usage', function() { | |||
$httpBackend.flush(); | |||
}); | |||
|
|||
it('should include a request body when calling custom delete with hasBody is true', function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
custom delete --> custom method
Can you also test with another method (in the same test), just to avoid giving the impression this is just about DELETE
.
Extended test case with a custom CREATE request. Improved documentation of hasBody.
I have added the requested changes. Should I squash down my changes to one commit on completion or would you like to take them as several commits if it's ready? |
|
||
var postResponse = R.post(); | ||
var putResponse = R.put(); | ||
var patchResponse = R.patch(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would these 3 calls fail if hasBody
was not set? I don't think so (but I haven't tried it either). Can you check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No they don't fail if hasBody is not set. This is a bug or a feature of ngResource not enforcing the described api.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't found a way to distinguish between request parameters and the request body in httpBackend.
Did I miss something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you pass two params (e.g. R.post(null, {foo: 'bar'})
), then the first one is interpreted as params
and the second one as data
.
For example, the following test currently fails, because {id: -1}
is not undefined. If something similar passes with your PR, then we are good:
$httpBackend.expectPOST('/foo').respond(function(method, url, data) {
expect(data).not.toBeUndefined();
return [200, {id: 42}];
});
var R = $resource('/foo');
var response = R.save(null, {id: -1});
$httpBackend.flush();
expect(response.id).toBe(42);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sry for the delayed response on my side. I have merged your proposed test case into the test method with hasBody true. The only problem for me was that I couldn't reproduce the failing of the test case. (Even when I used your version.)
Can you see my error or give me the error that you get?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was probably not clear. What I meant is that my test fails with the code currently in master (i.e. without your PR), which is expected, because we don't support the hasBody
yet. If the test passes with your PR, then it verifies that the feature you added (hasBody
) does indeed work for PUT/POST/PATCH
requests too, so it is good to be merged.
test/ngResource/resourceSpec.js
Outdated
it('should include a request body when calling custom method with hasBody is true', function() { | ||
var instant = {name: 'info.txt', value: 'V2hlbiB0aGUgdGltZSBlbmRzLg=='}; | ||
var fid = 42; | ||
var created = {fid: fid, filname: 'fooresource', value: 'V2hlbiB0aGUgdGltZSBlbmRzLg=='}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: You don't really need these 2 extra variables (fid
, created
). You could inline them (and only keep the necessary props) to avoid visual noise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have cleaned up those variables.
You don't need to squash them, btw. We can take care of it while merging. |
Is there anything that I should fix in this Pull request right now? |
@kaufholdr, that should be the last bit 😃 |
test/ngResource/resourceSpec.js
Outdated
@@ -102,6 +102,10 @@ describe('basic usage', function() { | |||
var condition = {at: '2038-01-19 03:14:08'}; | |||
|
|||
$httpBackend.expect('CREATE', '/fooresource', instant).respond({fid: 42}); | |||
$httpBackend.expectPOST('/fooresource').respond(function(method, url, data) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not needed here. POST
is not a custom method (which what this test is about).
test/ngResource/resourceSpec.js
Outdated
expect(deleteResponse.$resolved).toBe(true); | ||
}); | ||
|
||
it('should not include a request body if hasBody is false on POST, PUT and PATCH', function() { | ||
$httpBackend.expect('POST', '/foo').respond({}); | ||
$httpBackend.expectPOST('/foo').respond(function(method, url, data) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this change to all three methods. E.g. something like:
function verifyRequest(method, url , data) {
expect(data).toBeUndefined();
return [200, {id: 42}];
}
$httpBackend.expect('POST', '/foo').respond(verifyRequest);
$httpBackend.expect('PUT', '/foo').respond(verifyRequest);
$httpBackend.expect('PATCH', '/foo').respond(verifyRequest);
...
Fell free to tell me if there is still something missing. :-) |
test/ngResource/resourceSpec.js
Outdated
|
||
expect(postResponse.id).toBe(42); | ||
expect(putResponse.$resolved).toBe(true); | ||
expect(patchResponse.$resolved).toBe(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, change these two lines to ....id).toBe(42)
.
Thank. You. :-) |
I tweaked the docs a little bit and merged. Thank you for working on this, @kaufholdr 👍 |
The use of hasBody would allow developers of REST interfaces to decide if a request body is necessary or not on a per method basis.
The way ngResource currently handles request bodies in custom requests make the definition of custom requests pretty useless. It’s not possible to transfere a request body through a other requests than POST, PUT, PATCH. Or am I wrong?
It may be true that some browser implementations are also brocken when it comes to methods like DELETE but this broken behavior should not be enforced by angular.
Even as i read the #3207 after creating the patch its somehow picking up pmariduenas comment on creating a enableDeleteRequestBody. Its just the generic version for all custom requests.