how to use Wildcards in koa-route - node.js

var path = require('path')
var route= require('koa-route');//路由
app.use(route.get('/api/*',api.before));
I can't use it for this,how should I use wildcards in koa-route? Or,which other can do ?

I've been using https://github.com/alexmingoia/koa-router. I found koa-route too limiting.
It allows RegExp matching:
app.use(require('koa-router')(app));
// Matches '/test', '/test/foo/bar', '/test/foo/bar?page=2'
// but does NOT match '/test-route'
app.get(/^\/test(?:\/|$)/, function*() {
this.body = 'Test';
});
It looks to me like you're trying to attach middleware on /api/* that will run before all /api/* routes (like for authentication). This is how you can do that with koa-router:
///////////////////////////////////////////////////////////////////
// File: routes/api.js
///////////////////////////////////////////////////////////////////
var Router = require('koa-router');
// Create a router instance to bind middleware/routes to.
// Our module will export it so that our main routes.js file can
// mount it to our app.
var router = new Router();
// Middleware that ensures that a user is logged in for
// all routes attached to this router.
router.use(function*(next) {
this.assert(this.currentUser, 403);
yield next;
});
router.get('/test', function*() {
this.body = 'You went to /api/test';
});
module.exports = router;
///////////////////////////////////////////////////////////////////
// File: routes.js
///////////////////////////////////////////////////////////////////
var app = require('koa')();
var mount = require('koa-mount');
var apiRouter = require('./routes/api');
app.use(mount('/api', apiRouter.routes()));
If you navigate to /api, it'll be handled by the / handler in the router since you mounted it to /api, and it will return 403 unless you are logged in.

For
app.use(route.get('/api/*',api.before));
In api.before:
/api/a/b/c
==>
ctx.params[0] ; // => a/b/c

Related

how to add custom function to express module

I am trying to add a method
loadSiteSettings to express module
In app.js
var express = require('express');
var path = require('path');
var mongoose = require('mongoose');
//Set up default monggose connection for mongo db
var mongoDB = 'mongodb+srv://***:*****#cluste******j.mongodb.net/cms?retryWrites=true&w=majority';
mongoose.connect(mongoDB,{useNewUrlParser: true});
//Get the default connection
var db = mongoose.connection;
//Bind connection to error event (to get notification of connection errors)
db.on('error',console.error.bind(console, 'MongoDB connection error:'));///????????
var app = express();
///////////////////////////////////////////////////////////
var indexRouter = require('./routes/index');
app.loadSiteSettings = async function()
{
let setting = await db.collection('settings').findOne();
app.locals.siteSettings = setting;
}
app.loadSiteSettings();
//////////////////////////////////////////////////////
module.exports = app;
Index.Js for router
var express = require('express');
var router = express.Router();
var app = require('../app');
var util = require('util');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index');
});
///////////////////////////////////////////
router.get('/reloadSettings', function(req,res,next){
app.loadSiteSettings();
})
///////////////////////////////////////
module.exports = router;
so problem lies here, when server start it calls app.loadSiteSettings() in app.js
but when i use route '/reloadSettings' it seems app is undefined in index.js
This is an issue with circular dependencies and module.exports. This answer shows the same problem.
What's happening is app.js is required first and starts processing. The important thing to understand is that a file pauses execution while requiring a new file.
So when app.js requires ./routes/index, it has not finished processing, and has not reached module.exports = app. This means that when your routes file requires app.js, it's requiring it in its current state, which is the default export {}.
Thankfully there's a very simple fix:
// can be imported and tested separately from the app
const loadSiteSettings = function() {
return db.collection('settings').findOne();
}
router.get('/reloadSettings', async function(req,res,next){
let settings = await loadSiteSettings();
req.app.locals.siteSettings = settings
res.send(200); // signal the response is successful
})
This explains the issue in a lot more depth in case you're interested

how to create a nodeJS module with expressJS

