Browser History React Router - react-router

I am trying to configure my browserHistory. My route is
<Route path="/test" component = {App} />
It works fine if I create a link. But if I put localhost/test in the browser or url I get a 404 error. I assume it cant find it on the server.
Can someone please help me? I am new to react-router. Do I have to configure the server side?
Thank you so much in advance.

Yes, as mentioned in React Router documentation you must configure your server so that it always returns your index page, no matter which path the browser requests.
Using express, supposing you have a /public/index.html file, this would work:
/* Your express includes and init code would go here... */
// Serve static assets normally
app.use(express.static(__dirname + '/public'))
// Handle every other route with index.html, which will contain
// a script tag to your application's JavaScript file(s).
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, 'public', 'index.html'))
})
The important thing is app.get('*', ..., which will return the same thing (in this case your index.html file), no matter which path is requested from your browser.
Hope that helps.

Related

How to have Express.js server file using router path as root?

I have a directory structure like this
root
private
js
css
index.html
app.js
privateRouter.js
...
In my index.html I reference the js and css files using relative path like this:
<link rel="stylesheet" href="css/index.css"/>
<script src="js/index.js"></script>
On the server side I have a private router checks for authentication before serving these files:
In app.js:
app.use("/private", privateRouter);
in privateRouter.js:
router.use((req, res, next) => {
isAuthenticated(req) ? next() : res.redirect("/");
});
router.get('/', function(req, res, next) {
res.sendFile(require('path').join(__dirname + "/private/index.html"));
});
So now if I visit http://mywebsite.com/private I will get the the index.html, but the request of js/css files from the browser comes back to the server as http://mywebsite.com/js instead of http://mywebsite.com/private/js, thus returns file not found.
Alternatively if I serve files statically, the browser knows to request from /private/js:
app.use("/private", express.static(path.join(__dirname, 'private')));
But I can't server statically because I need to authenticate specific files and not serve all files publicly. And I don't want to have to append private/ before all file references on client side. I don't understand why when serving statically the server knows to use /private as root but when using a router it uses / as root.
How can I keep the url as http://mywebsite.com/private while not having to append private/ in all file references on the client side?
I'm new to express.js and it was also difficult for me to formulate the question. I think I'm fundamentally misunderstanding something about express.js works. Thanks for your help!
You can modify a request's URL using middleware for internal routing:
app.use((req,res,next)=>{
req.url = "/private" + req.url; // prepend "/private" to the url
next();
});
You can also set the base URL using the base tag, which means you only need to specify the base URL once per page.
<html>
<head>
<base href="private">
<link rel="stylesheet" href="css/index.css"/>
<script src="js/index.js"></script>
</head>
</html>
This should then send the requests to:
/private/css/index.css
/private/js/index.js
However, you will still need code to handle these paths, otherwise, you will still get the 404 not found error.

Universal routing with express and react router. Understanding history behaviour

I am using React Router 4.0 and Express 4.14 to create an app that has a mix of single-page-app (SPA) and multi-page-app (MPA). I don't know if that's good practice, but this is not the point. I am actually doing it to learn rather than for a real world app. This idea comes from the scenario where you have strongly separated sections inside an app, as for example a blog and a portfolio.
Client side
So, when I want to navigate as a SPA, I use the Link component from react-router-dom, like <Link to="/reactrouter-route">. If I want to make a request to a route handled by the server, I use <a href="/server-route">.
Server side
I have a middleware logging the path of any request received by my server. I define two routes, each serving a complete SPA. To keep with the blog/portfolio example, imagine I have the following
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log(req.path);
next();
});
app.get('/', (req, res) => {
res.sendFile('blog.html');
});
app.get('/portfolio', (req, res) => {
res.sendFile('portfolio.html');
});
Behaviour
When I go to / the blog gets loaded as a SPA and I can go to the different posts navigating back to / when I want. Everything works as expected. All this navigation inside the SPA is managed by React Router, and the server only gets the first request to /.
Imagine that from a specific post, say /posts/some-post, I have a link to the portfolio. If I click it, I get a request at the server, and it responds with the portfolio SPA. I can navigate inside the portfolio SPA, but I cannot go back to /posts/some-post. I get the following error:
Cannot GET /posts/some-post
I thought the error was thrown by the server, but surprisingly I don't get any request when going back. I only get requests at the server when going forward through a link (only with <a>).
I kept doing tests and there is no problem if I go back from /portfolio to /. This works as expected.
It gets interesting
I defined a route in my server with just the same rule that I had in my React Router routes. The path I was matching in this new route was /posts/:postid. I set this route to redirect to /. Now, I get the same error if I go from posts/some-post to /portfolio and I try to come back. This is not strange as the server doesn't get a request. It's also normal that I reach / if I go straight to /posts/some-post by typing in the URL in the browser.
But, once I go to /posts/some-post manually, I can go from /portfolio back to /posts/some-post without the error. Now it behaves as if the server was called. In fact, I get a request in the server to fetch the static files. However, I don't get a request to /posts/some-post nor /.
Even then, I would get an error if a go from /posts/some-other-post to /portfolio and try to go back.
Question
I guess this has to do with the cache, but I don't know what is going on there. When is the React Router handling going back? When is the server handlin it? How is the cache involved in this process?
It sounds like you need a clearer mental model of the roles of the server and the client in an SPA. "Single Page" is the important part.
The client, built with React, should never be loading pages from the server. It should be a "single page". In other words, you should not be using <a href="/server-route"> in your client app at all. The client should only get (JSON) data from the server using something like fetch (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
I highly suggest you check out Create React App which also explains how to integrate with a node API backend during development. Basically you want all your client routes to be something like /post/:postid which will be handled by React Router and then that React component would use fetch to get the data from something like /api/posts/10. If you use /api to prefix all your requests to the server it should help your mental model.

Node.js: My HTML requires an image

See, I was training with Node (and TS btw), and tried to do a trivial server with multiple request/response options. But I have a problem I don't know how to solve without using Express (at least for now I don't want to use it).
I have a HTML file which requests an image file. While in the IDE, everything looks like it's going to work, but when the server is running, the image cannot be found. It's kind of obvious why: The HTML makes a request the server doesn't know how to handle. Thing is, I thought the document could refer to other files without the need of talking to the server.
What is an elegant and working solution for my problem?
Thanks in advance.
import * as http from 'http'
import * as fs from 'fs'
fs.readFile('doc/kmCNHkq.jpg', function (err, data) {
let binaryimg = new Buffer(data).toString('base64');
if (err) throw err;
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'image/jpeg'});
res.end(data);
console.log("Delivered the jpeg");
}).listen(8000);
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(binaryimg);
console.log("Delivered base64 string");
}).listen(8124);
console.log("Unless bug, both servers are listening");
});
fs.readFile('doc/index.html', function(err, data) {
http.createServer(function(req,res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data)
}).listen(80);
console.log("HTML server is running")
})
(main.ts; Targets ES6)
<html>
<head>
</head>
<body>
<img src="doc/kmCNHkq.jpg"/>
</body>
</html>
(index.html)
Observation: I used to leave the HTML file in '../doc/' and resources on '../img/' however it seems that the HTML uses relative paths, so I copied the image into HTML's folder. If the solution also made it so I could leave the resources on their's respective folders it would be much appreciated.
#Edit:
Now I'm using this switch/case request handler. Working as expected, the HTML's request for the image is interpreted as a normal request (which may not end up scaling well, idk, but screw it). Thanks a lot!
import * as http from 'http'
import * as fs from 'fs'
var stream: fs.ReadStream,
folder = __dirname.substr(0, __dirname.length - 3);
http.createServer(function(req,res) {
switch (req.url){
case('/jpeg'):
stream = fs.createReadStream(folder + 'img/kmCNHkq.jpg');
stream.pipe(res);
console.log("Delivering the jpeg");
break;
case('/base64'):
fs.readFile('img/kmCNHkq.jpg', function (err, data) {
let img64 = new Buffer(data).toString('base64');
if (err) throw err;
res.end(img64);
console.log("Delivered base64 string");
})
break;
case('/html'):
stream = fs.createReadStream(folder + 'doc/index.html');
stream.pipe(res);
console.log("Sending the docs");
break;
default:
console.log("Shit happens");
}
}).listen(80)
(main.ts)
<html>
<body>
<img src="jpeg"/>
</body>
</html>
(index.html)
Short answer:
You won't be able to refer to specific resources on the server (such as your image) unless your server knows how to respond to those requests for that content. It looks like you can probably make your example work easily immediately though by changing the image src to just http://localhost:8000 though.
Longer answer:
Using 'doc/kmCNHkq.jpg' as the src for your image tells your browser that when it loads the page, it should go to the server it got the page from, and ask it for the 'doc/kmCNHkq.jpg' resource. If you specify a full URL including the protocol (the http://) then it will be absolute, instead of relative, so you can request from a different server than the one that served the page.
The servers that you've written don't actually look at the path of the file that's requested though (req.url), and actually they just always return the same content. If you connect to http://localhost:80 (the third server you've created above), and do request that jpg you'll still just get given the same HTML data of the page, because it just runs the two lines in your createServer call at the end of your example. You have written a server that always returns the image however above (the first server), just running on a different port, which is why the above solution works.
Just using that existing server is the simplest solution. The far more conventional approach though is to have just a single HTTP server running on a single port (instead of the 3 you have) and to use req.url to decide what data to return.
Traditionally for static content that means mapping a requested path directly onto the layout of the files on disk, so that requesting doc/abc.jpg looks for a doc folder in the server's directory, and returns the data from abc.jpg therein. That's not required necessarily at all though, and your server can interpret those paths however you like, to return content from anywhere.
(Note than none of this is really anything to do with TypeScript, or even much to do with Node.js. This is really just the essentials of how HTTP servers and browsers interact, and it would be almost identical with any other backing technology. I'd take a look more into the general HTTP and browser details if you're looking to get more background on this.)

