I'm having this issue where if I navigate to a page (through a named route), say for example /privacy, directly through the url, it works fine. When I click the refresh button, it stays on the page for a second, and goes to the root url ("/") and then traverses to a different page.
Here is a bit of my code:
routes: {
Landing.route: (context) => Landing(),
PrivPol.route: (context) => PrivPol(),
"/": (context) => Loading(),
},
initialRoute: "/",
and from the tutorials I've been looking at if I directly type in localhost:port/{PrivPol.route} it should go to and stay on that page, even if I refresh the page.
So it turns out that in one of the other files, I had a future that was being called, which was causing the re-route to take place!
Related
I have this (in my contact.js file).
axios
.post('http://localhost:3000/create', book)
.then(() => console.log('Created'))
.then(() => this.props.history.push({Home}))
.catch(err => {
console.error(err);
});
It calls this successfully, but now I want to render a new page, which would be home.js, in the same folder. I was wondering how I can do this, after it makes the call to the backend. Can I do it from the front end, or does it have to be backend?
If the url location of your home.js page is '/home', you can trigger a refresh of the page with the history.pushState method.
Though, you router descending from your React framework may have some implemented method to accomplish this task too. Keep in mind that the history API is access to to the navigator history, allowing you to trigger render of a previous page, a refresh, etc..
You access to the history API by the DOM Window object, for instance a call to the pushState method should look like :
window.history.pushState(null, "", "/Home");
Otherwise you can also use the location API from the BOM object
location.assign('/Home');
which is similar to the window.history.pushState method
https://www.javascripttutorial.net/javascript-bom/javascript-redirect/
Background Information
I am working on a NodeJS w/ Express project.
I am using some auth middleware, we can call it isAuthenticated()
The middleware is just protecting certain routes w/ passport, etc.
I am adding a file download link that is visible when a user is not authenticated yet. (i.e. the button is publicly visible).
If you click the button, the download route is protected, so we redirect you to a sign in page.
The Problem
Once the user authenticates, I am calling res.download(file);. The download works fine, but the user is still at the sign in page with their credentials typed in. (Note: the user is authenticated, but no further redirect is happening on this route). If I try res.download(file) and then make a call to res.redirect('/'), the file isn't downloaded.
Basically: I want to redirect the user to the home page at 'website.com/', but the approaches I've taken haven't worked.
Main Approach:
Set res.local.downloadFile2 = true in the router.get('/download/file2') route. Then redirect to home using res.redirect('/'). Now, in the home route, I would just do the following after rendering the home page again.
if (res.locals.downloadFile2 === true) {
res.download(file2)
}
However: res.locals.downloadFile2 === undefined when that check is being done. I've tried to step through and somewhere it is being reset in one of the _modules I am using, so this is not working. I am currently unsure of the cause.
I could probably solve this by not displaying the links until a user is Authenticated, then there shouldn't be any cases of redirecting to login and back again, which means they would never see the login page, etc. but that is not the right solution to this problem, and is not the one I want to implement.
I would really appreciate any help, and can provide more information if needed!
Thanks in advance.
The issue is that after the redirect, the res object is a complete new object due to the browser issued a new request to follow the redirect. That's why res.locals.downloadFile2 is undefined.
To solve your problem, you can replace your code res.local.downloadFile2 = true with code to set a cookie. The cookie holds information which will be stored on client side by the browser and send to the server with every request.
See cookies npm package for example:
var Cookies = require('cookies');
var cookies = new Cookies(req, res, { keys: ['arbitrary string to encrypt the cookie'] })
// get value of the cookie
var need_to_download_file = cookies.get('need_to_download_file', { signed: true })
// Logic to download file (if necessary)
if (need_to_download_file) {
// Trigger download of the file
// Reset cookie
cookies.set('need_to_download_file', false, { signed: true })
}
// set the cookie
cookies.set('need_to_download_file', true, { signed: true })
I have both a React APP and a Express API server on the same server/domain. Nginx is serving the React APP and proxying the express server to /api.
Nginx configuration
https://gist.github.com/dvriv/f4cff6e07fe6f0f241a9f57febd922bb
(Right now I am using the IP directly instead of a domain)
From the React APP, when the user does something I want him to download a file. I used a express route on my API server that serve the file. This works fine when the user put the URL.
This is my express route:
donwloadFile.route('/')
.get((req, res) => {
const file = '/tmp/PASOP180901.txt';
res.download(file);
});
This is my react redirect:
if (this.state.downloadFile === true) {
this.setState({ downloadFile: false });
setTimeout(() => {
window.location.href = '/api/downloadFile';
}, 100);
}
The address changes but the download don't start. If I press F5 then the download starts just fine. If I use a external URL to host my file, the download start just fine too.
Thanks
First things first. Don't use setTimeout, but rather use the callback function of setState to execute code after the state is set ensuring it has been modified. Calling the callback function will guarantee the state is changed before that code in the callback is executed.
from the official docs:
setState() enqueues changes to the component state and tells React
that this component and its children need to be re-rendered with the
updated state. This is the primary method you use to update the user
interface in response to event handlers and server responses.
setState() does not always immediately update the component. It may
batch or defer the update until later. This makes reading this.state
right after calling setState() a potential pitfall. Instead, use
componentDidUpdate or a setState callback (setState(updater,
callback)), either of which are guaranteed to fire after the update
has been applied.
setState(stateChange[, callback])
The second parameter to setState() is an optional callback function
that will be executed once setState is completed and the component is
re-rendered. Generally we recommend using componentDidUpdate() for
such logic instead.
So, instead of:
if (this.state.downloadFile === true) {
this.setState({ downloadFile: false });
setTimeout(() => {
// execute code, or redirect, or whatever
}, 100);
}
you should do:
if (this.state.downloadFile === true) {
this.setState({ downloadFile: false }, () => {
// execute code, or redirect, or whatever
});
}
Now, for your specific problem
Set headers in your server side
You can set the Content-Disposition header to tell the browser to download the attachment:
from here:
In a regular HTTP response, the Content-Disposition response header is
a header indicating if the content is expected to be displayed inline
in the browser, that is, as a Web page or as part of a Web page, or as
an attachment, that is downloaded and saved locally.
Set it like this:
('Content-Disposition: attachment; filename="/tmp/PASOP180901.txt"');
Force download from the client
There are multiple ways to force the download from the client, but this is the only one I've tried.
For this to work, you have to have the content of text somehow in the client (your express route can return it for example) and create a filename for the file that will be downloaded.
let element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
Basically you are creating an empty link component, setting the data attribute to it with the text's content, attaching the link to the body, clicking the link and then removing the link from the body.
Open the link in a new tab
Opening the link in a new tab will trigger the download as well:
window.open('/api/downloadFile');
Redirect programatically
Have a look at this question in SO
You can do this:
this.props.history.push("/api/downloadFile")?
If cannot access this.props.history you can import { withRouter } from 'react-router-dom'; and export default withRouter(yourComponent); to access it.
My web application is created with Spark Framework
(Connecting on same page as server) the url of the first page is http://localhost:4567/start
From here the user clicks on a button to decide one of four tasks.
The form action button is /start
The server checks everything is okay and then returns the new page for this task (e.g fixsongs) (i.e returns the page contents as a string ) from the page.
post(RoutePath.START, (request, response) -> new ServerStart().processRoute(request, response));//User has picked task on start page
ServerFixSongs ssfs = new ServerFixSongs();
path(RoutePath.STARTFIXSONGS, () ->
{
//Display Page
post(RoutePath.FIX, (request, response) -> ssfs.startTask(request, response)); //User submits form on Options page
});
The problem is url stays the same, i.e is one behind where the user is
Now I have worked out how to solve this, instead of the server returning page
it now does a redirect to /fixsongs.go (this is mapped in routes) which calls method that then returns the page contents as a string and modifys the url.
post(RoutePath.START, (request, response) -> new ServerStart().processRoute(request, response));//User has picked task on start page
ServerFixSongs ssfs = new ServerFixSongs();
path(RoutePath.STARTFIXSONGS, () ->
{
get(RoutePath.GO, (request, response) -> new FixSongsPage(request.session().attribute(FOLDER)).createPage(null, null)); //Display Page
post(RoutePath.FIX, (request, response) -> ssfs.startTask(request, response)); //User submits form on Options page
});
But I have two questions
Is this more cumbersome approach the correct way to do this
Do these additional redirect steps impact performance
Note I am not using templating but creating webpages using j2html
I cannot do a redirect directly to a html file in the first call since the html does not actually exist, the pages are created dynamically.
I have also realised that although when I submit start task from START page i submit a POST request because Im redirecting to STARTFIXSONGS this means at the next stage user can use BACK button to go back to STARTFIXSONGS. I would prefer they could not do this, so does this mean i shoud not be using redirects in this case.
Your question is not really related to Spark or j2html, but rather web-applications in general.
Is this more cumbersome approach the correct way to do this
It depends on what you want. If you want users to be able to navigate using the browser buttons, use the Post-Redirect-Get flow. If you don't want this you should probably be using ajax requests, where you post to the server using JavaScript and the server responds with instructions for updating the DOM (usually in JSON format)
Do these additional redirect steps impact performance
Not enough that you should worry about it. You get one more round-trip, but in 99.9% of cases it doesn't matter. If your target user-base is browsing your page using GPRS on a different continent it would, but that's probably not the case.
I am wanting to password protect my laravel application, ideally just on the deployment server (I'm using Fortrabbit), however, I don't mind logging in to my local development server.
I initially thought a route filter would suffice, but this doesn't seem to work:
Route::get('/', array('before' => 'auth.basic', function()
{
// Only authenticated users may enter...
}));
At the top of my route file, this is completely ineffective, however, at the bottom of my route file, it does seem to work, but if I physically type a sub-directory in, i.e. localhost:8888/user/ it seems to override it.
Any guidance as to how you show your applications to clients (without Google, anyone else finding them), would be hugely appreciated.
You need to apply the 'before' auth filter to all routes that require it.
The reason why it does not work at the top of your routes file is probably because you're specifying another GET route pointing to '/', whereas at the bottom of the file it will work fine since the route with auth.basic overwrites it.
You can do something like this to specify that all routes should be protected:
Route::group(array('before' => 'auth.basic'), function()
{
// all your routes placed in here will be protected by auth.basic
});
Can you make a group around your routes.
http://laravel.com/docs/routing#route-groups
(as suggested before me I see so I borrowed the code (give credit to that poster))
Route::group(array('before' => 'auth.basic'), function()
{
// all your routes placed in here will be protected by auth.basic
});
or
maybe you can use a patern based "*" wildcard at your routes?
Route::get('*', array('before' => 'auth.basic', function() {
// Only authenticated users may enter... }));
http://laravel.com/docs/routing#route-filters
Is a .htaccess file possible at fortrabbit?
than maybe use: http://www.htaccesstools.com/articles/password-protection/
Sometimes I use Pagekite to temporarily allow access to a site on my local development box : https://pagekite.net/