I have a problem getting express to render a page using an arabic url route:
app.get('/شقق-في-لندن', function(req, res){
res.render('apartments');
});
when I try access this using the browser localhost:5000/شقق-في-لندن i get an error saying not found.
Cannot GET /%D8%B4%D9%82%D9%82-%D9%81%D9%8A-%D9%84%D9%86%D8%AF%D9%86
You have two options:
a) Write a middleware (executing before routing) that URL-decodes the UTF8 characters in URL and updates req.url
Advantage: Any subsequent middleware you write can refer to the original UTF-8 characters
Crude example:
app.use(function(req, res, next) {
req.url = decodeURI(req.url);
next();
});
[OR]
b) Change route(s) to match the URL-encoded versions like so
app.get('/%D8%B4%D9%82%D9%82-%D9%81%D9%8A-%D9%84%D9%86%D8%AF%D9%86', function(req, res){
res.render('apartments');
});
or more elegantly:
app.get('/'+encodeURIComponent('شقق-في-لندن'), function(req, res){
res.render('apartments');
});
You could use request params and check for the url like the following
app.get('/:arabic_url', function(req, res , next ){
if(req.params.arabic_url !== 'شقق-في-لندن') {
return next();
}
res.render('apartments');
});
I found A problem with my current implementation and I recommend the way #Vasan because in my impementation the order of your routes will cause an error , unexpected behaviour and redundancy.
Here is the right solution
app.get('/' + encodeURIComponent('شقق-في-لندن') , function(req, res){
res.render('apartments');
});
Related
I want to check for a consistent URL parameter in each request in my Express.JS app. The base of the URL is always the same:
/command/UUID
But some times it can be
/command/UUID/something/1
The following code works only in the first case. As soon as I add another slash at the end, this middleware won't be executed.
app.all('/*/:uuid', function(req, res, next) {
console.log(req.params)
next();
});
I would appreciate if someone could help me out solve this issue.
Best.
Something like this:
app.all('/:command/:uuid/(*?)', function(req, res, next) {
console.log(req.params.command, req.params.uuid);
next();
});
What actually worked is the following code
app.set('strict routing', false );
app.all('/:cmd/:uuid*', function(req, res, next) {
console.log(req.params)
next();
});
And the result is as follow with this URL: /ip/165420/3123123/adasdsad
{ '0': '/3123123/adasdsad', cmd: 'ip', uuid: '165420' }
For routing, I'd like my middleware to pass the request the routes defined in a /html folder to server HTML(ejs), and if header Content-Type is application/json, use the routes defined in the /api folder.
But I don't want to have to define that in every route.
So I'm not looking for middleware that defines some req.api property that I can check on in every route
app.get('/', function(req, res) {
if(req.api_call) {
// serve api
} else {
// serve html
}
});
But I'd like something like this:
// HTML folder
app.get('/', function(req, res) {
res.send('hi');
});
// API folder
app.get('/', function(req, res) {
res.json({message: 'hi'});
});
Is this possible and if so, how can I do this?
I'd like it to work something like this:
app.use(checkApiCall, apiRouter);
app.use(checkHTMLCall, htmlRouter);
You can insert as the first middleware in the Express chain, a middleware handler that checks the request type and then modifies the req.url into a pseudo URL by adding a prefix path to it. This modification will then force that request to go to only a specific router (a router set up to handle that specific URL prefix). I've verified this works in Express with the following code:
var express = require('express');
var app = express();
app.listen(80);
var routerAPI = express.Router();
var routerHTML = express.Router();
app.use(function(req, res, next) {
// check for some condition related to incoming request type and
// decide how to modify the URL into a pseudo-URL that your routers
// will handle
if (checkAPICall(req)) {
req.url = "/api" + req.url;
} else if (checkHTMLCall(req)) {
req.url = "/html" + req.url;
}
next();
});
app.use("/api", routerAPI);
app.use("/html", routerHTML);
// this router gets hit if checkAPICall() added `/api` to the front
// of the path
routerAPI.get("/", function(req, res) {
res.json({status: "ok"});
});
// this router gets hit if checkHTMLCall() added `/api` to the front
// of the path
routerHTML.get("/", function(req, res) {
res.end("status ok");
});
Note: I did not fill in the code for checkAPICall() or checkHTMLCall() because you were not completely specific about how you wanted those to work. I mocked them up in my own test server to see that the concept works. I assume you can provide the appropriate code for those functions or substitute your own if statement.
Prior Answer
I just verified that you can change req.url in Express middleware so if you have some middleware that modifies the req.url, it will then affect the routing of that request.
// middleware that modifies req.url into a pseudo-URL based on
// the incoming request type so express routing for the pseudo-URLs
// can be used to distinguish requests made to the same path
// but with a different request type
app.use(function(req, res, next) {
// check for some condition related to incoming request type and
// decide how to modify the URL into a pseudo-URL that your routers
// will handle
if (checkAPICall(req)) {
req.url = "/api" + req.url;
} else if (checkHTMLCall(req)) {
req.url = "/html" + req.url;
}
next();
});
// this will get requests sent to "/" with our request type that checkAPICall() looks for
app.get("/api/", function(req, res) {
res.json({status: "ok"});
});
// this will get requests sent to "/" with our request type that checkHTMLCall() looks for
app.get("/html/", function(req, res) {
res.json({status: "ok"});
});
Older Answer
I was able to successfully put a request callback in front of express like this and see that it was succesfully modifying the incoming URL to then affect express routing like this:
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(function(req, res) {
// test modifying the URL before Express sees it
// this could be extended to examine the request type and modify the URL accordingly
req.url = "/api" + req.url;
return app.apply(this, arguments);
});
server.listen(80);
app.get("/api/", function(req, res) {
res.json({status: "ok"});
});
app.get("/html/", function(req, res) {
res.end("status ok");
});
This example (which I tested) just hardwires adding "/api" onto the front of the URL, but you could check the incoming request type yourself and then make the URL modification as appropriate. I have not yet explored whether this could be done entirely within Express.
In this example, when I requested "/", I was given the JSON.
To throw my hat in the ring, I wanted easily readable routes without having .json suffixes everywhere.
router.get("/foo", HTML_ACCEPTED, (req, res) => res.send("<html><h1>baz</h1><p>qux</p></html>"))
router.get("/foo", JSON_ACCEPTED, (req, res) => res.json({foo: "bar"}))
Here's how those middlewares work.
function HTML_ACCEPTED (req, res, next) { return req.accepts("html") ? next() : next("route") }
function JSON_ACCEPTED (req, res, next) { return req.accepts("json") ? next() : next("route") }
Personally I think this is quite readable (and therefore maintainable).
$ curl localhost:5000/foo --header "Accept: text/html"
<html><h1>baz</h1><p>qux</p></html>
$ curl localhost:5000/foo --header "Accept: application/json"
{"foo":"bar"}
Notes:
I recommend putting the HTML routes before the JSON routes because some browsers will accept HTML or JSON, so they'll get whichever route is listed first. I'd expect API users to be capable of understanding and setting the Accept header, but I wouldn't expect that of browser users, so browsers get preference.
The last paragraph in ExpressJS Guide talks about next('route'). In short, next() skips to the next middleware in the same route while next('route') bails out of this route and tries the next one.
Here's the reference on req.accepts.
I have the following code :
app.get('/payment', function(req, res) {
// do lots of stuff
});
now I want to add the following :
app.post('/payment', function(req, res) {
req.myvar = 'put something here';
// now do the same as app.get() above
});
Obviously I want to reuse the code. I tried doing next('/payment') inside the post handler and put it above the get handler, but no luck, probably because they are different VERBs.
What are my options ?
Thanks.
Just lift out the middleware to its own function and use it in both routes.
function doLotsOfStuff (req, res) {
// do lots of stuff
}
app.get('/payment', doLotsOfStuff);
app.post('/payment', function(req, res, next) {
req.myvar = 'put something here';
next();
}, doLotsOfStuff);
I need an equivalent of following express.js code in simple node.js that I can use in middleware. I need to place some checks depending on the url and want to do it in a custom middleware.
app.get "/api/users/:username", (req,res) ->
req.params.username
I have the following code so far,
app.use (req,res,next)->
if url.parse(req.url,true).pathname is '/api/users/:username' #this wont be true as in the link there will be a actual username not ":username"
#my custom check that I want to apply
A trick would be to use this:
app.all '/api/users/:username', (req, res, next) ->
// your custom code here
next();
// followed by any other routes with the same patterns
app.get '/api/users/:username', (req,res) ->
...
If you only want to match GET requests, use app.get instead of app.all.
Or, if you only want to use the middleware on certain specific routes, you can use this (in JS this time):
var mySpecialMiddleware = function(req, res, next) {
// your check
next();
};
app.get('/api/users/:username', mySpecialMiddleware, function(req, res) {
...
});
EDIT another solution:
var mySpecialRoute = new express.Route('', '/api/users/:username');
app.use(function(req, res, next) {
if (mySpecialRoute.match(req.path)) {
// request matches your special route pattern
}
next();
});
But I don't see how this beats using app.all() as 'middleware'.
You can use node-js url-pattern module.
Make pattern:
var pattern = new UrlPattern('/stack/post(/:postId)');
Match pattern against url path:
pattern.match('/stack/post/22'); //{postId:'22'}
pattern.match('/stack/post/abc'); //{postId:'abc'}
pattern.match('/stack/post'); //{}
pattern.match('/stack/stack'); //null
For more information, see: https://www.npmjs.com/package/url-pattern
Just use the request and response objects as you would in a route handler for middleware, except call next() if you actually want the request to continue in the middleware stack.
app.use(function(req, res, next) {
if (req.path === '/path') {
// pass the request to routes
return next();
}
// you can redirect the request
res.redirect('/other/page');
// or change the route handler
req.url = '/new/path';
req.originalUrl // this stays the same even if URL is changed
});
I am working on a web api on node.js using the Express3 framework and I would want my routing to look something like /v0.1/function.
Ideally, the routing should automatically load the specified version module by looking at the major and minor version number given in the url. My approach so far is:
app.use('/v:major.:minor', function(req) {
return require('./v' + req.params.major + '.' +
req.params.minor);
});
And in my ./v0.1/index.js:
module.exports = function() {
var express = require('express'),
app = express();
app.get('/test', function(req, res) {
res.json({ success: true });
});
return app;
}();
If I call /v0.1/test now, it somehow does not match the route (404), but app.get('/v:major.:minor', function(req, res) { /* ... */ }); is working just fine.
How can I achieve this?
I think you want app.all (http://expressjs.com/api.html#app.all)
This will make is accessible for all HTTP Verbs and still have the variable routing working.
I tested it and it seems to work.
Edited as per comment
app.all('/v:major.:minor/:endpoint', function(req, res, next) {
var version_handler = require('./v' + req.params.major + '.' + req.params.minor);
if (!req.params.endpoint in version_handler) // endpoint_isn't supported at this version, 404
version_handler[req.params.endpoint](req, res, next);
});
You have to use app.param(). From the example in express API :
app.param('id', /^\d+$/);
app.get('/user/:id', function(req, res){
res.send('user ' + req.params.id);
});
You must provide the regular expression for your parameter in URL, since major, minor both are non negative integers /^\d+$/ should be it for you.
The Simplest way I found is chain a custom middleware:
Something like this:
app.use('/api/:version', function (req, res, next) {
console.log('The version name is : ', req.params.version)
req.version=req.params.version; //use this inside api router
next() // pass control to the next handler
},api);
api is your router object