When running an angularjs application, the user access may be withdrawn from the server side. In this case, every request results in a http 302 redirect and a login html (no partial) page. Since angular does expect whatever it was requesting in case the access would still be given, it breaks as it may expect a json string but gets the html login page.
Is there anything I can do do catch this event (listen to redirects, figure out whether html is returned, etc) and do a "hard" refresh of the redirect url?
Since you can't interfere an ajax 302 response and redirect to an error page, you will need to be a little creative.
You can add a header or a different response code to your relevant responses and add an interceptor on the client end.
The interceptor is a module that every ajax request\response goes throught.
You can add code that will look for that header and simple perform a $window.location.href to the login page.
Read here about interceptors.
Check this example out - It handles 401 responses.
If I get you right you are talking about the $http Service from AngularJS.
If thats the point, you could transform the response by yourself and check if its valide JSON like this:
$http({
url: '...',
method: 'GET',
transformResponse: function (reponse) {
var ret = [];//if the return stays empty, the repsonse may be HTML
try {
ret = JSON.parse(reponse);
} catch (error) {
//here you could check for the error
}
return ret;
}
}).success(function(answer){
if(answer.length === 0){//its empty, so its probably HTML
$window.location.reload();//refresh page here
return;
}
//its JSON with content!
});
Related
Is there any easy way to configure CakePHP auth component to send out a json response instead of normal redirect to login url.
Im using many ajax requests, and when the session expires, the ajax request will get a response of my home page's html instead of json encoded status.
I would like CakePHP to send out the following json response, if user is not logged in, and the query was made using json extension.
{status: false, message: "Please log in"}
Non-ajax page loads would still need to redirect as usual.
You should look upon where is the system checking whether you are logged in and see the case where it is not logged in. Wrap the following if around it:
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
//put here the html for the log in screen
} else {
$response = '{status: false, message: "Please log in"}';
echo json_encode($response);
}
Since it is hard to determine if you are making an ajax request, Cake 3 uses X-Requested-With = XMLHttpRequest. It seems this is a default for jquery (but who uses that anymore? seriously.).
You will need to set that header manually to allow cakephp to detect that your request is indeed ajax.
Your ajax requests may work on other pages because you are probably setting the _serialize var and setting the Accept Header or using the .json extensions to force json. However, the auth function does not check the Accept Header it only checks the X-Requested-With header.
So verify in your xhr request that X-Requested-With is XMLHttpRequest and you should receive a 403.
If you want to further customize the element or data that is returned, checkout the AuthComponent ajaxLogin param.
Handle Ajax Unauthorized
Basically, you will need to set the value to an element you want rendered.
Cake will look in your Element Template path and load that element.
// In your AppController
$this->loadComponent('Auth', [
'ajaxLogin' => 'yourAjaxElement', // This is what is important
// Optional stuff....
'authenticate' => [
'Form' => [
'finder' => 'AuthUser'
]
],
'authorize' => ['Controller'],
// More settings...
Then create this element
Template/Element/yourAjaxElement.ctp
Happy coding.
I have a form collecting some information that I use $.post to handle an ajax request.
$.post(ajaxEndpoint, dataObject)
.done(function (response) {
if (response.status === 'success') {
// Send data to process asynchronously
otherApiCall(response.otherData);
// Redirect to the thank you page
window.location.replace(getThankYouUrl());
}
});
function otherApiCall (data) {
$.post(otherAjaxEndpoint, data);
}
The problem I have, from what I'm guessing, is that it redirects too quickly before the other POST can be made. But I do want it to POST asynchronously then redirect so the user isn't waiting for that second response. I don't care what the result of the second response is. I just want to finish the first response, send a second POST and the redirect immediately to cut down on the user looking at a spinner.
My second $.post seems like it doesn't get sent in time before the redirect happens because I never get the data from it. If I comment out the redirect, I do. I don't want to wait until the second done() but I can't figure how not to. What am I not understanding and/or doing wrong?
Additional Information/Update
I do have control over the server side handling. Is there something on that end that I could do to get a response quickly without waiting for the rest of the processing to finish?
You probably want to let the second post complete and then do the redirect.
A simple fix would be to return the $.post from second method and use done() of the second call to manage the redirect
$.post(ajaxEndpoint, dataObject)
.done(function (response) {
if (response.status === 'success') {
// Send data to process asynchronously
otherApiCall(response.otherData).done(function(){
// second post call now complete
// Redirect to the thank you page
window.location.replace(getThankYouUrl());
}).fail(function(){
// handle failed response
});
}
});
function otherApiCall (data) {
return $.post(otherAjaxEndpoint, data);
}
The best way to send data back to a server without having to wait for it to complete would be to use the navigator.sendBeacon API.
navigator.sendBeacon('/url/to/handler', yourData);
Quote from MDN:
Using the sendBeacon() method, the data will be transmitted asynchronously to the web server when the User Agent has had an opportunity to do so, without delaying the unload or affecting the performance of the next navigation.
Your data will have to be made into a ArrayBufferView, Blob, DOMString, or FormData, and I'm not sure if it is technically a POST request or not, but the request will persist after redirection.
It is currently supported in Firefox 31+, Chrome 39.0+, Opera 26+. For other browsers, you would have to do something else. You can feature-detect like so.
if (navigator.sendBeacon) {
// Use sendBeacon API.
}
else {
// Something else.
}
The redirect is probably cancelling the AJAX request that has been queued, but not yet sent. Try doing the redirect after a timeout, to give the second AJAX call a chance to be sent.
$.post(ajaxEndpoint, dataObject)
.done(function(response) {
if (response.status === 'success') {
// Send data to process asynchronously
otherApiCall(response.otherData);
// Redirect to the thank you page
setTimeout(function() {
window.location.replace(getThankYouUrl());
}, 10);
}
});
I'm not sure how reliable this is, though. Perhaps a better solution would be to perform the redirect when the second AJAX call goes to readystate == 3, which means the server is processing the request. There's no jQuery interface to this, so you'll probably have to do it using the low-level XMLHttpRequest interface.
I have model object:
module.exports = {
redirectTo: function(params, callback) {
callback(null, 'home/redirect_to');
}
}
Can I redirect user without loading page?
It will depend on variables, so redirecting not always needed. The only thing I need now is redirecting from server-side.
Server side redirection can be done by simply replying HTTP 302 to client.
If what you are asking is "How to go around server side redirection so that your Backbone callback can still be triggered?", then my solution will be following:
For example, when an user type a url http://yousite.com/blog/someid on his browser, and you want to use Backbone callback to handle his request. Then you can let your server return:
<script> window.location="http://yoursite.com#blog/someid"</script>
Note we replace a / into #.
Or, you can let your server return (fragment allowed in Location):
HTTP/1.1 302 Found
Location: http://yoursite.com#blog/someid
Then your Backbone callback will be triggered.
It can be done using this.redirectTo(url)
I know that you can't, when using an XMLHttpRequest, intercept a redirect or prevent it, as the browser will transparently follow it, but is it possible to either
A. Determine whether a request redirected, or
B. Determine where it redirected to? (assuming that the response gives no hints)
Example code:
$.post("/my-url-that-redirects/", {},
function(response, statusCode, xmlHttpRequest){
//Somehow grab the location it redirected to
}
);
In my case, firebug will first show a POST to the url, then a GET to the redirected url. Can that GET location be captured?
1) Use different status code than 301 (2**) (if request by ajax) and handle redirection on client side:
var STATUS = {
REDIRECT: 280
};
$.post('/redirected', {}, function(response, status, request) {
if (status == STATUS.REDIRECT) {
// you need to return the redirect url
location.href = response.redirectUrl;
} else {
$('#content').html(request.responseText);
}
});
2) DO NOT REDIRECT:
I use that in "redirect pattern" = redirecting after post request (you don't want to allow user to refresh the post request, etc..)
With ajax request, this is not necessary, so when the post request is ajax, I do forward instead (just forward to different controller - depends on your server-side framework, or what you are using...). POST requests are not cached by browsers.
Actually, I don't know what's the reason you need that, so this might not be so useful for you. This is helpful when server returns different responses for ajax requests than common requests, because when browser redirect ajax request, the redirected request is not XMLHttpRequest...
[updated]
You can access headers (of redirected request) like that:
$.post('redirected', {}, function(r, s, req) {
req.getAllResponseHeaders();
req.getResponseHeader('Location');
});
There should be 'Location' header, but it depends on the server, which headers are sent back...
After 4 years now it's possible to find the last redirect location using responseURL from XHR instance in Chrome 42+ (Opera 29+) and Firefox 39+ but it's not available in IE, Edge or safari yet.
I am working on an e-commerce site and I need google sign-on it, so when a user creates his/her shopping list and click on the add to list button. I am able to send my data through the $.ajax() method, so what I exactly want is when the response from ajax method come it should redirect me to Login page if the user is not logged in, else it should save my object.
In the target endpoint of that .ajax() call, check your authentication, and if the user is not logged in, set the response header to - 401 Unauthorized.
Then in the .ajax() do this:
$.ajax(function() {
//.. your other ajax stuff..//
error: function(jqXHR, textStatus, errorThrown) {
// only redirect if user unauthorized - 'errorThrown' has text part of HTTP status header
if(errorThrown == "Unauthorized") {
window.location.href = "myloginpage.html";
}
}
});
The response header being set to 401 will trigger .ajax()'s error function, instead of the success function.
Edit:
Changed the error function to only redirect on Unauthorized
Also note, that if this is a cross-domain jsonp call, it won't work, as jsonp requests fail silently and don't trigger the error function
check in your response callback function and write your programming logic that you want
$.ajax({
'url':location,
'type':type,
'success':function(response){
/*write here your logic*/
},
'error':function(){
/*you code for error handling*/
}
});
to redirect window by javascript use
window.location.href = 'your location';
You can redirect to login page using window.location = 'yourlocation' in either success or error function of the response (depending upon what response you are gettig from server. If you are bringing the response code in header 401 error function will be executed other wise success).
but i think what you would like to have is take user back to the same page after login from which he started.
If you are interrested in this, you can use spring security for this. Its very easy to integrate if you are using spring already.
If you are not using spring you might look for some alternative for the same. Following links may help you
Spring Security Ajax login
http://java.dzone.com/articles/implementing-ajax
In jquery there is .post() method found to do this. In action page you can do whatever you want.