Controller: fix route() parsing with regexp params
Also allow numbers in route param names
This commit is contained in:
parent
4692a23696
commit
69e9f3ce9c
@ -9,6 +9,9 @@ import Middleware, {MiddlewareType} from "./Middleware";
|
|||||||
import Application from "./Application";
|
import Application from "./Application";
|
||||||
|
|
||||||
export default abstract class Controller {
|
export default abstract class Controller {
|
||||||
|
/**
|
||||||
|
* TODO: this should not be static, it should actually be bound to an app instance.
|
||||||
|
*/
|
||||||
private static readonly routes: { [p: string]: string | undefined } = {};
|
private static readonly routes: { [p: string]: string | undefined } = {};
|
||||||
|
|
||||||
public static route(
|
public static route(
|
||||||
@ -20,11 +23,12 @@ export default abstract class Controller {
|
|||||||
let path = this.routes[route];
|
let path = this.routes[route];
|
||||||
if (path === undefined) throw new Error(`Unknown route for name ${route}.`);
|
if (path === undefined) throw new Error(`Unknown route for name ${route}.`);
|
||||||
|
|
||||||
|
const regExp = this.getRouteParamRegExp('[a-zA-Z0-9_-]+', 'g');
|
||||||
if (typeof params === 'string' || typeof params === 'number') {
|
if (typeof params === 'string' || typeof params === 'number') {
|
||||||
path = path.replace(/:[a-zA-Z_-]+\??/g, '' + params);
|
path = path.replace(regExp, '' + params);
|
||||||
} else if (Array.isArray(params)) {
|
} else if (Array.isArray(params)) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const match of path.matchAll(/:[a-zA-Z_-]+(\(.*\))?\??/g)) {
|
for (const match of path.matchAll(regExp)) {
|
||||||
if (match.length > 0) {
|
if (match.length > 0) {
|
||||||
path = path.replace(match[0], typeof params[i] !== 'undefined' ? params[i] : '');
|
path = path.replace(match[0], typeof params[i] !== 'undefined' ? params[i] : '');
|
||||||
}
|
}
|
||||||
@ -33,7 +37,7 @@ export default abstract class Controller {
|
|||||||
path = path.replace(/\/+/g, '/');
|
path = path.replace(/\/+/g, '/');
|
||||||
} else {
|
} else {
|
||||||
for (const key of Object.keys(params)) {
|
for (const key of Object.keys(params)) {
|
||||||
path = path.replace(new RegExp(`:${key}\\??`), params[key]);
|
path = path.replace(this.getRouteParamRegExp(key), params[key].toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +45,10 @@ export default abstract class Controller {
|
|||||||
return `${absolute ? config.get<string>('public_url') : ''}${path}` + (queryStr.length > 0 ? '?' + queryStr : '');
|
return `${absolute ? config.get<string>('public_url') : ''}${path}` + (queryStr.length > 0 ? '?' + queryStr : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static getRouteParamRegExp(key: string, flags?: string): RegExp {
|
||||||
|
return new RegExp(`:${key}(\\(.+?\\))?\\??`, flags);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly router: Router = express.Router();
|
private readonly router: Router = express.Router();
|
||||||
private readonly fileUploadFormRouter: Router = express.Router();
|
private readonly fileUploadFormRouter: Router = express.Router();
|
||||||
private app?: Application;
|
private app?: Application;
|
||||||
@ -194,4 +202,4 @@ export default abstract class Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RouteParams = { [p: string]: string } | string[] | string | number;
|
export type RouteParams = { [p: string]: string | number } | string[] | string | number;
|
||||||
|
35
test/Controller.test.ts
Normal file
35
test/Controller.test.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import Controller from "../src/Controller";
|
||||||
|
|
||||||
|
describe('Controller.route()', () => {
|
||||||
|
new class extends Controller {
|
||||||
|
public routes(): void {
|
||||||
|
const emptyHandler = () => {
|
||||||
|
//
|
||||||
|
};
|
||||||
|
this.get('/test/no-param', emptyHandler, 'test.no-param');
|
||||||
|
this.get('/test/no-param/', emptyHandler, 'test.no-param-slash');
|
||||||
|
this.get('/test/one-param/:param', emptyHandler, 'test.one-param');
|
||||||
|
this.get('/test/two-params/:param1/:param2', emptyHandler, 'test.two-params');
|
||||||
|
this.get('/test/paginated/:page([0-9]+)?', emptyHandler, 'test.paginated');
|
||||||
|
this.get('/test/slug/:slug([a-zA-Z0-9]+)?', emptyHandler, 'test.slug');
|
||||||
|
}
|
||||||
|
}().setupRoutes();
|
||||||
|
|
||||||
|
test('Empty params', () => {
|
||||||
|
expect(Controller.route('test.no-param')).toBe('/test/no-param');
|
||||||
|
expect(Controller.route('test.no-param-slash')).toBe('/test/no-param/');
|
||||||
|
expect(Controller.route('test.one-param')).toBe('/test/one-param/');
|
||||||
|
expect(Controller.route('test.two-params')).toBe('/test/two-params/');
|
||||||
|
expect(Controller.route('test.paginated')).toBe('/test/paginated/');
|
||||||
|
expect(Controller.route('test.slug')).toBe('/test/slug/');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Populated params', () => {
|
||||||
|
expect(Controller.route('test.no-param')).toBe('/test/no-param');
|
||||||
|
expect(Controller.route('test.no-param-slash')).toBe('/test/no-param/');
|
||||||
|
expect(Controller.route('test.one-param', {param: 'value'})).toBe('/test/one-param/value');
|
||||||
|
expect(Controller.route('test.two-params', {param1: 'value1', param2: 'value2'})).toBe('/test/two-params/value1/value2');
|
||||||
|
expect(Controller.route('test.paginated', {page: 5})).toBe('/test/paginated/5');
|
||||||
|
expect(Controller.route('test.slug', {slug: 'abc'})).toBe('/test/slug/abc');
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user