Comment obtenir tous les itinéraires enregistrés dans Express?
J'ai une application Web créée à l'aide de Node.js et Express. Maintenant, je voudrais lister tous les itinéraires enregistrés avec leurs méthodes appropriées.
Par exemple, si j'ai exécuté
app.get('/', function (...) { ... });
app.get('/foo/:id', function (...) { ... });
app.post('/foo/:id', function (...) { ... });
Je voudrais récupérer un objet (ou quelque chose d'équivalent à cela) tel que:
{
get: [ '/', '/foo/:id' ],
post: [ '/foo/:id' ]
}
Est-ce possible, et si oui comment?
UPDATE: Pendant ce temps, j'ai créé un paquet npm appelé get-routes qui extrait les routes d'une application donnée, ce qui résout ce problème. Actuellement, seul Express 4.x est pris en charge, mais je suppose que cela va pour le moment. Juste FYI.
express 3.x
Ok, je l'ai trouvé moi-même ... c'est juste app.routes
:-)
express 4.x
Applications - construit avec express()
app._router.stack
Routeurs - construits avec express.Router()
router.stack
Note: La pile inclut aussi les fonctions de middleware, elle devrait être filtrée pour obtenir le "routes" uniquement.
app._router.stack.forEach(function(r){
if (r.route && r.route.path){
console.log(r.route.path)
}
})
J'ai adapté un ancien message qui n'est plus en ligne pour mes besoins . J'ai utilisé express.Router () et enregistré mes itinéraires comme suit:
var questionsRoute = require('./BE/routes/questions');
app.use('/api/questions', questionsRoute);
J'ai renommé le fichier document.js en apiTable.js et je l'ai adapté comme suit:
module.exports = function (baseUrl, routes) {
var Table = require('cli-table');
var table = new Table({ head: ["", "Path"] });
console.log('\nAPI for ' + baseUrl);
console.log('\n********************************************');
for (var key in routes) {
if (routes.hasOwnProperty(key)) {
var val = routes[key];
if(val.route) {
val = val.route;
var _o = {};
_o[val.stack[0].method] = [baseUrl + val.path];
table.Push(_o);
}
}
}
console.log(table.toString());
return table;
};
alors je l'appelle dans mon server.js comme ceci:
var server = app.listen(process.env.PORT || 5000, function () {
require('./BE/utils/apiTable')('/api/questions', questionsRoute.stack);
});
Le résultat ressemble à ceci:
C'est juste un exemple, mais pourrait être utile .. j'espère ..
Voici une petite chose que j'utilise simplement pour obtenir les chemins enregistrés dans express 4.x
app._router.stack // registered routes
.filter(r => r.route) // take out all the middleware
.map(r => r.route.path) // get all the paths
Cela permet d'obtenir des itinéraires enregistrés directement sur l'application (via app.VERB) et des itinéraires enregistrés comme middleware de routeur (via app.use). Express 4.11.0
//////////////
app.get("/foo", function(req,res){
res.send('foo');
});
//////////////
var router = express.Router();
router.get("/bar", function(req,res,next){
res.send('bar');
});
app.use("/",router);
//////////////
var route, routes = [];
app._router.stack.forEach(function(middleware){
if(middleware.route){ // routes registered directly on the app
routes.Push(middleware.route);
} else if(middleware.name === 'router'){ // router middleware
middleware.handle.stack.forEach(function(handler){
route = handler.route;
route && routes.Push(route);
});
}
});
// routes:
// {path: "/foo", methods: {get: true}}
// {path: "/bar", methods: {get: true}}
Hacky copier/coller avec la permission de Doug Wilson sur les problèmes de express github Sale mais fonctionne comme un charme.
function print (path, layer) {
if (layer.route) {
layer.route.stack.forEach(print.bind(null, path.concat(split(layer.route.path))))
} else if (layer.name === 'router' && layer.handle.stack) {
layer.handle.stack.forEach(print.bind(null, path.concat(split(layer.regexp))))
} else if (layer.method) {
console.log('%s /%s',
layer.method.toUpperCase(),
path.concat(split(layer.regexp)).filter(Boolean).join('/'))
}
}
function split (thing) {
if (typeof thing === 'string') {
return thing.split('/')
} else if (thing.fast_slash) {
return ''
} else {
var match = thing.toString()
.replace('\\/?', '')
.replace('(?=\\/|$)', '$')
.match(/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//)
return match
? match[1].replace(/\\(.)/g, '$1').split('/')
: '<complex:' + thing.toString() + '>'
}
}
app._router.stack.forEach(print.bind(null, []))
Produit
Une fonction pour enregistrer toutes les routes dans Express 4 (peut être facilement modifiée pour la v3 ~)
function space(x) {
var res = '';
while(x--) res += ' ';
return res;
}
function listRoutes(){
for (var i = 0; i < arguments.length; i++) {
if(arguments[i].stack instanceof Array){
console.log('');
arguments[i].stack.forEach(function(a){
var route = a.route;
if(route){
route.stack.forEach(function(r){
var method = r.method.toUpperCase();
console.log(method,space(8 - method.length),route.path);
})
}
});
}
}
}
listRoutes(router, routerAuth, routerHTML);
Journaux en sortie:
GET /isAlive
POST /test/email
POST /user/verify
PUT /login
POST /login
GET /player
PUT /player
GET /player/:id
GET /players
GET /system
POST /user
GET /user
PUT /user
DELETE /user
GET /
GET /login
Fait de cela un NGP https://www.npmjs.com/package/express-list-routes
https://www.npmjs.com/package/express-list-endpoints fonctionne plutôt bien.
Exemple
Usage:
const all_routes = require('express-list-endpoints');
console.log(all_routes(app));
Sortie:
[ { path: '*', methods: [ 'OPTIONS' ] },
{ path: '/', methods: [ 'GET' ] },
{ path: '/sessions', methods: [ 'POST' ] },
{ path: '/sessions', methods: [ 'DELETE' ] },
{ path: '/users', methods: [ 'GET' ] },
{ path: '/users', methods: [ 'POST' ] } ]
Express 4
Avec une configuration Express 4 avec des noeuds finaux et des routeurs imbriqués
const express = require('express')
const app = express()
const router = express.Router()
app.get(...)
app.post(...)
router.use(...)
router.get(...)
router.post(...)
app.use(router)
En développant la réponse @caleb , il est possible d'obtenir tous les itinéraires de manière récursive et triés.
getRoutes(app._router && app._router.stack)
// =>
// [
// [ 'GET', '/'],
// [ 'POST', '/auth'],
// ...
// ]
/**
* Converts Express 4 app routes to an array representation suitable for easy parsing.
* @arg {Array} stack An Express 4 application middleware list.
* @returns {Array} An array representation of the routes in the form [ [ 'GET', '/path' ], ... ].
*/
function getRoutes(stack) {
const routes = (stack || [])
// We are interested only in endpoints and router middleware.
.filter(it => it.route || it.name === 'router')
// The magic recursive conversion.
.reduce((result, it) => {
if (! it.route) {
// We are handling a router middleware.
const stack = it.handle.stack
const routes = getRoutes(stack)
return result.concat(routes)
}
// We are handling an endpoint.
const methods = it.route.methods
const path = it.route.path
const routes = Object
.keys(methods)
.map(m => [ m.toUpperCase(), path ])
return result.concat(routes)
}, [])
// We sort the data structure by route path.
.sort((prev, next) => {
const [ prevMethod, prevPath ] = prev
const [ nextMethod, nextPath ] = next
if (prevPath < nextPath) {
return -1
}
if (prevPath > nextPath) {
return 1
}
return 0
})
return routes
}
Pour une sortie de chaîne de base.
infoAboutRoutes(app)
/**
* Converts Express 4 app routes to a string representation suitable for console output.
* @arg {Object} app An Express 4 application
* @returns {string} A string representation of the routes.
*/
function infoAboutRoutes(app) {
const entryPoint = app._router && app._router.stack
const routes = getRoutes(entryPoint)
const info = routes
.reduce((result, it) => {
const [ method, path ] = it
return result + `${method.padEnd(6)} ${path}\n`
}, '')
return info
}
Mise à jour 1:
En raison des limitations internes d'Express 4, il n'est pas possible de récupérer l'application montée et les routeurs montés. Par exemple, il n'est pas possible d'obtenir des itinéraires à partir de cette configuration.
const subApp = express()
app.use('/sub/app', subApp)
const subRouter = express.Router()
app.use('/sub/route', subRouter)
DEBUG=express:* node index.js
Si vous exécutez votre application avec la commande ci-dessus, elle sera lancée avec le module DEBUG
et donnera les itinéraires, ainsi que toutes les fonctions middleware en cours d'utilisation.
Vous pouvez vous référer à: ExpressJS - Debugging et debug .
Besoin de quelques ajustements mais devrait fonctionner pour Express v4. Y compris les itinéraires ajoutés avec .use()
.
function listRoutes(routes, stack, parent){
parent = parent || '';
if(stack){
stack.forEach(function(r){
if (r.route && r.route.path){
var method = '';
for(method in r.route.methods){
if(r.route.methods[method]){
routes.Push({method: method.toUpperCase(), path: parent + r.route.path});
}
}
} else if (r.handle && r.handle.name == 'router') {
const routerName = r.regexp.source.replace("^\\","").replace("\\/?(?=\\/|$)","");
return listRoutes(routes, r.handle.stack, parent + routerName);
}
});
return routes;
} else {
return listRoutes([], app._router.stack);
}
}
//Usage on app.js
const routes = listRoutes(); //array: ["method: path", "..."]
edit: améliorations du code
Les itinéraires express-list-list de Labithiotis m'ont inspiré, mais je souhaitais avoir une vue d'ensemble de tous mes itinéraires et de mes URL brutes, sans spécifier de routeur, et déterminer le préfixe à chaque fois. Je suis parvenu à remplacer la fonction app.use par ma propre fonction qui stocke la baseUrl et le routeur en question. De là, je peux imprimer n’importe quel tableau de tous mes itinéraires.
NOTE: cela fonctionne pour moi car je déclare mes routes dans un fichier de routes spécifique (fonction) qui est passé dans l'objet app, comme ceci:
// index.js
[...]
var app = Express();
require(./config/routes)(app);
// ./config/routes.js
module.exports = function(app) {
// Some static routes
app.use('/users', [middleware], UsersRouter);
app.use('/users/:user_id/items', [middleware], ItemsRouter);
app.use('/otherResource', [middleware], OtherResourceRouter);
}
Cela me permet de passer dans un autre objet 'app' avec une fonction d'utilisation fictive, et je peux obtenir TOUS les itinéraires. Cela fonctionne pour moi (suppression d'une vérification d'erreur pour la clarté, mais fonctionne toujours pour l'exemple):
// In printRoutes.js (or a gulp task, or whatever)
var Express = require('express')
, app = Express()
, _ = require('lodash')
// Global array to store all relevant args of calls to app.use
var APP_USED = []
// Replace the `use` function to store the routers and the urls they operate on
app.use = function() {
var urlBase = arguments[0];
// Find the router in the args list
_.forEach(arguments, function(arg) {
if (arg.name == 'router') {
APP_USED.Push({
urlBase: urlBase,
router: arg
});
}
});
};
// Let the routes function run with the stubbed app object.
require('./config/routes')(app);
// GRAB all the routes from our saved routers:
_.each(APP_USED, function(used) {
// On each route of the router
_.each(used.router.stack, function(stackElement) {
if (stackElement.route) {
var path = stackElement.route.path;
var method = stackElement.route.stack[0].method.toUpperCase();
// Do whatever you want with the data. I like to make a Nice table :)
console.log(method + " -> " + used.urlBase + path);
}
});
});
Cet exemple complet (avec quelques routeurs CRUD de base) vient d'être testé et imprimé:
GET -> /users/users
GET -> /users/users/:user_id
POST -> /users/users
DELETE -> /users/users/:user_id
GET -> /users/:user_id/items/
GET -> /users/:user_id/items/:item_id
PUT -> /users/:user_id/items/:item_id
POST -> /users/:user_id/items/
DELETE -> /users/:user_id/items/:item_id
GET -> /otherResource/
GET -> /otherResource/:other_resource_id
POST -> /otherResource/
DELETE -> /otherResource/:other_resource_id
Utiliser cli-table J'ai quelque chose comme ça:
┌────────┬───────────────────────┐
│ │ => Users │
├────────┼───────────────────────┤
│ GET │ /users/users │
├────────┼───────────────────────┤
│ GET │ /users/users/:user_id │
├────────┼───────────────────────┤
│ POST │ /users/users │
├────────┼───────────────────────┤
│ DELETE │ /users/users/:user_id │
└────────┴───────────────────────┘
┌────────┬────────────────────────────────┐
│ │ => Items │
├────────┼────────────────────────────────┤
│ GET │ /users/:user_id/items/ │
├────────┼────────────────────────────────┤
│ GET │ /users/:user_id/items/:item_id │
├────────┼────────────────────────────────┤
│ PUT │ /users/:user_id/items/:item_id │
├────────┼────────────────────────────────┤
│ POST │ /users/:user_id/items/ │
├────────┼────────────────────────────────┤
│ DELETE │ /users/:user_id/items/:item_id │
└────────┴────────────────────────────────┘
┌────────┬───────────────────────────────────┐
│ │ => OtherResources │
├────────┼───────────────────────────────────┤
│ GET │ /otherResource/ │
├────────┼───────────────────────────────────┤
│ GET │ /otherResource/:other_resource_id │
├────────┼───────────────────────────────────┤
│ POST │ /otherResource/ │
├────────┼───────────────────────────────────┤
│ DELETE │ /otherResource/:other_resource_id │
└────────┴───────────────────────────────────┘
Ce qui frappe le cul.
Approche légèrement actualisée et plus fonctionnelle de la réponse de @ prranay:
const routes = app._router.stack
.filter((middleware) => middleware.route)
.map((middleware) => `${Object.keys(middleware.route.methods).join(', ')} -> ${middleware.route.path}`)
console.log(JSON.stringify(routes, null, 4));
Cela a fonctionné pour moi
let routes = []
app._router.stack.forEach(function (middleware) {
if(middleware.route) {
routes.Push(Object.keys(middleware.route.methods) + " -> " + middleware.route.path);
}
});
console.log(JSON.stringify(routes, null, 4));
O/P:
[
"get -> /posts/:id",
"post -> /posts",
"patch -> /posts"
]
sortie json
function availableRoutes() {
return app._router.stack
.filter(r => r.route)
.map(r => {
return {
method: Object.keys(r.route.methods)[0].toUpperCase(),
path: r.route.path
};
});
}
console.log(JSON.stringify(availableRoutes(), null, 2));
ressemble à ça:
[
{
"method": "GET",
"path": "/api/todos"
},
{
"method": "POST",
"path": "/api/todos"
},
{
"method": "PUT",
"path": "/api/todos/:id"
},
{
"method": "DELETE",
"path": "/api/todos/:id"
}
]
sortie de chaîne
function availableRoutesString() {
return app._router.stack
.filter(r => r.route)
.map(r => Object.keys(r.route.methods)[0].toUpperCase().padEnd(7) + r.route.path)
.join("\n ")
}
console.log(availableRoutesString());
ressemble à ça:
GET /api/todos
POST /api/todos
PUT /api/todos/:id
DELETE /api/todos/:id
ceux-ci sont basés sur @ corvid answer
j'espère que cela t'aides
Donc, je cherchais toutes les réponses… je n'ai pas aimé la plupart… j'en ai pris quelques unes… a fait ceci:
const resolveRoutes = (stack) => {
return stack.map(function (layer) {
if (layer.route && layer.route.path.isString()) {
let methods = Object.keys(layer.route.methods);
if (methods.length > 20)
methods = ["ALL"];
return {methods: methods, path: layer.route.path};
}
if (layer.name === 'router') // router middleware
return resolveRoutes(layer.handle.stack);
}).filter(route => route);
};
const routes = resolveRoutes(express._router.stack);
const printRoute = (route) => {
if (Array.isArray(route))
return route.forEach(route => printRoute(route));
console.log(JSON.stringify(route.methods) + " " + route.path);
};
printRoute(routes);
pas la plus jolie .. mais imbriquée, et fait le tour
notez également le 20 là ... Je suppose juste qu'il n'y aura pas de route normale avec 20 méthodes .. donc j'en déduis que c'est tout ..
les détails de route sont la liste route pour "express": "4.x.x",
import {
Router
} from 'express';
var router = Router();
router.get("/routes", (req, res, next) => {
var routes = [];
var i = 0;
router.stack.forEach(function (r) {
if (r.route && r.route.path) {
r.route.stack.forEach(function (type) {
var method = type.method.toUpperCase();
routes[i++] = {
no:i,
method: method.toUpperCase(),
path: r.route.path
};
})
}
})
res.send('<h1>List of routes.</h1>' + JSON.stringify(routes));
});
SIMPLE SORTIE DE CODE
List of routes.
[
{"no":1,"method":"POST","path":"/admin"},
{"no":2,"method":"GET","path":"/"},
{"no":3,"method":"GET","path":"/routes"},
{"no":4,"method":"POST","path":"/student/:studentId/course/:courseId/topic/:topicId/task/:taskId/item"},
{"no":5,"method":"GET","path":"/student/:studentId/course/:courseId/topic/:topicId/task/:taskId/item"},
{"no":6,"method":"PUT","path":"/student/:studentId/course/:courseId/topic/:topicId/task/:taskId/item/:itemId"},
{"no":7,"method":"DELETE","path":"/student/:studentId/course/:courseId/topic/:topicId/task/:taskId/item/:itemId"}
]
J'ai publié un package qui imprime tous les middlewares ainsi que les itinéraires, ce qui est très utile pour essayer d'auditer une application express. Vous montez le paquet en tant que middleware, de sorte qu'il s'imprime même:
https://github.com/ErisDS/middleware-stack-printer
Il imprime une sorte d'arbre comme:
- middleware 1
- middleware 2
- Route /thing/
- - middleware 3
- - controller (HTTP VERB)
Sur Express 3.5.x, j'ajoute ceci avant de démarrer l'application pour imprimer les itinéraires sur mon terminal:
var routes = app.routes;
for (var verb in routes){
if (routes.hasOwnProperty(verb)) {
routes[verb].forEach(function(route){
console.log(verb + " : "+route['path']);
});
}
}
Peut-être que ça peut aider ...