I'm making a nodeJS module, and I want to use expressJS as a framework for it.
I'm trying to see, how I could go by, including a function inside and app.get(); and call it via another file, such as the actual app.
var express = require("express");
var app = express();
app.get("/", function (req, res) {
exports.type = function (text) {
console.log(req.ip);
console.log(text);
}
});
now when I use this, and i call it on the actual app like:
var web = require("directory_to_file");
var express = require("express");
var app = express();
var http = require("http").Server(app);
app.get("/", function (req, res) {
web.type("Hello, world");
});
http.listen(10022, function () {
console.log("server is up");
});
I get an error:
TypeError: Property 'type' of object #<Object> is not a function
anyone know a way to make it so I can call the function?
There are generally two things you want to export as a module - an API and a Middleware. The classic example of middleware is an authentication module. To do the middleware, just export the middleware. I tend to do a little more than that so I can configure the middleware later. Something along the lines of this:
module.exports = exports = function(config) {
// Do something with config here
return function(req, res, next) {
// your middleware here
};
};
You can then use your middleware in your main program like this:
var app = require('express')(),
mymodule = require('./mymodule');
var config = {}; // replace with whatever config you need
app.use(mymodule(config));
app.listen(process.env.PORT || 3000);
To implement an API, you will create a Router object, then attach your routes to the Router object. You can then "mount" your router in your main program. For example, you could have a file called 'myroutes.js' with the following contents:
var express = require('express'),
myroutes = express.Router();
myroutes.get('/foo', (req, res) => {
res.status(200).type('application/json').send({ myparam: 'foo' });
});
module.exports = exports = myroutes;
Have the following in your main program:
var app = require('express')(),
myroutes = require('./myroutes');
app.use('/api', require('./myroutes'));
app.listen(process.env.PORT || 3000);
Here, in 'myroutes.js', I'm defining a sub-route of /foo and then in the main program, I'm mounting that on /api - so I would access /api/foo to access that API.
In your directory_to_file you are only exporting on app.get('/') which will never be called.
You could add in your directory_to_file the following code
var express = require('express');
var router = express.Router();
router.get('/', function(req, server) {
console.log(req.ip);
});
module.exports = router;
And in your main file you could use app.use('/', web)
A short explanation:
You are creating a new express app / config in your directory_to_file file which won't be launched or used. So your app.get event won't be fired once.
That's why web.type is not a function. You are not exporting anything.
Use the way I provided. This is a commonly used method.
You could call the code I provided a "route". Create multiple routes / route files and include them in your main method.
Your code just looks confused. If I understand you correctly, what you are really trying to do (at least in Node/express terminology) is write your own middleware.
Express is designed with this in mind and it's pretty straightforward e.g.
ipLogger.js
module.exports = function(req, res, next) {
console.log(req.ip);
next();
}
app.js
var http = require("http")
, express = require("express");
, app = express()
, server = http.Server(app)
, ipLogger = require("./ipLogger.js");
app.use(ipLogger()); // log IP of all requests
// handle routes
server.listen(10022, function() {
console.log("server is up");
});

Is it possible to get the base URL for an express router

I have an authentication middleware that needs to make a call to an outside service and provide a callback URL. For example:
var express = require('express');
var app = express();
// This will work just fine
app.use('/', getAuthRouter());
// The redirects here will end up going to /oauth/callback
// instead of /admin/oauth/callback
app.use('/admin', getAuthRouter());
function getAuthRouter() {
var authRouter = express.Router();
// Setup auth routes
var callbackUrl = '/oauth/callback';
var loginUrl = '/login';
authRouter.get(callbackUrl, .... });
authRouter.get(loginUrl, function(req, res, next){
// Make call to OAuth server
res.redirect("http://authserver/?callback=" + callbackUrl);
});
return authRouter;
}
The problem is that authRouter does not know that it's actually mounted under /admin so it has no way to prepend that to the callback param.
Is there any way that I can get that inside the getAuthRouter function?
Use request.baseUrl, which was designed for this use case.
Use req.url:
You can use req.url.
If you are on http://example.com/test/here it will return /test/here.
Pass it as argument:
But in your case you could also pass the base url as a parameter of your middleware :
function getAuthRouter(baseUrl) {
var authRouter = express.Router();
baseUrl = baseUrl || ""; // Default
// Setup auth routes
var callbackUrl = baseUrl + '/oauth/callback';
var loginUrl = '/login';
// ...
}
And you'll then call it like so :
app.use('/admin', getAuthRouter("/admin"));
Or
app.use ('/', getAuthRouter()); // Calls default

How to modularize routing with Node.js Express

