417 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			417 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|  | # router
 | ||
|  | 
 | ||
|  | [![NPM Version][npm-image]][npm-url] | ||
|  | [![NPM Downloads][downloads-image]][downloads-url] | ||
|  | [![Node.js Version][node-version-image]][node-version-url] | ||
|  | [![Build Status][ci-image]][ci-url] | ||
|  | [![Test Coverage][coveralls-image]][coveralls-url] | ||
|  | 
 | ||
|  | Simple middleware-style router | ||
|  | 
 | ||
|  | ## Installation
 | ||
|  | 
 | ||
|  | This is a [Node.js](https://nodejs.org/en/) module available through the | ||
|  | [npm registry](https://www.npmjs.com/). Installation is done using the | ||
|  | [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): | ||
|  | 
 | ||
|  | ```bash | ||
|  | $ npm install router | ||
|  | ``` | ||
|  | 
 | ||
|  | ## API
 | ||
|  | 
 | ||
|  | ```js | ||
|  | var finalhandler = require('finalhandler') | ||
|  | var http = require('http') | ||
|  | var Router = require('router') | ||
|  | 
 | ||
|  | var router = Router() | ||
|  | router.get('/', function (req, res) { | ||
|  |   res.setHeader('Content-Type', 'text/plain; charset=utf-8') | ||
|  |   res.end('Hello World!') | ||
|  | }) | ||
|  | 
 | ||
|  | var server = http.createServer(function (req, res) { | ||
|  |   router(req, res, finalhandler(req, res)) | ||
|  | }) | ||
|  | 
 | ||
|  | server.listen(3000) | ||
|  | ``` | ||
|  | 
 | ||
|  | This module is currently an extracted version from the Express project, | ||
|  | but with the main change being it can be used with a plain `http.createServer` | ||
|  | object or other web frameworks by removing Express-specific API calls. | ||
|  | 
 | ||
|  | ## Router(options)
 | ||
|  | 
 | ||
|  | Options | ||
|  | 
 | ||
|  | - `strict`        - When `false` trailing slashes are optional (default: `false`) | ||
|  | - `caseSensitive` - When `true` the routing will be case sensitive. (default: `false`) | ||
|  | - `mergeParams`   - When `true` any `req.params` passed to the router will be | ||
|  |   merged into the router's `req.params`. (default: `false`) ([example](#example-using-mergeparams)) | ||
|  | 
 | ||
|  | Returns a function with the signature `router(req, res, callback)` where | ||
|  | `callback([err])` must be provided to handle errors and fall-through from | ||
|  | not handling requests. | ||
|  | 
 | ||
|  | ### router.use([path], ...middleware)
 | ||
|  | 
 | ||
|  | Use the given [middleware function](#middleware) for all http methods on the | ||
|  | given `path`, defaulting to the root path. | ||
|  | 
 | ||
|  | `router` does not automatically see `use` as a handler. As such, it will not | ||
|  | consider it one for handling `OPTIONS` requests. | ||
|  | 
 | ||
|  | * Note: If a `path` is specified, that `path` is stripped from the start of | ||
|  |   `req.url`. | ||
|  | 
 | ||
|  | <!-- eslint-disable no-undef --> | ||
|  | 
 | ||
|  | ```js | ||
|  | router.use(function (req, res, next) { | ||
|  |   // do your things | ||
|  | 
 | ||
|  |   // continue to the next middleware | ||
|  |   // the request will stall if this is not called | ||
|  |   next() | ||
|  | 
 | ||
|  |   // note: you should NOT call `next` if you have begun writing to the response | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | [Middleware](#middleware) can themselves use `next('router')` at any time to | ||
|  | exit the current router instance completely, invoking the top-level callback. | ||
|  | 
 | ||
|  | ### router\[method](path, ...[middleware], handler)
 | ||
|  | 
 | ||
|  | The [http methods](https://github.com/jshttp/methods/blob/master/index.js) provide | ||
|  | the routing functionality in `router`. | ||
|  | 
 | ||
|  | Method middleware and handlers follow usual [middleware](#middleware) behavior, | ||
|  | except they will only be called when the method and path match the request. | ||
|  | 
 | ||
|  | <!-- eslint-disable no-undef --> | ||
|  | 
 | ||
|  | ```js | ||
|  | // handle a `GET` request | ||
|  | router.get('/', function (req, res) { | ||
|  |   res.setHeader('Content-Type', 'text/plain; charset=utf-8') | ||
|  |   res.end('Hello World!') | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | [Middleware](#middleware) given before the handler have one additional trick, | ||
|  | they may invoke `next('route')`. Calling `next('route')` bypasses the remaining | ||
|  | middleware and the handler mounted for this route, passing the request to the | ||
|  | next route suitable for handling this request. | ||
|  | 
 | ||
|  | Route handlers and middleware can themselves use `next('router')` at any time | ||
|  | to exit the current router instance completely, invoking the top-level callback. | ||
|  | 
 | ||
|  | ### router.param(name, param_middleware)
 | ||
|  | 
 | ||
|  | Maps the specified path parameter `name` to a specialized param-capturing middleware. | ||
|  | 
 | ||
|  | This function positions the middleware in the same stack as `.use`. | ||
|  | 
 | ||
|  | The function can optionally return a `Promise` object. If a `Promise` object | ||
|  | is returned from the function, the router will attach an `onRejected` callback | ||
|  | using `.then`. If the promise is rejected, `next` will be called with the | ||
|  | rejected value, or an error if the value is falsy. | ||
|  | 
 | ||
|  | Parameter mapping is used to provide pre-conditions to routes | ||
|  | which use normalized placeholders. For example a _:user_id_ parameter | ||
|  | could automatically load a user's information from the database without | ||
|  | any additional code: | ||
|  | 
 | ||
|  | <!-- eslint-disable no-undef --> | ||
|  | 
 | ||
|  | ```js | ||
|  | router.param('user_id', function (req, res, next, id) { | ||
|  |   User.find(id, function (err, user) { | ||
|  |     if (err) { | ||
|  |       return next(err) | ||
|  |     } else if (!user) { | ||
|  |       return next(new Error('failed to load user')) | ||
|  |     } | ||
|  |     req.user = user | ||
|  | 
 | ||
|  |     // continue processing the request | ||
|  |     next() | ||
|  |   }) | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | ### router.route(path)
 | ||
|  | 
 | ||
|  | Creates an instance of a single `Route` for the given `path`. | ||
|  | (See `Router.Route` below) | ||
|  | 
 | ||
|  | Routes can be used to handle http `methods` with their own, optional middleware. | ||
|  | 
 | ||
|  | Using `router.route(path)` is a recommended approach to avoiding duplicate | ||
|  | route naming and thus typo errors. | ||
|  | 
 | ||
|  | <!-- eslint-disable no-undef, no-unused-vars --> | ||
|  | 
 | ||
|  | ```js | ||
|  | var api = router.route('/api/') | ||
|  | ``` | ||
|  | 
 | ||
|  | ## Router.Route(path)
 | ||
|  | 
 | ||
|  | Represents a single route as an instance that can be used to handle http | ||
|  | `methods` with it's own, optional middleware. | ||
|  | 
 | ||
|  | ### route\[method](handler)
 | ||
|  | 
 | ||
|  | These are functions which you can directly call on a route to register a new | ||
|  | `handler` for the `method` on the route. | ||
|  | 
 | ||
|  | <!-- eslint-disable no-undef --> | ||
|  | 
 | ||
|  | ```js | ||
|  | // handle a `GET` request | ||
|  | var status = router.route('/status') | ||
|  | 
 | ||
|  | status.get(function (req, res) { | ||
|  |   res.setHeader('Content-Type', 'text/plain; charset=utf-8') | ||
|  |   res.end('All Systems Green!') | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | ### route.all(handler)
 | ||
|  | 
 | ||
|  | Adds a handler for all HTTP methods to this route. | ||
|  | 
 | ||
|  | The handler can behave like middleware and call `next` to continue processing | ||
|  | rather than responding. | ||
|  | 
 | ||
|  | <!-- eslint-disable no-undef --> | ||
|  | 
 | ||
|  | ```js | ||
|  | router.route('/') | ||
|  |   .all(function (req, res, next) { | ||
|  |     next() | ||
|  |   }) | ||
|  |   .all(checkSomething) | ||
|  |   .get(function (req, res) { | ||
|  |     res.setHeader('Content-Type', 'text/plain; charset=utf-8') | ||
|  |     res.end('Hello World!') | ||
|  |   }) | ||
|  | ``` | ||
|  | 
 | ||
|  | ## Middleware
 | ||
|  | 
 | ||
|  | Middleware (and method handlers) are functions that follow specific function | ||
|  | parameters and have defined behavior when used with `router`. The most common | ||
|  | format is with three parameters - "req", "res" and "next". | ||
|  | 
 | ||
|  | - `req`  - This is a [HTTP incoming message](https://nodejs.org/api/http.html#http_http_incomingmessage) instance. | ||
|  | - `res`  - This is a [HTTP server response](https://nodejs.org/api/http.html#http_class_http_serverresponse) instance. | ||
|  | - `next` - Calling this function that tells `router` to proceed to the next matching middleware or method handler. It accepts an error as the first argument. | ||
|  | 
 | ||
|  | The function can optionally return a `Promise` object. If a `Promise` object | ||
|  | is returned from the function, the router will attach an `onRejected` callback | ||
|  | using `.then`. If the promise is rejected, `next` will be called with the | ||
|  | rejected value, or an error if the value is falsy. | ||
|  | 
 | ||
|  | Middleware and method handlers can also be defined with four arguments. When | ||
|  | the function has four parameters defined, the first argument is an error and | ||
|  | subsequent arguments remain, becoming - "err", "req", "res", "next". These | ||
|  | functions are "error handling middleware", and can be used for handling | ||
|  | errors that occurred in previous handlers (E.g. from calling `next(err)`). | ||
|  | This is most used when you want to define arbitrary rendering of errors. | ||
|  | 
 | ||
|  | <!-- eslint-disable no-undef --> | ||
|  | 
 | ||
|  | ```js | ||
|  | router.get('/error_route', function (req, res, next) { | ||
|  |   return next(new Error('Bad Request')) | ||
|  | }) | ||
|  | 
 | ||
|  | router.use(function (err, req, res, next) { | ||
|  |   res.end(err.message) //= > "Bad Request" | ||
|  | }) | ||
|  | ``` | ||
|  | 
 | ||
|  | Error handling middleware will **only** be invoked when an error was given. As | ||
|  | long as the error is in the pipeline, normal middleware and handlers will be | ||
|  | bypassed - only error handling middleware will be invoked with an error. | ||
|  | 
 | ||
|  | ## Examples
 | ||
|  | 
 | ||
|  | ```js | ||
|  | // import our modules | ||
|  | var http = require('http') | ||
|  | var Router = require('router') | ||
|  | var finalhandler = require('finalhandler') | ||
|  | var compression = require('compression') | ||
|  | var bodyParser = require('body-parser') | ||
|  | 
 | ||
|  | // store our message to display | ||
|  | var message = 'Hello World!' | ||
|  | 
 | ||
|  | // initialize the router & server and add a final callback. | ||
|  | var router = Router() | ||
|  | var server = http.createServer(function onRequest (req, res) { | ||
|  |   router(req, res, finalhandler(req, res)) | ||
|  | }) | ||
|  | 
 | ||
|  | // use some middleware and compress all outgoing responses | ||
|  | router.use(compression()) | ||
|  | 
 | ||
|  | // handle `GET` requests to `/message` | ||
|  | router.get('/message', function (req, res) { | ||
|  |   res.statusCode = 200 | ||
|  |   res.setHeader('Content-Type', 'text/plain; charset=utf-8') | ||
|  |   res.end(message + '\n') | ||
|  | }) | ||
|  | 
 | ||
|  | // create and mount a new router for our API | ||
|  | var api = Router() | ||
|  | router.use('/api/', api) | ||
|  | 
 | ||
|  | // add a body parsing middleware to our API | ||
|  | api.use(bodyParser.json()) | ||
|  | 
 | ||
|  | // handle `PATCH` requests to `/api/set-message` | ||
|  | api.patch('/set-message', function (req, res) { | ||
|  |   if (req.body.value) { | ||
|  |     message = req.body.value | ||
|  | 
 | ||
|  |     res.statusCode = 200 | ||
|  |     res.setHeader('Content-Type', 'text/plain; charset=utf-8') | ||
|  |     res.end(message + '\n') | ||
|  |   } else { | ||
|  |     res.statusCode = 400 | ||
|  |     res.setHeader('Content-Type', 'text/plain; charset=utf-8') | ||
|  |     res.end('Invalid API Syntax\n') | ||
|  |   } | ||
|  | }) | ||
|  | 
 | ||
|  | // make our http server listen to connections | ||
|  | server.listen(8080) | ||
|  | ``` | ||
|  | 
 | ||
|  | You can get the message by running this command in your terminal, | ||
|  |  or navigating to `127.0.0.1:8080` in a web browser. | ||
|  | ```bash | ||
|  | curl http://127.0.0.1:8080 | ||
|  | ``` | ||
|  | 
 | ||
|  | You can set the message by sending it a `PATCH` request via this command: | ||
|  | ```bash | ||
|  | curl http://127.0.0.1:8080/api/set-message -X PATCH -H "Content-Type: application/json" -d '{"value":"Cats!"}' | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Example using mergeParams
 | ||
|  | 
 | ||
|  | ```js | ||
|  | var http = require('http') | ||
|  | var Router = require('router') | ||
|  | var finalhandler = require('finalhandler') | ||
|  | 
 | ||
|  | // this example is about the mergeParams option | ||
|  | var opts = { mergeParams: true } | ||
|  | 
 | ||
|  | // make a router with out special options | ||
|  | var router = Router(opts) | ||
|  | var server = http.createServer(function onRequest (req, res) { | ||
|  |   // set something to be passed into the router | ||
|  |   req.params = { type: 'kitten' } | ||
|  | 
 | ||
|  |   router(req, res, finalhandler(req, res)) | ||
|  | }) | ||
|  | 
 | ||
|  | router.get('/', function (req, res) { | ||
|  |   res.statusCode = 200 | ||
|  |   res.setHeader('Content-Type', 'text/plain; charset=utf-8') | ||
|  | 
 | ||
|  |   // with respond with the the params that were passed in | ||
|  |   res.end(req.params.type + '\n') | ||
|  | }) | ||
|  | 
 | ||
|  | // make another router with our options | ||
|  | var handler = Router(opts) | ||
|  | 
 | ||
|  | // mount our new router to a route that accepts a param | ||
|  | router.use('/:path', handler) | ||
|  | 
 | ||
|  | handler.get('/', function (req, res) { | ||
|  |   res.statusCode = 200 | ||
|  |   res.setHeader('Content-Type', 'text/plain; charset=utf-8') | ||
|  | 
 | ||
|  |   // will respond with the param of the router's parent route | ||
|  |   res.end(req.params.path + '\n') | ||
|  | }) | ||
|  | 
 | ||
|  | // make our http server listen to connections | ||
|  | server.listen(8080) | ||
|  | ``` | ||
|  | 
 | ||
|  | Now you can get the type, or what path you are requesting: | ||
|  | ```bash | ||
|  | curl http://127.0.0.1:8080 | ||
|  | > kitten
 | ||
|  | curl http://127.0.0.1:8080/such_path | ||
|  | > such_path
 | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Example of advanced `.route()` usage
 | ||
|  | 
 | ||
|  | This example shows how to implement routes where there is a custom | ||
|  | handler to execute when the path matched, but no methods matched. | ||
|  | Without any special handling, this would be treated as just a | ||
|  | generic non-match by `router` (which typically results in a 404), | ||
|  | but with a custom handler, a `405 Method Not Allowed` can be sent. | ||
|  | 
 | ||
|  | ```js | ||
|  | var http = require('http') | ||
|  | var finalhandler = require('finalhandler') | ||
|  | var Router = require('router') | ||
|  | 
 | ||
|  | // create the router and server | ||
|  | var router = new Router() | ||
|  | var server = http.createServer(function onRequest (req, res) { | ||
|  |   router(req, res, finalhandler(req, res)) | ||
|  | }) | ||
|  | 
 | ||
|  | // register a route and add all methods | ||
|  | router.route('/pet/:id') | ||
|  |   .get(function (req, res) { | ||
|  |     // this is GET /pet/:id | ||
|  |     res.setHeader('Content-Type', 'application/json') | ||
|  |     res.end(JSON.stringify({ name: 'tobi' })) | ||
|  |   }) | ||
|  |   .delete(function (req, res) { | ||
|  |     // this is DELETE /pet/:id | ||
|  |     res.end() | ||
|  |   }) | ||
|  |   .all(function (req, res) { | ||
|  |     // this is called for all other methods not | ||
|  |     // defined above for /pet/:id | ||
|  |     res.statusCode = 405 | ||
|  |     res.end() | ||
|  |   }) | ||
|  | 
 | ||
|  | // make our http server listen to connections | ||
|  | server.listen(8080) | ||
|  | ``` | ||
|  | 
 | ||
|  | ## License
 | ||
|  | 
 | ||
|  | [MIT](LICENSE) | ||
|  | 
 | ||
|  | [ci-image]: https://badgen.net/github/checks/pillarjs/router/master?label=ci | ||
|  | [ci-url]: https://github.com/pillarjs/router/actions/workflows/ci.yml | ||
|  | [npm-image]: https://img.shields.io/npm/v/router.svg | ||
|  | [npm-url]: https://npmjs.org/package/router | ||
|  | [node-version-image]: https://img.shields.io/node/v/router.svg | ||
|  | [node-version-url]: http://nodejs.org/download/ | ||
|  | [coveralls-image]: https://img.shields.io/coveralls/pillarjs/router/master.svg | ||
|  | [coveralls-url]: https://coveralls.io/r/pillarjs/router?branch=master | ||
|  | [downloads-image]: https://img.shields.io/npm/dm/router.svg | ||
|  | [downloads-url]: https://npmjs.org/package/router |