Rails - AJAX PUT or PATCH firing multiple times - javascript

I'm trying to update a resource's attribute via AJAX (using PUT or PATCH request) and the request is getting fired multiple times.
I'm using Angular JS and jQuery.
HTML Template
Here is how my HTML Template looks like -
<span id="test" ng-click="setValue('test')"></span>
Javascript Code
Here is how my Angular JS code looks like -
$scope.setValue = function(value){
$.ajax({
method: 'PATCH' // or PUT,
url: 'resources/' + $scope.resourceId,
data: {
test: value
}
}).success(function(response){
console.log(response);
});
};
Rails Code
Here is how my controller's update method looks like -
def update
#resource.update(resource_params)
respond_with(#resource)
end
Screenshots
The AJAX request gets fired several times (close to 15 times). See screenshot below -
By simply changing the PATCH (or PUT) request to POST, the call gets fired only one. See screenshot below -
Is there any reason why the PUT requests are getting fired multiple times, while the POST request gets fired only once?
Even though the PUT request updates the value correctly. I'd like to prevent it from being fired multiple times. Is there any way to do that? (Without changing the routes or controller methods)

First thing to note: your screenshot from your POST request is returning 404, most likely because you're not updating your controller action to be a create instead of an `update. (Make sure that your routes are set up to match as well). Since there's no action, Rails returns a 404, and it will stop processing.
This is important, because your PATCH screenshot looks like it's probably infinitely redirecting to itself. Each PATCH request is getting an HTTP 302 Redirect in response, and since you're getting a lot of them, my guess is that it's redirecting to itself (either the same URL, or a URL that redirects to the same controller method... or some other middleware that's causing a redirect for any URL).
So, if you changed your controller action and route to allow for a POST, I bet you'd get the same multiple requests & redirects that you get with a PATCH.
That solves the one mystery. The next is to ask why you're getting the infinite redirects. I can't answer that from the available information, but this might get you started on a solution.

Related

Spring MVC - RequestParamException parameter is not present