I'm building a web app with Express and Node and am trying to factor my routing so that I don't have hundreds of routes in the same file. This site serves different files within the projects directory, so I made a file in routes/ called projectRoutes.jsto handle the routing for project files:
var express = require('express');
module.exports = function() {
var functions = {}
functions.routeProject = function(req, res) {
res.render('pages/projects/' + req.params.string, function(err, html) {
if (err) {
res.send("Sorry! Page not found!");
} else {
res.send(html);
}
});
};
return functions;
}
Then, in my routes.js, I have this...
var projectRoutes = require("./projectRoutes");
router.get('/projects/:string', function(req, res) {
projectRoutes().routeProject(req, res);
});
Is there a better way to structure this functionality within projectRoutes.js? In other words, how can I configure projectRoutes.js so that I can write the follow line of code in index.js:
router.get('/projects/:string', projectRoutes.routeProject);
The above seems like the normal way to handle something like this, but currently the above line throws an error in Node that says the function is undefined.
Thanks for your help!
You should use the native express router, it was made to solve this exact problem! It essentially lets you create simplified nested routes in a modular way.
For each of your resources, you should separate out your routes into several modules named <yourResource>.js. Those modules would contain all of the routing code as well as any other configuration or necessary functions. Then you would attach them in index.js with:
var apiRoute = router.route('/api')
apiRoute.use('/< yourResource >', yourResourceRouter)
For example, if you had a resource bikes:
In index.js:
var apiRoute = router.route('/api')
, bikeRoutes = require('./bikes')
apiRoute.use('/bikes', bikeRoutes)
Then in bike.js:
var express = require('express')
, router = express.Router()
, bikeRoutes = router.route('/')
bikeRoutes.get(function (req, res) {
res.send('api GET request received')
});
module.exports = bikeRoutes
From there its easy to see that you can build many different resources and continually nest them.
A larger of example of connecting the routes in index.js would be:
var apiRoute = router.route('/api')
, bikeRoutes = require('./bikes')
, carRoutes = require('./cars')
, skateboardRoutes = require('./skateboards')
, rollerskateRoutes = require('./rollerskates')
// routes
apiRoute.use('/bikes', bikeRoutes)
apiRoute.use('/cars', carRoutes)
apiRoute.use('/skateboards', skateboardRoutes)
apiRoute.use('/rollerskates', rollerskateRoutes)
Each router would contain code similar to bikes.js. With this example its easy to see using express's router modularizes and makes your code base more manageable.
Another option is to use the Router object itself, instead of the Route object.
In Index.js:
//Load Routes
BikeRoutes = require('./routes/Bike.js');
CarRoutes = require('./routes/Car.js');
//Routers
var express = require('express');
var ApiRouter = express.Router();
var BikeRouter = express.Router();
var CarRouter = express.Router();
//Express App
var app = express();
//App Routes
ApiRouter.get('/Api', function(req, res){...});
ApiRouter.use('/', BikeRouter);
ApiRouter.use('/', CarRouter);
In Bike.js:
var express = require('express');
var router = express.Router();
router.get('/Bikes', function(req, res){...});
module.exports = router;
Similarly in Car.js

Use node js domains in handler function

I have https server written in express js. And I added domains to my server. App.js file:
var d = require('domain').create();
d.on('error', function(error) {
console.error("Domain caught error: "+ error.stack);
});
d.run(function() {
var express = require('express');
var appServer = express();
var https = require('https').createServer(options, appServer);
https.listen(8000, function() {
log.info('Server is listening on port ' + 8000);
});
appServer.use(appServer.router);
var routes = require('./routes')(appServer); //my routes file
});
I have route handler functions in other files. How can I use domain created in my app.js file in my route files without exporting it from app.js file.
Update:
routes.js file:
var auth = require('./auth');
module.exports = function(app) {
app.namespace('/login', function(){
app.post('/user', auth.verifyUser);
});
};
auth.js file:
exports.verifyUser = function(req,res) {
//here I want to see my domain
};
You can pass it when you require your routes:
var routes = require('./routes')(appServer, d);
Then within your routes/index.js file:
module.exports = function(app, domain) {
// ...
};
Update
To update the answer based on the question update, here's a possible solution to include the domain within each route definition. There are a couple ways to do this (you could make each route a class, which would be instantiated and passed the domain, then have a function defined per route, for example). Because the intent is to keep these route definition signatures function(req, res) as you've defined above for verifyUser, we're going to need to pass the domain to this route prior to calling the function. Keeping this very simple, we can do just that with a setDomain function:
Same code in your main index.js when you require your routes:
var routes = require('./routes')(appServer, d);
In your routes.js, you can pass the domain to the routes via the setDomain function:
var auth = require('./auth');
module.exports = function(app, domain) {
auth.setDomain(domain);
app.namespace('/login', function() {
app.post('/user', auth.verifyUser);
});
};
Finally, in your auth.js file, you can get the domain, and have access to it within the scope of the file to use it in the verifyUser function:
var myDomain;
module.exports.setDomain = function(domain) {
myDomain = domain;
};
module.exports.verifyUser = function(req, res) {
res.send("myDomain: " + myDomain);
};
I'm not sure I really like this solution, but again, it's keeping the signature the same for verifyUser. Maybe you'll think of a better one, or want to refactor your code to better make use of this domain you're passing around within your code (maybe define it somewhere else and pull it from there wherever it's needed).

Resources