i have a problem with res.render() in expressjs
i use ajax to request on this route:
route.get('/about',authentication,(req,res)=>{
res.render('about');
});
I did some search and found out that res.render does not work with ajax calls
so how can I change and render page without res.render().
If I remove the res.render and console.log it it will work actually any code work but not res.render (by clicking a link I send a token in header with ajax request then in my route I have an authentication middleware that get the token then redirects the user to about.ejs page)
I just want to change the page. Any idea will help guys.
thx
here is the front-end request:
$(document).ready(function(){
$('#about').click(function(){
// window.location.href='/about';
$.ajax({
method:'get',
url:'http://localhost:5000/about',
headers:{"authtoken":localStorage.getItem('authToken')}
}).done(()=>{
// window.location.href='/about';
}).catch(e=>console.log('header.ejs error'));
});
});
res.render composes a html page using templates and sends the final composed result from the server to the client. It does not issue a rendering of the page in the client window.
If the request is issued by entering the URL in the addressbar of the browser, then the browser will do the request and render the result the server sends.
If you do an ajax request you will receive that response, but you are responsible to do something with it in the .done callback. The browser does not magically know what has to be done with the data if you do an ajax request. And because you do not have anything in your .done callback nothing will happen.
So you have to do something like that:
.done(response => {
var bodyContent = response.match(/<body>(.*)<\/body>/)[1]
$('body').html(bodyContent);
})
Usually ajax is used to update a portion of page using some resposne from server without refreshing page. If you want to navigate to another route instead of ajax use form submits or href. If you stick to ajax then return a JSON from the server and do the alteration using javascript
requestHandler=(req,res)=>{
res.json({data:{}})
}
Step-1: Make a public URL, say http://your-web-site.com/redirect_destination.html
Step-2: On making ajax request from front-end, redirect user to that page using .redirect() method on response
EDIT:
The point is, I don't think it's possible to render page by making an ajax request. You can prepare a URL which renders your desired page and redirect user to that URL using .redirect() method.
Your router will not render page ,it will send only response and I didnt get just for page rendering, why you are calling ajax request on click event.If dont mind you can write logic on click and change window.location with your router. It will render particular page, for that your router should be something like this:
// about page route (http://localhost:8080/about)
router.get('/about',isAuthenticated, function(req, res) {
res.sendFile(path.join(__dirname+'/public/about.html'));
});
function isAuthenticated(req, res, next) {
// do any checks you want to in here
// CHECK THE USER STORED IN SESSION FOR A CUSTOM VARIABLE
// you can do this however you want with whatever variables you set up
if (req.user.authenticated)
return next();
// IF A USER ISN'T LOGGED IN, THEN REDIRECT THEM SOMEWHERE
res.redirect('/');
}
And change your url on click to http://localhost:8080/about
Related
I'm using Axios in a Vue app. I would like to send a request to my backend (to log the click of a button) but not wait for the response and then redirect the client to another page. Right now, the request is made, the redirect happens right away, and the initial request is 'cancelled' and my backend doesn't process the request.
I'd like to just fire off that request, then redirect AND NOT cancel the initial request. How can I do that?
Thanks all for the comments. Seems sendBeacon seems like it's been nerfed as you cannot send application/json for content type, and it this article says it is not reliable. So I used the following solution in my Vue component:
onClickLink: async function () {
var img = new Image()
img.src = `/my/path/to/controller/${this.$route.params.id}`
window.location.href = externalLinksFile.new_url
}
I want to send a post request from inside an iframe in laravel. I have the script file (from which I'm sending the request) in a folder in public (public/scripts/subscript/main.js). And my example request looks like this in the main.js file:
$.post("./server/update-data", { id: 1 }, function(data){
console.log(data);
}).fail(function() {
alert("ERROR");
});
//route
Route::post('/server/update-data', 'PageController#goToServer');
The specific issue is that the request is going through the parent route then to the route given, e.g
(localhost:8000/parent/server/update-data)
but should look like this
(localhost:8000/server/update-data)
which should not be. How do I skip the parent route?
Please note that this same code works for requests that are not from inside the iFrame.
I am trying to create web pages which will accept a login, redirect to a data page all done through API / CRUD in Python and html pages with scripts. All this is for a college assignment.
So, when the user lands on the home page they're redirected to the login-page. In this they enter a Username and Password and click a submit button. All this goes well.
However, on clicking submit, I'm getting the error mentioned in the title. I've validated the JSON in JSONLint.com. The 'return' in the API is a redirect and is received and shows up in the console as an error.
The script of the login page is:
<script>
function login(){
// used to create the login
var userLogin={}
userLogin.uname=document.getElementById("loginUsername").value
userLogin.pass=document.getElementById("loginPassword").value
$.ajax({
"url":"http://127.0.0.1:5000/processlogin",
"method":"POST",
"data":JSON.stringify(userLogin),
"dataType":"json",
contentType:"application/json; charset=utf-8",
"success":function(result){
console.log(result)
alert("Logged In");
},
"error":function(xhr,status,error){
console.log("error: " + status + ", message: " + error);
console.log(JSON.stringify(userLogin))
console.warn(xhr.responseText)
}
})
}
</script>
The API in Python is:
#app.route('/processlogin',methods=['GET','POST'])
def process_login():
# check credentials
# if bad, redirect to login page again
#content=request.get_json(force=True)
#print(content)
session['username']='got it'
return redirect('data.html')
and the console in the browser shows:
When I put return 'done' in the Python API, the browser console shows this in the error instead of the HTML code.
Any advice gratefully received.
Seamus
The dataType argument in the ajax call means that a JSON return value is expected, but you are returning html. JQuery tries to deserialise the response and fails.
Ajax isn't really necessary here if you want to redirect the browser to a new page. Better to submit the form in the usual way and on success redirect to a route that will serve data.html as a response.
If the redirect must be performed via Ajax, respond with a redirect to a route that serves data.html on the server side. In the ajax handler, get the redirect url from the response's location header, fetch the html and load it.
I have an ajax post call and I want to just send the form values without waiting for a response to come. I want to redirect from my express app and not form my client side. server-side:
router.post("/", (req, res)=>{
res.status(200).send(`signup/confirm-email?v=${req.body.email}`);
// i want to be able to rediect with res.redirect("signup/confirm-email?v=${req.body.email}")
};
});
client-side:
$.post('/signup', $('#signup-form-container form').serialize())
.done(lnk=>{
window.location.replace(`${window.location}${lnk}`); })
.fail(function(xhr){ //handle errors };
The code above works, it sends a response to ajax and then i redirect from client side.
I want to redirect from server-side.
I tried to redirect from my express app using res.redirect() but it doesn't work without logging any errors to the console and in the network tab in the dev tool, it shows that the request type is xhr. If there isn't a solution to this problem, than is there a way to hide the query in the route recieved. I don't want v=${req.body.email} to be displayed in the url bar. Thank you in advance.
The point of Ajax is that the request is made by JavaScript and the response is handled by JavaScript.
If you return a redirect response, then JavaScript will follow the redirect, make a new request, and handle the response to that.
If you don't want to handle the response with JavaScript: Don't use Ajax. Use a regular form submission instead.
Avoid using jQuery, it will almost always lead to bad practices and unmaintainable code.
You should use plain Javascript or React if you need a framework (which is mainly if you need to work on a big project, but may not be suited for total JS beginner). My solution here is in plain Javascript.
As a replacement for the jQuery request util ($.post(), etc ...), browsers have a very good HTTP request API named fetch (MDN). I use it in this solution.
If you don't want to show the email param in the URL, you can set it in the localStorage.
You cannot redirect directly from Express for a POST method. Only if you were trying to GET a page, Express could redirect to another GET (and so change the URL in your browser).
The only solution to your need is to use the window.location after receiving the response from your /signup endpoint.
Server-side:
router.post('/', function (req, res) {
const form = req.body.form
// your registration code here
// ...
res.json({
success: true,
email: form.email
})
})
Client-side:
signup.js
const form = {
email: document.getElementById('email').value
// add other fields you need
}
fetch('/signup', {
method: 'POST',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({ form })
})
.then(res => res.json())
.then(res => {
// Set the localStorage item so you can get it back
// in the JS script of /signup/confirm-email
localStorage.setItem('email', res.email)
// Redirect to the confirm email page
window.location = '/signup/confirm-email'
})
Client-side:
confirmEmail.js
const email = localStorage.getItem('email')
if (!email) window.location = '/signup'
// proceed with your code
Code for this answer
I have a web site that is trying to call an MVC controller action on another web site. These sites are both setup as relying party trusts in AD FS 2.0. Everything authenticates and works fine when opening pages in the browser window between the two sites. However, when trying to call a controller action from JavaScript using the jQuery AJAX method it always fails. Here is a code snippet of what I'm trying to do...
$.ajax({
url: "relyingPartySite/Controller/Action",
data: { foobar },
dataType: "json",
type: "POST",
async: false,
cache: false,
success: function (data) {
// do something here
},
error: function (data, status) {
alert(status);
}
});
The issue is that AD FS uses JavaScript to post a hidden html form to the relying party.
When tracing with Fiddler I can see it get to the AD FS site and return this html form which should post and redirect to the controller action authenticated. The problem is this form is coming back as the result of the ajax request and obviously going to fail with a parser error since the ajax request expects json from the controller action. It seems like this would be a common scenario, so what is the proper way to communicate with AD FS from AJAX and handle this redirection?
You have two options.
More info here.
The first is to share a session cookie between an entry application (one that is HTML based) and your API solutions. You configure both applications to use the same WIF cookie. This only works if both applications are on the same root domain.
See the above post or this stackoverflow question.
The other option is to disable the passiveRedirect for AJAX requests (as Gutek's answer). This will return a http status code of 401 which you can handle in Javascript.
When you detect the 401, you load a dummy page (or a "Authenticating" dialog which could double as a login dialog if credentials need to be given again) in an iFrame. When the iFrame has completed you then attempt the call again. This time the session cookie will be present on the call and it should succeed.
//Requires Jquery 1.9+
var webAPIHtmlPage = "http://webapi.somedomain/preauth.html"
function authenticate() {
return $.Deferred(function (d) {
//Potentially could make this into a little popup layer
//that shows we are authenticating, and allows for re-authentication if needed
var iFrame = $("<iframe></iframe>");
iFrame.hide();
iFrame.appendTo("body");
iFrame.attr('src', webAPIHtmlPage);
iFrame.load(function () {
iFrame.remove();
d.resolve();
});
});
};
function makeCall() {
return $.getJSON(uri)
.then(function(data) {
return $.Deferred(function(d) { d.resolve(data); });
},
function(error) {
if (error.status == 401) {
//Authenticating,
//TODO:should add a check to prevnet infinite loop
return authenticate().then(function() {
//Making the call again
return makeCall();
});
} else {
return $.Deferred(function(d) {
d.reject(error);
});
}
});
}
If you do not want to receive HTML with the link you can handle AuthorizationFailed on WSFederationAuthenticationModule and set RedirectToIdentityProvider to false on Ajax calls only.
for example:
FederatedAuthentication.WSFederationAuthenticationModule.AuthorizationFailed += (sender, e) =>
{
if (Context.Request.RequestContext.HttpContext.Request.IsAjaxRequest())
{
e.RedirectToIdentityProvider = false;
}
};
This with Authorize attribute will return you status code 401 and if you want to have something different, then you can implement own Authorize attribute and write special code on Ajax Request.
In the project which I currently work with, we had the same issue with SAML token expiration on the clientside and causing issues with ajax calls. In our particular case we needed all requests to be enqueud after the first 401 is encountered and after successful authentication all of them could be resent. The authentication uses the iframe solution suggested by Adam Mills, but also goes a little further in case user credentials need to be entered, which is done by displaying a dialog informing the user to login on an external view (since ADFS does not allow displaying login page in an iframe atleast not default configuration) during which waiting request are waiting to be finished but the user needs to login on from an external page. The waiting requests can also be rejected if user chooses to Cancel and in those cases jquery error will be called for each request.
Here's a link to a gist with the example code:
https://gist.github.com/kavhad/bb0d8e4a446496a6c05a
Note my code is based on usage of jquery for handling all ajax request. If your ajax request are being handled by vanilla javascript, other libraries or frameworks then you can perhaps find some inspiration in this example. The usage of jquery ui is only because of the dialog and stands for a small portion of the code which could easly be swapped out.
Update
Sorry I changed my github account name and that's why link did not work. It should work now.
First of all you say you are trying to make an ajax call to another website, does your call conforms to same origin policy of web browsers? If it does then you are expecting html as a response from your server, changedatatype of the ajax call to dataType: "html", then insert the form into your DOM.
Perhaps the 2 first posts of this serie will help you. They consider ADFS and AJAX requests
What I think I would try to do is to see why the authentication cookies are not transmitted through ajax, and find a mean to send them with my request. Or wrap the ajax call in a function that pre authenticate by retrieving the html form, appending it hidden to the DOM, submitting it (it will hopefully set the good cookies) then send the appropriate request you wanted to send originally
You can do only this type of datatype
"xml": Treat the response as an XML document that can be processed via jQuery.
"html": Treat the response as HTML (plain text); included script tags are evaluated.
"script": Evaluates the response as JavaScript and evaluates it.
"json": Evaluates the response as JSON and sends a JavaScript Object to the success callback.
If you can see in your fiddler that is returning only html then change your data type to html or if that only a script code then you can use script.
You should create a file anyname like json.php and then put the connection to the relayparty website this should works
$.ajax({
url: "json.php",
data: { foobar },
dataType: "json",
type: "POST",
async: false,
cache: false,
success: function (data) {
// do something here
},
error: function (data, status) {
alert(status);
}
});