@e22m4u/js-trie-router
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -18,3 +18,3 @@ import http from 'http'; | ||
const server = new http.Server(); | ||
server.on('request', router.requestHandler); | ||
server.on('request', router.requestListener); | ||
@@ -21,0 +21,0 @@ // слушаем входящие запросы |
@@ -18,3 +18,3 @@ import http from 'http'; | ||
const server = new http.Server(); | ||
server.on('request', router.requestHandler); | ||
server.on('request', router.requestListener); | ||
@@ -21,0 +21,0 @@ // слушаем входящие запросы |
@@ -18,3 +18,3 @@ import http from 'http'; | ||
const server = new http.Server(); | ||
server.on('request', router.requestHandler); | ||
server.on('request', router.requestListener); | ||
@@ -21,0 +21,0 @@ // слушаем входящие запросы |
@@ -30,3 +30,3 @@ import http from 'http'; | ||
const server = new http.Server(); | ||
server.on('request', router.requestHandler); | ||
server.on('request', router.requestListener); | ||
@@ -33,0 +33,0 @@ // слушаем входящие запросы |
{ | ||
"name": "@e22m4u/js-trie-router", | ||
"version": "0.0.2", | ||
"description": "Trie-based router for Node.js", | ||
"version": "0.0.3", | ||
"description": "Trie-роутер для Node.js", | ||
"type": "module", | ||
@@ -6,0 +6,0 @@ "main": "src/index.js", |
190
README.md
## @e22m4u/js-trie-router | ||
A pure ES-module of the Node.js HTTP router that uses the | ||
[Trie](https://en.wikipedia.org/wiki/Trie) for routing. | ||
ES-модуль HTTP роутера для Node.js, использующий | ||
[Trie](https://en.wikipedia.org/wiki/Trie) | ||
для разрешения маршрутов. | ||
- Uses [path-to-regexp](https://github.com/pillarjs/path-to-regexp) syntax. | ||
- Supports path parameters. | ||
- Parses JSON-body automatically. | ||
- Parses a query string and a `cookie` header. | ||
- Supports `preHandler` and `postHandler` hooks. | ||
- Asynchronous request handler. | ||
- Поддержка [path-to-regexp](https://github.com/pillarjs/path-to-regexp) синтаксиса. | ||
- Автоматический парсинг JSON-тела запроса. | ||
- Парсинг строки запроса и заголовка `cookie`. | ||
- Поддержка `preHandler` и `postHandler` хуков. | ||
- Позволяет использовать асинхронные обработчики. | ||
## Installation | ||
## Установка | ||
@@ -19,21 +19,21 @@ ```bash | ||
To load an ES-module set `"type": "module"` in the `package.json` | ||
or use the `.mjs` extension. | ||
Для загрузки ES-модуля требуется установить `"type": "module"` в файле | ||
`package.json`, или использовать `.mjs` расширение. | ||
## Overview | ||
## Обзор | ||
A basic "Hello world." example. | ||
Базовый пример создания экземпляра роутера, объявления маршрута | ||
и передачи слушателя запросов `http` серверу. | ||
```js | ||
import http from 'http'; | ||
import {TrieRouter} from '../src/index.js'; | ||
import {HTTP_METHOD} from '../src/route.js'; | ||
import {TrieRouter} from '@e22m4u/js-path-trie'; | ||
const server = new http.Server(); // A Node.js HTTP server. | ||
const router = new TrieRouter(); // A TrieRouter instance. | ||
const server = new http.Server(); // создание экземпляра HTTP сервера | ||
const router = new TrieRouter(); // создание экземпляра роутера | ||
router.defineRoute({ | ||
method: HTTP_METHOD.GET, // Request method. | ||
path: '/', // Path template like "/user/:id". | ||
handler(ctx) { // Request handler. | ||
method: 'GET', // метод запроса "GET", "POST" и т.д. | ||
path: '/', // шаблон пути, пример "/user/:id" | ||
handler(ctx) { // обработчик маршрута | ||
return 'Hello world!'; | ||
@@ -43,4 +43,4 @@ }, | ||
server.on('request', router.requestHandler); | ||
server.listen(3000, 'localhost'); | ||
server.on('request', router.requestListener); // подключение роутера | ||
server.listen(3000, 'localhost'); // прослушивание запросов | ||
@@ -50,22 +50,24 @@ // Open in browser http://localhost:3000 | ||
### RequestContext | ||
### Контекст запроса | ||
The first parameter of a route handler is a `RequestContext` instance. | ||
Первый параметр обработчика маршрута принимает экземпляр класса | ||
`RequestContext` с набором свойств, содержащих разобранные | ||
данные входящего запроса. | ||
- `container: ServiceContainer` is an instance of the [ServiceContainer](https://npmjs.com/package/@e22m4u/js-service) | ||
- `req: IncomingMessage` is a native request from the `http` module | ||
- `res: ServerResponse` is a native response from the `http` module | ||
- `params: ParsedParams` is a key-value object of path parameters | ||
- `query: ParsedQuery` is a key-value object of a parsed query string | ||
- `headers: ParsedHeaders` is a key-value object of request headers | ||
- `cookie: ParsedCookie` is a key-value object of a parsed `cookie` header | ||
- `method: string` is a request method in lower case like `get`, `post` etc. | ||
- `path: string` is a request pathname with a query string | ||
- `pathname: string` is a request pathname without a query string | ||
- `container: ServiceContainer` экземпляр [сервис-контейнера](https://npmjs.com/package/@e22m4u/js-service) | ||
- `req: IncomingMessage` нативный поток запроса модуля `http` | ||
- `res: ServerResponse` нативный поток ответа модуля `http` | ||
- `params: ParsedParams` объект ключ-значение с параметрами пути | ||
- `query: ParsedQuery` объект ключ-значение с параметрами строки запроса | ||
- `headers: ParsedHeaders` объект ключ-значение с заголовками запроса | ||
- `cookie: ParsedCookie` объект ключ-значение разобранного заголовка `cookie` | ||
- `method: string` метод запроса в верхнем регистре, например `GET`, `POST` и т.д. | ||
- `path: string` путь включающий строку запроса, например `/myPath?foo=bar` | ||
- `pathname: string` путь запроса, например `/myMath` | ||
Here are possible values of RequestContext properties. | ||
Пример доступа к контексту из обработчика маршрута. | ||
```js | ||
router.defineRoute({ | ||
method: 'get', | ||
method: 'GET', | ||
path: '/users/:id', | ||
@@ -81,3 +83,3 @@ handler(ctx) { | ||
console.log(ctx.cookie); // {foo: 'bar', baz: 'qux'} | ||
console.log(ctx.method); // "get" | ||
console.log(ctx.method); // "GET" | ||
console.log(ctx.path); // "/users/10?include=city" | ||
@@ -90,5 +92,8 @@ console.log(ctx.pathname); // "/users/10" | ||
### Sending response | ||
### Отправка ответа | ||
Return values of a route handler will be sent as described below. | ||
Возвращаемое значение обработчика маршрута используется в качестве ответа | ||
сервера. Тип значения влияет на представление возвращаемых данных. Например, | ||
если результатом будет являться тип `object`, то такое значение автоматически | ||
сериализуется в JSON. | ||
@@ -104,10 +109,9 @@ | value | content-type | | ||
Here is an example of a JSON response. | ||
Пример возвращаемого значения обработчиком маршрута. | ||
```js | ||
router.defineRoute({ | ||
router.defineRoute({ // регистрация маршрута | ||
// ... | ||
handler(ctx) { | ||
// sends "application/json" | ||
return {foo: 'bar'}; | ||
handler(ctx) { // обработчик входящего запроса | ||
return {foo: 'bar'}; // ответ будет представлен в виде JSON | ||
}, | ||
@@ -117,4 +121,4 @@ }); | ||
If the `ServerResponse` has been sent manually, then a return | ||
value of the route handler will be ignored. | ||
Контекст запроса `ctx` содержит нативный экземпляр класса `ServerResponse`, | ||
который может быть использован для ручного управления ответом. | ||
@@ -132,21 +136,26 @@ ```js | ||
### Route hooks | ||
### Хуки маршрута | ||
A route definition allows you to set following hooks: | ||
Определение маршрута методом `defineRoute` позволяет задать хуки | ||
для отслеживания и перехвата входящего запроса и ответа | ||
конкретного маршрута. | ||
- `preHandler` is executed before a route handler. | ||
- `postHandler` is executed after a route handler. | ||
- `preHandler` выполняется перед вызовом обработчика | ||
- `postHandler` выполняется после вызова обработчика | ||
If the `preHandler` hook returns a value other than `undefined` | ||
or `null`, it will be used as the server response. | ||
#### preHandler | ||
Перед вызовом обработчика маршрута может потребоваться выполнение | ||
таких операции как авторизация и проверка параметров запроса. Для | ||
этого можно использовать хук `preHandler`. | ||
```js | ||
router.defineRoute({ | ||
router.defineRoute({ // регистрация маршрута | ||
// ... | ||
preHandler(ctx) { | ||
return 'Are you authenticated?'; | ||
// вызывается перед обработчиком | ||
console.log(`Incoming request ${ctx.method} ${ctx.path}`); | ||
// Incoming request GET /myPath | ||
}, | ||
handler(ctx) { | ||
// the request handler will be skipped because | ||
// the "preHandler" hook returns a non-empty value | ||
return 'Hello world!'; | ||
@@ -157,6 +166,29 @@ }, | ||
A return value of the route handler will be passed as the second | ||
argument to the `preHandler` hook. | ||
Если хук `preHandler` возвращает значение отличное от `undefined` и `null`, | ||
то такое значение будет использовано в качестве ответа сервера, а вызов | ||
обработчика маршрута будет пропущен. | ||
```js | ||
router.defineRoute({ // регистрация маршрута | ||
// ... | ||
preHandler(ctx) { | ||
// возвращение ответа сервера | ||
return 'Are you authorized?'; | ||
}, | ||
handler(ctx) { | ||
// данный обработчик не вызывается, так как | ||
// хук "preHandler" уже отправил ответ | ||
}, | ||
}); | ||
``` | ||
#### postHandler | ||
Возвращаемое значение обработчика маршрута передается вторым аргументом | ||
хука `postHandler`. По аналогии с `preHandler`, если возвращаемое | ||
значение отличается от `undefined` и `null`, то такое значение будет | ||
использовано в качестве ответа сервера. Это может быть полезно для | ||
модификации возвращаемого ответа. | ||
```js | ||
router.defineRoute({ | ||
@@ -167,4 +199,4 @@ // ... | ||
}, | ||
preHandler(ctx, data) { | ||
// after the route handler | ||
postHandler(ctx, data) { | ||
// выполняется после обработчика маршрута | ||
return data.toUpperCase(); // HELLO WORLD! | ||
@@ -175,28 +207,31 @@ }, | ||
### Global hooks | ||
### Глобальные хуки | ||
A `Router` instance allows you to set following global hooks: | ||
Экземпляр роутера `TrieRouter` позволяет задать глобальные хуки, которые | ||
имеют более высокий приоритет перед хуками маршрута, и вызываются | ||
в первую очередь. | ||
- `preHandler` is executed before each route handler. | ||
- `postHandler` is executed after each route handler. | ||
- `preHandler` выполняется перед вызовом обработчика | ||
- `postHandler` выполняется после вызова обработчика | ||
The `addHook` method of a `Router` instance accepts a hook name as the first | ||
parameter and the hook function as the second. | ||
Добавить глобальные хуки можно методом `addHook` экземпляра роутера, | ||
где первым параметром передается название хука, а вторым его функция. | ||
```js | ||
router.addHook('preHandler', (ctx) => { | ||
// executes before each route handler | ||
// вызов перед обработчиком маршрута | ||
}); | ||
router.addHook('postHandler', (ctx, data) => { | ||
// executes after each route handler | ||
// вызов после обработчика маршрута | ||
}); | ||
``` | ||
Similar to a route hook, if a global hook returns a value other than | ||
`undefined` or `null`, that value will be used as the server response. | ||
Аналогично хукам маршрута, если глобальный хук возвращает значение | ||
отличное от `undefined` и `null`, то такое значение будет использовано | ||
как ответ сервера. | ||
## Debug | ||
## Отладка | ||
Set environment variable `DEBUG=jsTrieRouter*` before start. | ||
Установка переменной `DEBUG` перед командой запуска включает вывод логов. | ||
@@ -207,3 +242,3 @@ ```bash | ||
## Testing | ||
## Тестирование | ||
@@ -214,11 +249,4 @@ ```bash | ||
## Contribution | ||
## Лицензия | ||
- Bug fixes. | ||
- Grammar correction. | ||
- Documentation improvements. | ||
- Vulnerability fixes. | ||
## License | ||
MIT |
@@ -14,3 +14,3 @@ import HttpErrors from 'http-errors'; | ||
*/ | ||
export const METHODS_WITH_BODY = ['post', 'put', 'patch', 'delete']; | ||
export const METHODS_WITH_BODY = ['POST', 'PUT', 'PATCH', 'DELETE']; | ||
@@ -104,3 +104,3 @@ /** | ||
parse(req) { | ||
if (!METHODS_WITH_BODY.includes(req.method.toLowerCase())) { | ||
if (!METHODS_WITH_BODY.includes(req.method.toUpperCase())) { | ||
this.debug( | ||
@@ -107,0 +107,0 @@ 'Body parsing was skipped for the %s request.', |
@@ -73,3 +73,3 @@ import {Errorf} from '@e22m4u/js-format'; | ||
get method() { | ||
return this.req.method.toLowerCase(); | ||
return this.req.method.toUpperCase(); | ||
} | ||
@@ -76,0 +76,0 @@ |
@@ -91,8 +91,8 @@ import {expect} from './chai.js'; | ||
describe('method', function () { | ||
it('returns the method name in lower case', function () { | ||
const req = createRequestMock({method: 'POST'}); | ||
it('returns the method name in upper case', function () { | ||
const req = createRequestMock({method: 'post'}); | ||
const res = createResponseMock(); | ||
const cnt = new ServiceContainer(); | ||
const ctx = new RequestContext(cnt, req, res); | ||
expect(ctx.method).to.be.eq('post'); | ||
expect(ctx.method).to.be.eq('POST'); | ||
}); | ||
@@ -99,0 +99,0 @@ }); |
@@ -64,3 +64,3 @@ import {Route} from './route.js'; | ||
); | ||
const triePath = `${req.method.toLowerCase()}/${requestPath}`; | ||
const triePath = `${req.method.toUpperCase()}/${requestPath}`; | ||
const resolved = this._trie.match(triePath); | ||
@@ -67,0 +67,0 @@ if (resolved) { |
@@ -25,15 +25,15 @@ import {Errorf} from '@e22m4u/js-format'; | ||
* @type {{ | ||
* DELETE: 'delete', | ||
* POST: 'post', | ||
* GET: 'get', | ||
* PUT: 'put', | ||
* PATCH: 'patch', | ||
* GET: 'GET', | ||
* POST: 'POST', | ||
* PUT: 'PUT', | ||
* PATCH: 'PATCH', | ||
* DELETE: 'DELETE', | ||
* }} | ||
*/ | ||
export const HTTP_METHOD = { | ||
GET: 'get', | ||
POST: 'post', | ||
PUT: 'put', | ||
PATCH: 'patch', | ||
DELETE: 'delete', | ||
GET: 'GET', | ||
POST: 'POST', | ||
PUT: 'PUT', | ||
PATCH: 'PATCH', | ||
DELETE: 'DELETE', | ||
}; | ||
@@ -138,3 +138,3 @@ | ||
); | ||
this._method = routeDef.method.toLowerCase(); | ||
this._method = routeDef.method.toUpperCase(); | ||
if (typeof routeDef.path !== 'string') | ||
@@ -141,0 +141,0 @@ throw new Errorf( |
@@ -198,9 +198,9 @@ import {Route} from './route.js'; | ||
it('sets the option "method" in lowercase to the "method" property', function () { | ||
it('sets the option "method" in upper case to the "method" property', function () { | ||
const route = new Route({ | ||
method: 'POST', | ||
method: 'post', | ||
path: '/', | ||
handler: () => undefined, | ||
}); | ||
expect(route.method).to.be.eq('post'); | ||
expect(route.method).to.be.eq('POST'); | ||
}); | ||
@@ -207,0 +207,0 @@ |
@@ -42,3 +42,3 @@ import {Route} from './route.js'; | ||
/** | ||
* Request handler. | ||
* Request listener. | ||
* | ||
@@ -52,4 +52,4 @@ * Example: | ||
* const server = new http.Server(); | ||
* server.on('request', router.requestHandler); // Sets the request handler. | ||
* server.listen(3000); // Starts listening for connections. | ||
* server.on('request', router.requestListener); // Sets the request listener. | ||
* server.listen(3000); // Starts listening for connections. | ||
* ``` | ||
@@ -59,3 +59,3 @@ * | ||
*/ | ||
get requestHandler(): RequestListener; | ||
get requestListener(): RequestListener; | ||
@@ -62,0 +62,0 @@ /** |
@@ -50,3 +50,3 @@ import {Service} from './service.js'; | ||
/** | ||
* Request handler. | ||
* Request listener. | ||
* | ||
@@ -60,4 +60,4 @@ * Example: | ||
* const server = new http.Server(); | ||
* server.on('request', router.requestHandler); // Sets the request handler. | ||
* server.listen(3000); // Starts listening for connections. | ||
* server.on('request', router.requestListener); // Sets the request listener. | ||
* server.listen(3000); // Starts listening for connections. | ||
* ``` | ||
@@ -67,3 +67,3 @@ * | ||
*/ | ||
get requestHandler() { | ||
get requestListener() { | ||
return this._handleRequest.bind(this); | ||
@@ -70,0 +70,0 @@ } |
@@ -28,6 +28,6 @@ import {describe} from 'mocha'; | ||
describe('requestHandler', function () { | ||
describe('requestListener', function () { | ||
it('to be a function', function () { | ||
const router = new TrieRouter(); | ||
expect(typeof router.requestHandler).to.be.eq('function'); | ||
expect(typeof router.requestListener).to.be.eq('function'); | ||
}); | ||
@@ -47,3 +47,3 @@ | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
}); | ||
@@ -63,3 +63,3 @@ | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
}); | ||
@@ -79,3 +79,3 @@ | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
}); | ||
@@ -95,3 +95,3 @@ | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
}); | ||
@@ -112,3 +112,3 @@ | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
}); | ||
@@ -129,3 +129,3 @@ | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
}); | ||
@@ -148,3 +148,3 @@ | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
}); | ||
@@ -169,3 +169,3 @@ | ||
}); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
}); | ||
@@ -193,3 +193,3 @@ | ||
}); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
}); | ||
@@ -220,3 +220,3 @@ | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
const result = await res.getBody(); | ||
@@ -249,3 +249,3 @@ expect(result).to.be.eq(body); | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
const result = await res.getBody(); | ||
@@ -281,3 +281,3 @@ expect(result).to.be.eq(body); | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
const result = await res.getBody(); | ||
@@ -317,3 +317,3 @@ expect(result).to.be.eq(body); | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
const result = await res.getBody(); | ||
@@ -348,3 +348,3 @@ expect(result).to.be.eq(body); | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
const result = await res.getBody(); | ||
@@ -379,3 +379,3 @@ expect(result).to.be.eq(body); | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
const result = await res.getBody(); | ||
@@ -410,3 +410,3 @@ expect(result).to.be.eq(body); | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
const result = await res.getBody(); | ||
@@ -441,3 +441,3 @@ expect(result).to.be.eq(preHandlerBody); | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
const result = await res.getBody(); | ||
@@ -469,3 +469,3 @@ expect(result).not.to.be.eq(handlerBody); | ||
const res = createResponseMock(); | ||
router.requestHandler(req, res); | ||
router.requestListener(req, res); | ||
const result = await res.getBody(); | ||
@@ -472,0 +472,0 @@ expect(result).to.be.eq(body); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
222770
240