I'm new to express and I'm trying to use express.Router() in order to route to different end points. When I follow tutorials online, I can only get text to send from the root '/' and no other endpoint. As far as I can tell my code matches well with the tutorial, so I'm not seeing where something is missing.
express.js
"use strict";
const express = require('express');
const app = express();
const resources = require('../routes/resources');
const commonExpress = require('common/lib/express');
commonExpress.setup_pre(app);
app.use('/', (req, res) => {
res.send('<h1>Hey Socket.io</h1>');
});
app.use('/resources', resources)
//use /routes/resources to handle endpoints
//that start with '/resources'
commonExpress.setup_post(app);
module.exports = app;
resources.js
"use strict";
const express = require('express');
const router = express.Router();
router.use(function(req, res, next) {
console.log(req.url, "#", Date.now());
next();
});
router.route('/messages').get((req, res) => {
res.send("hi get /resources/messages")
}).post((req, res) => {
res.send("hi post /resources/messages");
});
router.route('/messages/:userId').get((req, res) => {
res.send("hi get /resources/messages " + req.params.userId);
}).put((req, res) => {
res.send("hi put /resources/messages " + req.params.userId);
})
module.exports = router;
commonExpress
const express = require('express');
const logger = require('morgan');
const utils = require('./utils');
const cookieParser = require('cookie-parser');
module.exports = {
setup_pre: app => {
app.use(logger('dev'));
app.use(express.json());
app.use(cookieParser());
app.use('/health', (req, res) => res.status(200).send());
},
setup_post: app => {
app.disable('x-powered-by');
app.use(utils.handleMezError);
app.use(utils.handleMongoError);
app.use(utils.handleUnknownError);
app.use(function (req, res, next) {
res.status(404).send();
})
}
};
These are the responses I get when I use the curl command:
tk#tk-desktop:~/messenger$ curl http://localhost:4000/
<h1>Hey Socket.io</h1>tk#tk-desktop:~/messenger$ curl http://localhost:4000/resources
<h1>Hey Socket.io</h1>tk#tk-desktop:~/messenger$ curl http://localhost:4000/resources/messages
<h1>Hey Socket.io</h1>tk#tk-desktop:~/messenger$ curl http://localhost:4000/resources/messages/:userId
Your code here:
app.use('/', (req, res) => {
res.send('<h1>Hey Socket.io</h1>');
});
grabs all possibly URLs and sends a response and does not allow any of the other request handlers to see inbound requests. Since request handlers in Express are processed in the order you register them, the only request handlers that can run before this one grabs the inbound request are the ones in commonExpress.setup_pre(app);.
Probably, you want to change that above route to:
app.get('/', (req, res) => {
res.send('<h1>Hey Socket.io</h1>');
});
So, it only responds to a GET request and so it only matches the exact / URL (see details below).
It may help you to understand a few things:
app.use() is greedy. It will match any inbound URL that starts with the path you use. So, if you have app.use('/', ...), that will match all possible URLs since all URLs start with /. As such, app.use() is usually used for middleware, that prepares a request/response, but doesn't actually send one and then calls next() to continue routing to subsequent handlers and doesn't call res.send() or any other method that sends a response. Once you've sent a response and not called next(), all routing is done for that request.
Inbound requests are processed by Express in the order the request handlers are registered. So, if you have overlapping handlers, you would generally put the more permissive handlers later and the more specific handlers earlier. If you do the reverse which is what you have here, the more permissive handlers will grab everything and the others will never get a shot.
It looks like you can either change your app.use('/', ...) to an app.get('/', ...) which will only match the exact / URL and nothing else or you can change it to be a middleware handler that doesn't send a response and does call next() to continue routing.
It also looks like you may be using app.use() in other places where app.get() is more appropriate. For example with app.use('/health', ...), app.use() will match all HTTP verbs (GET, POST, PUT, DELETE, PATCH, etc...), but if you are sending a response in your route handler, you typically want to be matching only one of those verbs. If it's a request/response type of route where the browser requests content, that would be a GET and should use app.get(), not app.use().
Related
In Node Js, on the entry file e.g. index.js, How can I get requested data either as Form-data or Form-URL-encoded or Raw JSON data in middleware?
In my project, I am handling various API request,
Few requests contain file type so requesting as form-data.
Few requests do not contain file type so requests are coming as Form-URL-encoded.
Now the main problem is before routing, I need a specific field from req.body in the middleware.
But I am getting req.body as undefined.
For reference here is my index.js file:
const express = require('express');
const app = express();
app.use(express.raw());
app.use(express.urlencoded({ extended: false }));
app.use((req, res, next) => {
const routes_handler = require('./routes/index.js')(app, express, req);
next();
})
app.listen(3000, () => {
console.log("Server running at Port " + 3000);
});
and the routes/index.js file as follows:
module.exports = function (app, express, req) {
console.log(req.body);
//I need here data of req.body for all requests type (form data, URL-encoded, raw JSON)
app.post('/', function (req, res) {
console.log("Here I can get the requested body easily", req.body)
res.send('Hello World');
});
app.post('*', function (req, res) {
res.send({
code: 0,
message: 'No Content',
status_code: 204,
data: {
error: 'API not found!'
}
});
});
}
Also, I know for file type data, POSTMAN will send the request as Form-data, not as Form-url-encoded. So which I should choose Formidable or Multer?
The way you get all the data in index.js is by creating middlewares for your application, every time any routes that go into your application will be passed through this middleware.
Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle. The next function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware.
The below middleware will simply listen to all routes & adds up request time to request time, here goes the code
let express = require('express')
let app = express()
let bodyParser = require("body-parser")
app.use(bodyParser.json())
let requestTime = function (req, res, next) { // simply updating add the requestBody using the middleware
req.requestTime = Date.now();
req.json_body = req.body;
next()
}
app.use(requestTime) // requestTime is the middleware here
app.get('/', function (req, res) {
var responseText = 'Hello World!<br>'
responseText += '<small>Requested at: ' + req.requestTime + '</small>'
res.send(responseText)
})
app.listen(3000)
Few things to note here
Always add interceptor above all routes
Don't forget to add next() inside the middleware, else it will not go to the next route.
Now, coming to the second part of your question which is accessing body,formdata, etc
You can use body-parser npm module to achieve that, something like this
Starting from Express 4, body-parser comes inbuilt with it, so you can try out something
app.use(express.json());
app.use(
bodyParser.urlencoded({
extended: false
})
);
Now, the last bit, you don't need Multer for formdata, but for file upload like multipart/form-data you will need this. Both are good in their own ways, I would go for Multer.
Hope this will help you :)
I believe the body-parser module is your answer.
https://www.npmjs.com/package/body-parser
Add the following line before the routes in your index.js after installing the body-parser package.
app.use(bodyParser.json())
I am trying to setup a multi language website with Express and NodeJs. My problem is I get redirected what it feels like 100 times and my browser is giving me a error that the webpage is not working because it redirected me too many times.
app.js
app.use('/', (req,res,next) => {
res.redirect('/en-US');
next();
});
app.use('/:lang', indexRouter);
app.use('/:lang/users', usersRouter);
index.js (indexRouter)
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index');
});
module.exports = router;
The problem is that this route handler:
app.use('/', (req,res,next) => {
res.redirect('/en-US');
next();
});
will get hit for not only /, but also /en-US. app.use() matches any route handler for which the path is equal to or a subset of the requested path. So, the browser requests "/", you redirect to "/en-US", which then redirects to "/en-US" and so on, an infinite loop.
I don't know the overall URL design of your site to know what the best overall solution is. You can prevent the infinite redirect loop by just changing app.use() to app.get():
app.get('/', (req,res,next) => {
res.redirect('/en-US');
});
But, that will make the redirect only work for GET requests which may or may not be OK. If you want all HTTP verbs to redirect, you could change to app.all():
app.all('/', (req,res,next) => {
res.redirect('/en-US');
});
The important thing to understand here is that app.get(), app.post(), app.all(), etc... all require an exact match for the URL path, whereas app.use() just requires a subset match. This is a little understood aspect of the Express design.
In addition, remove the call to next() after you do res.redirect(). At that point, you've sent the response, you don't want any other request handlers to see the request. You're done with routing.
under your app.js
Try using
app.use('/', router )
How about you try dealing with the '/' route through the app.js directly instead of index.js
I am trying Express redirects on a website running from localhost. Express captures the http calls made and redirects them as required using the express routing feature. It matches the uri pattern of the requests made from the internal host.
var express = require('express')
var app = express()
app.get('/about', function (req, res) {
res.send('about')
})
If I am running from localhost:4200, the above code will route requests that resemble, http://localhost:4200/about.
Now let's say there is a button which on click opens https://google.com. Is there a way in express to catch this request and route it elsewhere?
var express = require('express')
var app = express()
app.get('/about', function (req, res) {
res.status(301).redirect('https://www.google.com') // status 301 or 302 for permanent or temporary redirection
})
or
app.use((req, res, next) => {
if (true) { // make your own condition in the express middleware
return res.status(301).redirect(`https://www.google.com`);
}
return next();
});
I'm trying to use https://github.com/bmullan91/express-subdomain for subdomain routing in express. The following are the contents of my main.js and src/routes/site files.
const express = require('express');
const bodyParser = require('body-parser');
const subdomain = require('express-subdomain');
const siteRouter = require('./src/routes/site');
const app = express()
app.use(express.json() );
app.use(express.urlencoded());
app.use(express.static('public'));
app.use(subdomain('*.www', siteRouter));
app.get('/', function(req, res) {
res.send('Homepage');
});
const server = app.listen(80,'x3.loc', function () {
var host = server.address().address;
var port = server.address().port;
console.log('X3 listening at http://%s:%s', host, port);
});
const express = require('express');
let router = express.Router();
router.get('/', function(req, res) {
res.send('Welcome to site');
});
module.exports = router;
This way of doing app.use(subdomain('*.www', siteRouter)); has been suggested in https://github.com/bmullan91/express-subdomain/issues/33 but does not work.
I have also tried just * as the subdomain aswell, but that caused the homepage w/o a subdomain, to get treated like one aswell. How could I get this to work?
We know that / matches any base path regardless of subdomain. So I made your homepage middleware "subdomain-aware" like so:
app.get('/', function(req, res,next) {
/* If there are any subdomains, skip to next handler, since request is not for the main home page */
if (req.subdomains.length > 0) {
return next();
}
res.send('Homepage');
});
Then I placed the middleware for subdomains below the homepage middleware like so:
app.use(subdomain('*', siteRouter));
This makes homepage middleware to serve requests for x3.loc and the subdomain middleware to serve requests for any subdomain like api.x3.loc or api.v1.x3.loc.
But in my opinion, the real fix should be done in the module. I think it should be changed so that either the case where req.subdomains being empty is handled, or * is matched against an actual string, instead of skipping the iteration.
I am surprised that the fix suggested in bug 33 worked as-is for the reporter. In my testing, it works the opposite way i.e. www.example.com goes to subdomain middleware while stat1.example.com goes to the homepage middleware. Perhaps the reporter saw this and swapped the middleware bodies.
I have the following:
app.js
...
var api = require('./routes/api');
app.use('/', api);
app.use('/api', api);
./routes/api
...
var router = express.Router();
router.get('/', passport.authenticate('bearer', { session: false }), function (req, res) {
res.json({
msg: 'API is running'
});
});
How would I differentiate between the / and /api route so that I could render a page if i'm on / and return only JSON if i'm on /api?
I was thinking of passing a function to non-api routes to render a json response, and another function to api routes to display the json response?
Any other way to do this, or am I just overthinking it?
Thanks in advance.
Why are you mounting 2 paths on the same route file i.e.
var api = require('./routes/api');
app.use('/', api);
app.use('/api', api);
You can use:
var api = require('./routes/api');
var index = require('./routes/index');
app.use('/', index);
app.use('/api', api);
Now, you can write your routes in these 2 files. / in index file will be use for domain.com/ url, and / route in api file will be used for domain.com/api url. Now, you can handle both requests as differently as you want.
You could also specify both routes in index file(in this case, you do not need to create api file and mount it on a path). e.g.
router.get('/', function( req, res ){
// code for / path here
});
router.get('/api', function( req, res ){
// code for /api path here
});
It really depends what you want.
Example from one of my projects:
We had the frontend under /client/ so when we got / we used a res.redirect('/client/') where your HTML file could be.
Or maybe something like res.render(view [, locals] [, callback]) fits your needs
Take a look at Express Documentaion for res there are many possibilities.