I am currently using Express to load a one page client side app in React.js and react-router.
It is structured such that I have /about and /contact as standard jade views, but /ui as the actual react app. Any subsequent navigation beyond /ui such as ui/dashboard is handled by react-router. This means that if I need to visit www.foobar.com/ui/dashboard, I would need to hit my route for ui*, grab the url past ui/ (dashboard in this case) and pass that to react router (via variable in the jade view which is the react app's entry point) which then loads this route from a react component. I would like to make this approach work as this enables users to save urls for the react app. Trouble is I can't figure out how this could work as:
I cannot pass variables with express router redirects
I need to load the react app without anything beyond /ui as otherwise react router will append its routing urls in front of it ruining the point in this approach
I cannot store the initial request url in a cookie and then redirect as I need to send a response in order to set a cookie
I cannot satisfactorily modify the url via client side js
Code:
//Example 1
router.get('/ui*', requireLogin, function(req, res){
res.render('./app/index', {initURL: req.path); //Doesnt work as everything past ui/ is still present when react-router does its thing
});
//Example 2
router.get('/ui', requireLogin, function(req, res){
res.render('./app/index', {initURL: req.path); //Doesnt work as no way of accessing the initial requests url when redirecting
});
router.get('/ui*', requireLogin, function(req, res){
res.redirect('/ui'); //Cant pass additional parameters here
});
var express = require('express')
var React = require('react')
var Router = require('react-router')
var routes = require('./routes')
var app = express()
app.use('/ui', requireLogin, function(req, res, next) {
// store initially requested url as a query parameter
// e.g. /ui/dashboard would become /ui?redirect=dashboard
// if the user is not logged in
var params = req.query;
// params.redirect === 'dashboard'
// Now you can check if there is a redirect and use it in your initial route
var router = Router.create({location: req.url, routes: routes})
router.run(function(Handler, state) {
var html = React.renderToString(<Handler/>, {initalState: params.redirect})
return res.render('react_page', {html: html})
})
})
Does this get you on the right track?
I think you have to distinguish your client side routing and your server side routing.
You will need a server side routing:
the client needs to load the static content (*.html, *.js, *.css)
the client needs to authenticate, and load the data he needs (/api/...)
You will also need a client side routing framework (like ui.router for angular.js):
you will split your client application logically in different routes that the browser (user) can remember, like /, /dashboard, /...
Regards, Remy
Related
I need Express server to serve static files (website). It works in my code well:
var express = require('express');
var app = express();
var path = require('path');
var p = path.join(__dirname, '../web/public');
app.use("/files", function (req, res) {
return res.send("I will do something on server");
});
app.use(express.static(p));
app.use('/*',express.static(p));
let port = 80;
var server = app.listen(port);
But when I have a form served by this server, it redirects after submit, and the page displays only something like
Cannot POST /dashboard/
How can I correctly handle post requests within the meaning of serving static files? I tried to add this right before declaring the port:
app.post(express.static(p));
app.post('/*', express.static(p));
But it doesn't work.
PS: my entire page is served (I want that) from index.html. In order to do that, I have in my code that:
app.use(express.static(p));
app.use('/*',express.static(p));
How do I "redirect" post requests to index.html? In this file I have the entire app logic (it is a single page app).
PS2: I really need default submit. Going around that is not a solution for me (I want to offer the user to save their login and password, and without submitting the form, Google chrome will not offer that to users). And on the other side I really need use POST, as the login form is transferring sensitive data...
Any ideas?
Thanks!
There is no file called dashboard so there is nothing for express.static to serve.
To handle that dashboard route, you can do this:
app.post("/dashboard", function (req, res) {
return res.send("I will do something with this post");
});
If you want to redirect 404 traffic to index.html, you can do this after all your route definitions:
app.use("/", function (req, res) {
return res.redirect("/index.html");
});
I am attempting to build a single page app using Express.js. On my index.html page, I have a basic form, which upon submit will make a request to an API, retrieve the data, parse it, and then I want to render the parsed data as a list of links. Right now I am able to render the index page, and I can make the submission call to the API from the form that I added to the page. What I am confused about is how to properly redirect the data I get from the API call and then render it on the same page. I've built simple apps using Express before where there were multiple views, but never a single page app. For those apps, I know that for the response you could call something like res.render('name of view to render', data), but that is not working in this case. I've tried to find some solutions through this site and via the Express docs, but I have not seen anything that didn't also include using another framework like Angular. For the purposes of this app, I need to not include any additional frameworks, and I am a bit lost.
My function for calling the API looks like this right now. When it is called, I am directed to a page that just has the json displayed
app.use(express.static(path.join(__dirname, '/public')));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use('/', express.static(path.join(__dirname, 'public')));
app.get('/search', function(req, res) {
var title = req.query.movieTitle;
var url = 'http://www.omdbapi.com/?s=' + title;
request(url, function (err, response, body) {
var results = JSON.parse(body);
var movieTitles = results.Search;
console.log(movieTitles);
res.send(movieTitles);
});
});
The basic thing you have to do is:
define routes which your back-end app has to respond with the spa
send your "skeleton" file to the response on that routes
Example code:
const handler = (req, res) => res.send(path.join(__dirname, "path/to/your/index.html"))
const routes = ["/", "/hello", "/world"]
routes.forEach( route => app.get(route, handler) )
This should get you started
A Node.js app needs to:
1.) receive data from another serve that calls /some_endpoint,
2.) process the data it receives to create a String result jwtString,
3.) redirect the user to the root / url of the AngularJS app while passing the jwtString as a res.header included inside the res.redirect.
What specific changes need to be made to the Node.js routing code below in order for the _jwt header to be successfully passed from the Node.js app to the client AngularJS at at /?
app.get('/some_endpoint', function(req, res) {
//Process data from another server that was redirected here.
//then send the result of that processing in jwtString while
//redirecting user to the root url of the Angular app that
//is served by this Node.js instance.
res.setHeader('_jwt', jwtString);
res.redirect('/');
});
app.get('*', function(req, res) {
var queryData = url.parse(req.url, true);
console.log('in get *, queryData is: ');console.log(queryData);
res.sendfile('./public/index.html'); // load the single view file (angular will handle the front-end)
});
When I run the code above, I cannot seem to find the _jwt header in the Network tab of the FireFox developer tools.
I'm using the mean.js (http://meanjs.org/generator.html) boilerplate as a starting point for an app, as I love the inbuilt authentication/authorization.
However, I'm having a problem with using HTML5.
In my angular app i've set HTML5(true) and I know that I need to set up a catchall route for all the other requests to be redirected.
I have the following routes on my express as the root and catchall:
///this is the app/controllers/core.server.controller:
exports.index = function(req, res) {
res.render('index', {
user: req.user || null,
request: req
});
};
exports.catchall = function(req, res){
res.redirect('/');
};
And then the routing itself
'use strict';
module.exports = function(app) {
// Root routing
var core = require('../../app/controllers/core.server.controller');
app.route('/').get(core.index);
app.route('*').get(core.catchall);
};
now the pages are redirecting no problem when I enter routes that are just garbage, but when I enter a route that exists in express (with no associated view I'm getting server output).
Here is the route i mean for express:
'use strict';
/**
* Module dependencies.
*/
var users = require('../../app/controllers/users.server.controller'),
articles = require('../../app/controllers/articles.server.controller');
module.exports = function(app) {
// Article Routes
app.route('/articles')
.get(articles.list)
.post(users.requiresLogin, articles.create);
// Finish by binding the article middleware
app.param('articleId', articles.articleByID);
};
I have no view associated with this in Express/Node - just in Angular.
so when I navigate to localhost:3000/articles via a link the angular manages the route and renders the correct angular template.
However, when enter localhost:3000/articles and press enter (or refresh the browser on this url) I get the following:
[{"_id":"5555ede9dac9d0d99efae164","user":{"_id":"5554fa21141c5aef7b0354d7","displayName":"heny as"},"__v":0,"content":"This is the blurb for him","title":"VP, Global Marketing","created":"2015-05-15T13:00:25.177Z"}]
but I want to get getting the rendered page template from angular
Can anyone help?
This is because you call the server url (wich is the same used for ajax calls), you need to
Either intercept request type in the server side (ajax | not ajax) and redirect all no ajax to root path (/) and angular will use ajax to get articles in client side.
Define a single root (/) for serving your single web application and use (/api/...) to handle all your ajax requests.
The way I desgined my express nodejs application as per below;
app.get('/', page.index);
//Add new,edit and delete form
app.get('/approval', page.approval);
//Task to approve/reject the subordinate form
app.get('/task', page.task);
//Report
app.get('/report', page.report);
//Admin
app.get('/admin', page.admin)
app.listen(3000);
Hence I can always access using the url i.e
http://<Servername>:3000/
http://<Servername>:3000/approval
http://<Servername>:3000/task
Lets say now I will need to have a parent app root as appsA ( ie.
http://<Servername>:3000/appsA/
http://<Servername>:3000/appsA/approval
http://<Servername>:3000/appsA/task
How can I do that from express/nodejs without go and add appsA as a url on each of the get request.
You can use router to do that, example
var express = require("express");
var router = express.Router();
router.get('/', page.index);
router.get('/approval', page.approval);
router.get('/task', page.task);
router.get('/report', page.report);
router.get('/admin', page.admin)
// Then implement root route path
app.use("/appsA", router);
Note: Routers are only available for Express 4 and above, This solution will not work on all Express versions below version 4, If you still want to do this with express 3, you can implement your own Router middleware