Polymer - url rooting after deployment to subdirectory

Ive created a basic Polymer app from the starter kit (via Yeoman). I've today deployed it to the 'sandbox' on my domain and am getting a strange routing issue. The app is essentially a feed reader.
View app here
When I first visit the app I'm given a blank page whereas locally I'm taken straight to the feed. When clicking on 'News Feed' I'm then taken to the feed as expected.
I've added a route for the path of the domain structure as below but this did not fix it.
You can view the full code of the project here.
routing.html
page('/', function () {
app.route = 'home';
});
page('http://purelywebdesign.co.uk/sandbox/f1feedreader/', function () {
app.route = 'home';
});
I've also tried:
page('/sandbox/f1feedreader/', function () {
app.route = 'home';
});
Any help much appreciated.
Page.js allows you to configure the base path:
page.base('/sandbox/f1feedreader/');
or just use window.location if you don't want to tie is to that specific deployment.
page.base(window.location.pathname);
This is an issue with the way the router page.js works. I assume you were testing with gulp serve (which creates a server and sets the web app base url of "/" to be localhost:3000/). The way you're currently setting your page.js routes is that it's looking exactly after the domain name and not at the "root" of the web directory.
In your case page.js is looking at everything after http://purelywebdesign.co.uk/ (meaning all your routes include should start from sandbox/f1feedreader instead of just /f1feedreader).
The documentation for page.js https://visionmedia.github.io/page.js/ says that it uses regular expressions so you could also update the strings.

