I am working on a web Spring MVC 4 application for my end of studies project, the application should connect web and android clients.
I used #RestController to help android app communicate easily via json. I am trying to catch connection failure ex:404 in both web or mobile app, even in the web application i tried to make almost all communication with the server via Ajax hoping to make user more relaxed (Progress bars, Live info hints...) but, one of the problems in countries that after using event.preventDefault() from jQuery, i can't get over it in next actions and the response pages loaded and kept unseen! for example here is my login page example:
login.js
jQuery(document).ready(function ($) {
$("#login-form").submit(function (event) {
//Do somthing...
// Call login function
logViaAjax(event);
// Prevent the form from submitting via the browser.
return false; //Or using event.preventDefault()
});
});
// logViaAjax function
function logViaAjax(event) {
var logObj = {};
logObj["username"] = $("#username").val();
logObj["password"] = $("#password").val();
//the response
$.ajax({
type: "POST",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
url: "/login",
data: logObj,
dataType: 'text',
timeout: 10000,
success: function (data) {
if (data.indexOf("login-box") !== -1) {
showLogFail();
}else{
window.location.reload();
//I dont need to reload the page because this gives me login page again... May be if there is a way to get the URL from data!?
}
},
error: function () {
showWarning();
}
});
I am using Spring Security 4 to handle authentification and we know that it gives url ex:/login?fail for failure and get the requested page if succeed and i recently knew that it redirect even before getting in the loginUrl my ex /login. I tried to test if the user logged in on server side and send json response but this could leads to lose the page before requesting login.
I think i should test in the success: function() if url loaded in the jQuery(but not showed) contains ?fail or not, to inform user to change log or password. Else, if it do not, the page user need is charged in the back and all what i need is to show it.
UPDATE
After those little changes all what I need to make is just to keep the page beyond the login page and reshow it. Take note that this function is automatically offered by Spring Security but it still running on background. Ex in my case when user click /messenger the app redirect him to /login to login, if he fail login it gives /login?fail but this page is unseen due to JQuery, else he login successfully it send him to /messenger again…
Try this
var flag;
jQuery(document).ready(function ($) {
$("#login-form").submit(function (event) {
//Do somthing...
// Call login function
logViaAjax(event);
if(flag.status == 404){
return false;
}
});
});
function logViaAjax(event) {
var logObj = {};
logObj["username"] = $("#username").val();
logObj["password"] = $("#password").val();
//the response
flag = $.ajax({
type: "POST",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
url: "/login",
data: logObj,
dataType: 'text',
timeout: 10000,
success: function (data) {
//Here to move to the page unseen if success and show error hint if it fail...
},
error: function () {
showWarning(); //Show error alerts, this part is DONE!
}
});
Changes Made
► Assigned the AJAX to a variable and dded a condition in the jQuery(document).ready
AJAX - The onreadystatechange Event
Demo Fiddle to show 404 error
Related
Issue:
The first ajax is working properly in the main.js, the second one is doing its job at first look but I think there might be a bug somewhere. I can reach the getProducts method after I click to the button.
The product_list.html file should appear on the browser screen, but it doesn't.
I get no error message on the front-end or the back-end.
This is what I noticed: After click to the button -> F12 -> Network -> products -> I can see here a status code: 200 and the product_list.html file content as response.
In case the POST ajax call succeeds and in the case I add: location.href = "/products";, the browser will load product_list.html
I use the get ajax call because i need to pass the jwt token in the req header. (I deleted the jwt authentication parts from the code below because I narrowed down the error to the $.ajax() and res.sendFile() relationship)
//routes.js
routes.get("/products", ProductController.getProducts);
//ProductController.js
var root = path.join(__dirname, '../../views');
module.exports = {
getProducts(req, res){
console.log("getProducts!"); //I can see in the console
res.sendFile("product_list.html", {root}) //It doesn't render the html
},
}
//main.js
$("#btn-login").click(function(){
$.ajax({
type: 'POST',
url: "http://localhost:8000/login",
dataType: "json",
contentType: "application/json",
data: JSON.stringify({
"username": $("#login-user").val(),
"password": $("#login-pwd").val(),
}),
success: function(data){
if ($("#login-chkbx").is(':checked')){
$.ajax({
url: "http://localhost:8000/products",
type: 'GET',
beforeSend: function (xhr) {
xhr.setRequestHeader("user", localStorage.getItem("user"));
},
});
}
}else{
console.log("Checkbox is not checked");
}
}
});
});
What causes the issue and how to solve it?
Thanks!
file should appear on the browser screen
No it does not and it should not. The file should be returned to the ajax function call in the success callback:
$.ajax({
url: "http://localhost:8000/products",
type: 'GET',
beforeSend: function (xhr) {
xhr.setRequestHeader("user", localStorage.getItem("user"));
},
success: function (file) {
// The file is in the "file" variable above.
// Do whatever you want with it. For example you can replace
// the current page with the returned file:
document.body.innerHTML = file;
}
});
That is the whole point of AJAX - a mechanism for programmers to override the normal flow of HTTP requests that loads the response to the browser page. If it allows you to not load the response to the browser page it also means it will not automatically load the response to the browser page - because doing so will not allow you to not load the response.
If you want to automatically load the response then don't use ajax:
// Replace $.ajax(... with:
window.location.href = "http://localhost:8000/products";
However, this method does not allow you to set custom request header.
in your frontend code you do nothing with the GET /products response
the backend sendfile as the name says it just sends the file over to the requester. Being an ajax call, it must be rendered by the frontend code.
Add something like success: response => $('body').html(response)
I'm trying to implement a quick Login-Logout solution.
Login:
Sends a POST request with AJAX (Client-Side)
NodeJS creates a cookie (Server-Side)
Reloads the page with javascript (Client-Side)
Works fine
Logout:
Sends a POST request with AJAX (Client-Side)
NodeJS deletes the cookie (Server-Side)
I make sure to delete the cookie on client side as well with JS (Client-Side)
Reloades the page with JS (Client-Side)
NodeJS still somehow receives the cookie from the req ???
So, when I reload the page after Logout operation, I'm still logged-in. Only after I force refresh or press "Logout" one extra time, I see that I'm indeed logged-out.
I tried many different approaches and methods of loading the page and deleting the cookie, but nothing seems to work.
Javascript:
$("#logout").click(function () {
var username = this.value;
logout(username)
var currentUrl = window.location.href;
window.location.assign(currentUrl); //I tried different methods of reloading the page
});
// perform logout action
function logout(username) {
$.ajax({
type: "POST",
url: "/logout",
data: JSON.stringify({ username: username }),
success: function () {
//just in case remove the cookie
removeLoginCookie()
},
error: function () {
removeLoginCookie()
},
dataType: "json",
contentType: "application/json"
});
}
NodeJS:
app.post('/logout', (req, res) => {
try{
res.clearCookie('userlogin', { path: '/' })
res.send({ "status": "Logged out successfully!" })
}catch(e){
res.sendStatus(500);
}
});
I was able to fix the problem, by setting a timeout before reloading the page, but I'm not a fan of this solution and I'm still curious on why was it happening, and what would be a better way to do this.
$("#login").click(function () {
var username = this.value;
logout(username)
setTimeout(()=>{
var currentUrl = window.location.href;
window.location.assign(currentUrl);
}, 500)
});
I am trying very hard to implement history.js a jQuery plugin for IE 8 and 9. The demo seems to be fine in IE10+ and other modern browsers but I'm not able to understand the problem below for IE<=9. The demo is a asp.net mvc5 application where I want to provide a single page user experience where I'm just loading partial views and performing a post action via ajax. All the views load in a #shell div in my master view. May be I am missing something!!
My demo app starts with http://localhost:1089/ which is the first request that renders a login page. Any other pushState() causes a change in the url with appending #Home/About?ref=one.
Following is my script that is executed in layout.cshtml which is loaded only once:
(function (window, undefined) {
History.Adapter.bind(window, 'statechange', function () {
var state = History.getState();
if (state.data.options == undefined) {
alert("options is undefined");
var options = {
url: state.url,
type: "GET",
dataType: "html"
};
GetPartial(options);
} else {
GetPartial(state.data.options);
}
});
})(window);
$(document).on('click', 'a', function (e) {
e.preventDefault();
var options = {
url: $(this).prop('href'),
type: "GET",
dataType: "html"
};
History.pushState({ options: options }, $(this).text(), $(this).prop('href'));
});
function GetPartial(options) {
if (options) {
$.ajax({
url: options.url,
type: options.type || 'GET',
dataType: options.datatype || 'html',
async: true,
data: options.dataToPost,
beforeSend: function() {
$("#loaderMain").show();
},
complete: function() {
$("#loaderMain").hide();
},
cache: false,
}).success(function(response, status, xhr) {
var ct = xhr.getResponseHeader("content-type") || "";
if (ct.indexOf('html') > -1) {
// returned result is of type html, so act accordingly
if (options.selector == undefined) {
InjectContentToShell(response);
} else {
$(options.selector).empty().append(response);
}
} else if (ct.indexOf('json') > -1) {
// returned result is of type json, so act accordingly
}
}).fail(function(e) {
console.log(e);
});
}
}
function InjectContentToShell(content) {
$('#shell').fadeOut(100, function () {
$(this).empty().append(content);
}).fadeIn(100);
}
When I render this first page, I add one entry to History by pushState method like this:
$('#submit').on('click', function (e) {
e.preventDefault();
var formInstance = $(this).closest('form'),
dataToPost = formInstance.serialize();
var options = {
url: formInstance.prop('action'),
type: formInstance.prop('method'),
dataType: "html",
dataToPost: dataToPost
};
History.pushState({ options: options }, "Home - All Summary Views", "/Home/Index");
});
Pushing the state above, changes the url to http://localhost:1089/#Home/Index in html4 browser which is fine. I am able to go back and forward. This works well.
Now when I refresh the page in html4 browsers, the request sent to server is for first url i.e. http://localhost:1089/ which bring the first view to the client. So, the user can see the first page i.e. login screen. And then statechange event is fired and the state url still remembers the last url where page was refreshed and a new request is made for this url to server. The response is then injected in shell.
So, the problem is - on page refresh, user is getting two views instantly - the initial page where the user started his browsing session and soon the second page is loaded from the server and added to shell via animation as when the statechange event is fired, the History.getState() still remembers the last url.
I don't want to show this initial page on page refresh. If this is solved, then everything works fine with this history.js I think!
The above problem is also mentioned as a concept of bookmarking in the this article. Refer to the first figure and the text written above it.
My demo application works fine in html5 browsers as on every History.pushState() the browser's url is changed to the url I have specified. And when page is refreshed, the request is sent to the server as per that url and not with the first url in the browsing session started.
After trying many things, I have come up with the following solution to avoid showing the first view i.e. login page to the users with html4 browser:
I have created a view "Index" that is the first view being rendered. Which doesn't have any html content but has this script:
<script>
$(function () {
if (History.getCurrentIndex() == 0) {
var options = {
url: "/Account/Login",
type: "GET",
dataType: "html"
};
History.pushState({ options: options }, "Login Page", "/Account/Login");
}
});
</script>
The above code checks if the current index is 0 or not. If it is 0, then it means that user has just started his browsing history and it should give a login screen. If it is not 0, then it means that user was on some other step of user journey within the application which does nothing. And once this view is rendered the statechange event is fired which already has the information about the url of the page where user requested for refresh. As per the logic of statechange event handler above in my question, that resource is requested from the server and injected in the #shell container.
The above solution is working very well and gives the same experience to user as if they are on html5 browser. The above code works for me but will be glad to know if someone has any other solution.
Very new to code in general so apologies in advance if i dont explain myself properly,
But I have a form, that actions a piece of JavaScript on submit.
If the form validates successfully then it calls a php file for server side processing.
Once the server side processing is complete the php file returns some data (a url) which the user is then redirected to (client side)
This all works fine on desktop (chrome, IE, FF) and via modern mobile devices, however the redirect is not working on some devices (blackberry for one), and a i assume other older devices. Instead of the redirect URL going straight into the address bar, it is being placed after the url of the original page - as such causing the user to be redirected to a page that of course doesnt exist.
Below is the script that is called on submit. Again apologies if none of the above makes sense...I am very new to all this:
$(function () {
$('#wait').hide();
$('form#leads_form').on('submit', function (e) {
if (validateFrm()) {
$(":submit", this).attr("disabled", true);
$('#wait').show();
var jqxhr = $.ajax({
type: 'post',
timeout: 300000,
url: 'sell-save-leads.php',
cache: false,
data: $('form').serialize(),
success: function (data) {
//alert("Submit success: " + data);
window.top.location.href = data;
}
});
} else {
//alert("validation errors");
$('#wait').hide();
}
e.preventDefault();
});
});
If anyone is able to help or offer some advice that would be great.
As your form is located in an iFrame I suggest you to use this jQuery plugin to send messages from an iframe to its parent:
http://benalman.com/projects/jquery-postmessage-plugin/
With this you could send a message from inside your success function, containing the new url, and catch it in the parent window.
You can also use
window.top.location.assign(data);
Ref: https://developer.mozilla.org/en-US/docs/Web/API/Window.location
I have added the required meta tags to my web app so that it can be launched on an iPhone from the springboard with no safari elements (address bar etc).
However, my login page works via JS, with the response coming back as JSON.
In Safari, when not launched as a web app, the redirect works fine (using window.location).
When launched as a web app, the redirect doesn't seem to work properly. The screen will refresh but be on the same page.
Is this a limitation of a web app or (as I suspect) am I doing it wrong?
This is the code that responds to the ajax post
genericPost: function (f, ajaxLoad) {
if (ajaxLoad == undefined)
ajaxLoad = true;
var form = $(f);
var data = form.serialize();
var action = form.attr("action");
$.ajax({
type: 'POST',
url: action,
data: data,
success: function (response) {
if (response.worked) {
//navigate to the next required page
//Notify.showMessage(response.message);
if (response.redirect) {
if (ajaxLoad) {
Notify.showMessage(response.message, "ok");
var back = response.direction == "back";
$.mobile.changePage(response.redirect, "slide", back, true);
}
else {
window.location = response.redirect;
}
}
else {
Notify.showMessage(response.message, "ok");
}
}
else {
Notify.showMessage(response.message, "error");
}
},
dataType: "json",
error: function () {
Notify.showMessage("Could not submit your form at the time. Please try again. Please try again", "error");
}
});
}
Thanks
Ok, so the window.location was a red herring.
The issue was that ios seems to send a different user agent string when running full screen, one that IIS did not like.
IIS therefore decided that this browser did not support cookies or any of that stuff so the authentication token was failing.
This was fixed by adding a app_devices folder to my project and a .browser file.