I've got an issue that occurs eventually in my website. It uses AJAX requests to get data from the server, which uses Spring MVC.
What happens (intermittently) is that sometimes we got an exception like this one:
org.springframework.web.bind.MissingServletRequestParameterException: Required Integer parameter 'page' is not present
at
This kind of exception occurs in some AJAX POST calls (not only for this case!!) and we still cannot reproduce it to understand what is happening.
For example, in one of the cases the parameter 'page' (used to load content while the user scrolls the page - so it's a required variable) is being sent through an AJAX call that has a 'data' field with the page parameter coming from a form like this:
<input type="hidden" name="page" id="page" value="1">
And a ajax call like this one (both $("#filter") and url are ok):
$.ajax({
type: "POST",
data: $("#filter").serialize(), // serializes the form's elements.
url: _ctx + URL_FILTER,
cache: false
})
The only way we got to reproduce that is by changing its property 'name' to something other than "page". But I guess this is not the case (most users don't even open the developer console...)
I've googled it a lot and I checked every possibility. The enconding is ok:
(Content-Type: application/x-www-form-urlencoded; charset=UTF-8)
The parameters are ok, the AJAX looks ok, everything seems ok... But we cannot find what is going on. We have tried a lot of possibilities but we still couldn't force these exceptions to happen.
One hypothesis we have is that sometimes the AJAX may send empty data blocks, with none of the parameters. But we don't even know whether it's true or not and how to check its veracity.
What are the possibilities? How can it be tested?
EDIT:
We could reproduce one of the ways to get the Exception: Reloading the page repeatedly for some seconds (keeping the reload key pressed for a while). Is there a way to prevent the exception for this case?!
Make the below change to controller's class method for page parameter
#RequestParam(defaultValue = 0) int page.
Or paste the controller method here.
If you are not been able to figure out what is reason behind missing parameter, so you can add
public void controllerMethodName (#RequestParam(required = false) int page)
code in your controller definition which will not throw any exception if parameter is not present in your ajax request.
Are you sure your form isn't being modified at the same time? For example, if your library used to handle the scrolling of the page tries to send the same form in a very short time (the first call updates the form while the second call is serializing it). This might be only on some browsers, not all of them, given that you can't easily reproduce it.
The way data is put back in the form might also be responsible for your problem.
Logging HTTP requests / using analytics would help you identify which requests / user agents are causing issues.

AngularJS 1.2 $http seems to cache POST requests when no data is passed

I'm having an issue with AngularJS 1.2 POST requests using $http service.
On a button click, I trigger a POST request like so:
$http.post('my/url').success(function(responseText) {
// Do something
console.log(responseText);
});
My problem is, if I click twice on the button and the first callback hasn't fired yet, only one HTTP request is issued, but my callback is fired twice (with the same data as a parameter).
If I explicitely add cache: false:
$http.post('my/url', {cache: false}).success(function(responseText) {
// Do something
console.log(responseText);
});
Then two requests are issued as I expected.
It looks like a bug to me. Why would anybody want to cache a POST request? Moreover, the AngularJS documentation specifies that we have to pass cache: true if we want to activate the cache for GET requests. Which sounds like it is inactive by default, and not active at all for POST requests (which would make sense to me).
Is there a way to deactivate the cache once and for all, for every request, on the 1.2 branch? I didn't find any.
EDIT
I misused $http.post. The second parameter is not the options map, but rather the data to send to the server. According to the documentation, this parameter is mandatory. So I can execute this:
$http.post('my/url', {'anything': 'anything'}).success(function(responseText) {
// Do something
console.log(responseText);
});
And it works as expected.
So the real question is: why is the data parameter mandatory for a $http.post call to work properly? I feel like I am missing something about HTTP. I already have everything I need in the URL (something like company/company_id/employee) and I don't need any additional data.

Bizarre behavior with jQuery DELETE requests

I'm having a really strange issue interacting with a RESTful json interface and can't seem to get anywhere diagnosing it.
Using jQuery, I send a DELETE request to the JSON api and receive a 204 response. Success method is called. Everything is happy.
$.ajax({
type: "DELETE",
url: "/content_blocks/" + $(this).parent().attr('id'),
dataType: "json",
success: deleteItem($(this).parent())
});
From Firebug console:
> DELETE http://localhost:3000/content_blocks/228 204 No Content 92ms
Now I do the exact same thing with another item and get this:
> DELETE http://localhost:3000/content_blocks/231 (spinning circle loading forever)
The server sends the same response, jQuery fires the success callback. But the response is apparently never received.
If I send a third DELETE request, it works normally again, and apparently alternates this way forever.
I have another ajax call that sends a POST request. I do any number of these in a row with no issue. But if I do a successful DELETE first, then a POST, the POST behaves like the second, unsuccessful DELETE, except that the success method doesn't fire. If I do a second POST, that one works, just like the third DELETE.
To me this seems like something about the first DELETE request, even though it's successful, is broken or not getting properly reset. Somehow a second request resets it.
I'm using rails 4.1.0.rc1 and jQuery 1.11.0 if that makes any difference.
Has anyone seen anything like this before?
Could this be a jQuery bug?

Get jQuery post redirect response

I've written some HTML/Javascript that sits on a third-party server for security reasons. This page performs a javascript post to another page on the same site. However, instead of responding with useful data, it instead wants to perform a redirect (if you would post via a normal HTML form to this page, it would redirect your browser). How can I process this process? I basically want to be able to extract the url's query parameters that it is trying to redirect with (and then put this link into a hidden form field).
Here is my basic ajax post...
$.ajax({
url: '/someurl/idontcontrol',
data: serialized_form_data,
async: false,
type: 'POST',
success: function(data, textStatus, x) {
alert(x);
alert(x.getAllResponseHeaders());
return false;
$('#redirect_link').val(WHAT_DO_I_PUT_HERE);
}
});
Note that the URL I am posting to is not one that I control, so I have no power over what it returns.
UPDATE: When I use the above alerts, I receive "[object XMLHttpRequest]" and "null". I'm monitoring the headers with a Firefox plugin and they seem be coming back as expected, but I can't seem to access them via javascript (I've also tried x.getResponseHeader('Location'), but that and all other calls to getResponseHeader return empty).
ALSO: I don't know if it matters, but the status code is 302 (as opposed to 301 or 303).
Thanks!
According to the jQuery Documentation the success method can take a third argument which is the XMLHttpRequest object.
According to Mozilla's XMLHttpRequest page, this object should have a "status" property. If you check the value of this property, and it is a redirect code, such as 301 (permanent redirect) or 303 (temporary redirect) it is known the processing page is trying to perform a redirect. The "statusText" property should be able to be used to determine the location it is trying to redirect you to.
If you know it is trying to redirect, you can then re-post the data through Ajax to the new URL.
The strange thing is though, when researching this, stumbled across this page that indicates the XMLHttpRequest object should follow redirects (see the comments). So it seems like this would be a non-issue.
Unless the processing page is using an HTML meta redirect (like below) to do the redirection. In that case, I'm not sure what could be done - maybe try to parse the returned HTML for meta tags and see if any of them are attempting a redirect.
<meta http-equiv="refresh" content="0;url=http://www.example.com/some-redirected-page">
You can't get the full HTTP headers from an AJAX call in JQUery, so you can't process the redirect in this way.
However with a raw javascript request you do have access to the XMLHttpRequest getAllResponseHeaders() method which will allow you to process the redirect (this function for single headers).
Sorry, not directly an answer to your question, but I'm sure it's possible with jQuery too as it's quite simple with Prototype.
// Warning: this is Prototype, not jQuery ;-)
//...
onComplete: function(response) {
var refresh = response.getResponseHeader("Refresh");
var whatever = response.getResponseHeader("Whatever");
}
//...

detecting JSON loaded by browser

I have an application in which most requests are submitted via AJAX, though some are submitted via "regular" HTTP requests. If a request is submitted and the user's session has timed out, the following JSON is returned:
{"authentication":"required"}
The JavaScript function which submits all AJAX requests handles this response by showing a popup message and redirecting the user back to the login page.
However, when a non-AJAX request receives this response the JSON is simply shown in the browser because the response is processed directly by the browser (i.e. the aforementioned JavaScript function is bypassed). Obviously this is not ideal and I would like the non-AJAX requests that receive this response to behave the same as the AJAX requests. In order to achieve this, I can think of 2 options:
Go through the application and convert all the requests to AJAX requests. This would work, but could also take a long time!
The JSON shown above is generated by a very simple JSP. I'm wondering if it might be possible to add a JavaScript event handler to this JSP which is run just before the content is displayed in the browser - I'm assuming this would never be called for AJAX requests? This handler could call the other JavaScript code that displays the popup and performs the redirection.
If anyone knows how exactly I can implement the handler I've outlined in (2), or has any other potential solutions, I'd be very grateful if they'd pass them on.
Cheers,
Don
3) Change your AJAX code to add a variable to the GET or POST: outputJson=1
You cannot add a handler to the JSP that way. Anything you add to it will make it a non-JSON producing page.
There are two options that I can see:
Add a parameter to the page by appending a URL parameter to the screen that modifies the output.
URL: http://domain/page.jsp?ajaxRequest=true
would output json only
URL: http://domain/page.jsp
would display a jsp page that could forward to another page.
OR
change the response to have the forwarding code in the JSP that will get executed by the web browser if it is hit directly. Then have your calling AJAX to strip the forwarding code out, and then process what is left.
4) Read up on the 'Accept' request HTTP header.
Then, on the server side tailor the output:
e.g.
if(Accept contains application/json...) { // client asking for json, likely to be XHR
return {"foo":"bar"}
} else { // other
return "Location: /login-please";
}
Start with a smarter error message, like this:
{"error":"authentication required"}
Wrap the JSON output in a callback:
errorHandler({"error":"authentication required"});
Have a handler waiting in your script:
function errorHandler(r) {
alert(r.error);
}
And don't forget to send it down as text/javascript and not application/x-json.

Categories