Serving Dynamic Webpages with Node.js

I am having a hard time understanding how exactly node.js serves dynamic content.
So let's say we have the following code that renders a home page:
var express = require('express'),
app = express();
app.get('/', function(req,res){
res.render('home.html');
});
However, let's say this home page was supposed to be a user profile in which you are pulling user information from a database, which results in code:
var express = require('express'),
mongoose = require('mongoose'),
app = express();
mongoose.connect('mongodb://localhost/ExampleDB');
app.get('/:id', function(req,res){
User.findOne({_id: req.id}, function (err, user){
var name = user.name;
var profilePic_uri = user.profilePic_uri;
res.render('home.html');
});
So, ideally home.html is just a template page, in which you set maybe the user's profile picture, their name, etc in the route handler. Right, because the idea behind node, is that this app.js should be able to handle pulling the dynamic content from a database at run time. Where I am having trouble is understanding how exactly rendering dynamic pages work with node. The html page is a static page. You can't really render a php or a asp page because, well, that doesn't really make sense does it?
Which leaves me with the question, how is it done?
If you add...
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
...after app=express() has been done then it will now default to the Jade rendering engine if you don't use an extension. Then in your router:
app.get('/', function(req,res){
res.render('home', {
var1: 'val1',
var2: 'val2'
});
});
You'd need to add Jade to your project's package.json file in the dependencies:
"jade": "~1.9.1",
...and then "npm install" in your folder to bring that in.
Then you'd need a file named /views/home.jade with the contents:
doctype html
html
body
p Var1 is #{var1}
p Var2 is #{var2}
You should see--when you visit your home page--that the values have been passed to the Jade rendering engine and expanded in place in the template as 'val1' and 'val2', respectively.
In your case the page is static. Here comes in play template engines (view engines), with this you can render the content of the page dynamic.
Some of the template engines that I remember right now are vash (it's like razor syntax from MVC, I love it) and jade. There are more of them.
Note: You can find in the links I provided how to integrate them with express.
What you are actually asking is not, how it works in Node, but how Express renders templates. Once you understand that you're actually using a feature of the express module, you're probably more aware of what you need to search for to get the correct documentation.
In short: Express features a template rendering engine that does the job for you, very much similar to what ASP or PHP do.
To get to know Node, I'd advise to try to build something without all the libraries first, just to get to know the platform, and to understand why it's feasible to use such libraries or frameworks (such as express).