What is ending a route - node.js

Supose i have this route code:
app.get('/', function(req, res) {
res.send('Hello world!');
});
app.get('/something', function(req, res) {
// Do something
});
If i visit the / route in my browser, the "Hello world" message will show, and my response will have ended.
Question: Is it the absence of next() in this router, or the res.send() that ENDS the http request?

It's res.end() that actually ends the response, but res.send() calls this.
You can see this in the source here.

Sorry, but if you want to log every request including '/' then you have to reorder using:
app.use(function(req, res, next) {
// log each request to the console
console.log(req.method, req.url);
// continue doing what we were doing and go to the route
next();
});
app.get('/', function(req, res) {
res.send('Hello world!');
});

Related

Express trigger not found route for each route

I am using express with a pattern like this :
app = express();
router = express.Router();
router.use((req, res, next) => {
console.log("my middleware before");
next();
});
router.get('/foo', (req, res, next) => {
console.log("My route");
res.send("<h1>Hello</h1>")
next();
});
router.use((req, res, next) => {
console.log("my middleware after");
});
app.use("/", router);
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
app.use((err, req, res, next) => {
console.log("Error occure");
res.send("<h1>Error</h1>");
});
app.listen(3000);
When I request '/foo' I would like to have
> my middleware before
> My route
> my middleware after
<h1>Hello</h1>
And when I request anything else :
> page not found
> Error occure
<h1>Error</h1>
But the page not found route is executed in each case, even if route '/foo' is done.
How can I get it working ?
When I run your code, I do not get the output you show, so something about your real code is apparently different than what you show in your question.
I do get a slightly confusing output and that happens because the browser sends both the /foo request and a /favicon.ico request. When I run it, the /foo request generates the desired output. The /favicon.ico request generates some middleware output and then gets stuck in the router.
If you filter out the /favicon.ico route (so that it doesn't confuse things) by adding this as the first route:
app.get("/favicon.ico", (req, res) => {
res.sendStatus(404);
});
Then, I get exactly this output in the server logs when I request /foo:
my middleware before
My route
my middleware after
Which is exactly what you asked for.
There is, however, a general problem with this:
router.use((req, res, next) => {
console.log("my middleware after");
});
Because it will catch and hang any legit requests that haven't yet had a response sent. You can't really code it that way unless you only don't call next() if a response has already been sent.
As a bit of a hack, you could do this:
router.use((req, res, next) => {
console.log("my middleware after");
// if response hasn't yet been sent, continue routing
if (!res.headersSent) {
next();
}
});
But, there is probably a better way to solve whatever problem you're actually trying to solve. If, in the future, you describe your real problem rather than a problem you have with your solution, then you allow people to offer a wider range of solutions to your real problem including things you haven't even thought of to try. As your question is written right now, we're stuck down the solution path you followed and don't know what the original problem was. That is, by the way, referred to as an XY Problem.
Do this
app = express();
router = express.Router();
router.use((req, res, next) => {
console.log("my middleware before");
next();
});
router.get('/foo', (req, res, next) => {
// use locals to record the fact we have a match
res.locals.hasMatch = true
console.log("My route");
res.send("<h1>Hello</h1>")
next();
});
router.use((req, res, next) => {
console.log("my middleware after");
});
app.use("/", router);
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
app.use((err, req, res, next) => {
// check locals to see if we have a match
if (!res.locals.hasMatch) {
console.log("Error occure");
res.send("<h1>Error</h1>");
}
});
app.listen(3000);
You can utilize middlewares and even nest them.
You can implement it like this:
Middlewares
const before = (req, res, next) => {
console.log("my middleware before");
next(); // Supply next() so that it will proceed to the next call,
// in our case, since this is supplied inside the router /foo, after this runs, it will proceed to the next middleware
};
const after = (req, res, next) => {
console.log("my middleware after");
};
Route
// Supply "before" middleware on 2nd argument to run it first when this route is called
router.get('/foo', before, (req, res, next) => {
console.log("My route");
res.send("<h1>Hello</h1>");
next(); // Call next() to proceed to the next middleware, or in "after" middleware
}, after); // Supply the "after" middleware
Once ran, it will proceed with this desired result sequence:
> my middleware before
> My route
> my middleware after
Unmatched Routes Handler
Instead of this
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
You can implement it like this instead, this is after your app.use("/", router); -- This will handle your unmatched routes:
Sources:
https://stackoverflow.com/a/44540743/6891406
https://stackoverflow.com/a/16637812/6891406
app.use((req, res, next) => {
console.log("page not found");
res.json({ error: 'Page not Found' })
});

From callback url, how do I get the last req and res after callback?

User accesses to /search url then I authenticate user.
Once it's logged in, it redirects to /login/callback.
Then I want to redirect the user to www.myproxy/search.
Currently, it's redirecting to www.myproxy/login/callback. I can't find any way to get the previous res and req.
How do I get the previous res and req after the callback?
app.get('/login',
function (req, res, next) {
console.log('-----------------------------');
console.log('/Start login callback ');
next();
},
passport.authenticate('samlStrategy'),
);
app.post('/login/callback',
function (req, res, next) {
console.log('-----------------------------');
console.log('/Start login callback ');
next();
},
passport.authenticate('samlStrategy'),
function (req, res) {
console.log('-----------------------------');
console.log('login call back dumps');
console.log(req.user);
console.log('-----------------------------');
apiProxy.web(req, res, {target: serviceProvider})
}
);
app.all('*',
function (req, res, next) {
console.log('-----------------------------');
console.log('/Start login callback ');
next();
},
passport.authenticate('samlStrategy'),
function(req, res) {
console.log('passing source!')
apiProxy.web(req, res, {target: serviceProvider})
}
);
okay the thing is http is a stateless protocol, so what I mean by stateless is
you cannot know what happened in earlier request and response, and can neither keep a track of future req and resp, it has to be done explicitly somewhere in your app(only if you want to track the reqs and resps).
But here in your case I suggest you to add an additional parameter to your req body with a redirect route and if it is null or default, set a default route, and add the route to your response url.

How to show custom message of route not found in expressjs?

I used this code at the bottom of app.js
app.use(function (req, res) {
res.send('Route Not Found');
})
But it executes in every request. I want to execute it only when route is not found.
This works fine. Make sure to place app.get('*' ... at the end of each file with routes if you are including external routes files.
const express = require('express')
const app = express()
app.get('/', function(req, res){
res.status(200).send('GET /')
})
app.get('/a', function(req, res){
res.status(200).send('GET /a')
})
app.get('*', function(req, res){
res.status(404).send('404')
})
app.listen(3000)
Put your routes handler before :
app.get("/a", require("./controllers/handler1.js"); // catch get route on a
app.get("/b", require("./controllers/handler2.js"); // catch get route on b
app.post("/a", require("./controllers/handler1.js"); // catch post route on a
// error handler, if no route has been caught
app.get("/*", function(req, res){ res.send("404 not found"); res.end();});
app.post("/*", function(req, res){ res.send("404 not found"); res.end();});

Using express middleware only for a GET request

The way I understand it, if I do something like:
app.use('/something', function(req, res, next) {
// some content here
});
This basically means that if there's a request to 'something', then the middleware (my function) is executed before the next function.
So if I have something like this to handle a GET request,
app.get('/something', function(req, res, next) {
console.log('hello');
});
Then 'hello' is going to be printed out after my original function has finished executing.
But how do I make it so that my middleware function is just executed when I ONLY make a GET request and not a POST request?
For a GET only middleware, just do the following
// Get middleware
app.get('/something', function(req, res, next) {
console.log('get hello middleware');
next();
});
// GET request handler
app.get('/something', function(req, res) {
console.log('get hello');
res.end();
});
// POST request handler
app.post('/something', function(req, res) {
console.log('post hello');
res.end();
});
app.post('/something', your_middleware, function(req, res, next) {
console.log('hello');
});
Only during the post request your_middleware will be executed.
your_middleware should be a function as follows:
function(req, res, next){
....
next()
}
you can pipe in as many middlewares you want in this way for a specific route and request type

Set custom header when using res.render

I'm using express.js's res.render function and I met some issue to set custom headers, I'm tried 4 kind of method and all failed
here is my code
Method 1
app.get('/login', function(req, res, next) {
res.writeHead(200, {'Content-Type':'text/plain; charset=utf-8'});
next()
});
app.get('/login', function(req, res) {
res.locals.text="hello";
res.render('index');
});
It has a error log with this code: Error: Can't set headers after they are sent.
Method 2 (example from here)
app.get('/login', function(req, res, next) {
res.header(200, {'Content-Type':'text/plain; charset=utf-8'});
next()
});
app.get('/login', function(req, res) {
res.locals.text="hello";
res.render('index');
});
the code comes with the error: TypeError: field.toLowerCase is not a function
Method 3
app.get('/login', function(req, res) {
res.writeHead(200, {'Content-Type': 'application/xhtml+xml; charset=utf-8'});
res.locals.text="hello";
res.render('index');
});
the code also has error: Error: Can't set headers after they are sent.
That's all the ways I can thought and find but still can't resolve, is there any ways to set custom header (especially encoding type) with res.render?
To set custom response header with res.render, you can use res.set(). Here is an example code:
app.get('/login', function(req, res) {
res.set({'Content-Type': 'application/xhtml+xml; charset=utf-8'});
res.locals.text="hello";
res.render('index');
});
Please check Express document for more details about res.set()
If you want to set an header for your response, you can use setHeader method on http.ServerResponse object.
var server = require('http').createServer(function(req, res) {
res.setHeader('content-type', 'application/json');
res.write(headerStr);
res.end();
});

Resources