classrouter
TypeScript icon, indicating that this package has built-in type declarations

4.4.2 • Public • Published

Classrouter

expressjs based routing system.

npm install classrouter

future


  • Routing
  • Param pipe
  • Error handle
  • Response filter

get start


// sample controller
// home.controller.ts
 
@Controller({
    // name param requared. use  the  param. Swagger json. 
    name: 'home'
})
export class HomeController {
    @Get(path :'/')
    home(){
        return new ViewResponse('home.html', {
            message : 'welcome'
        });
    }
 
    @Get(path :'/sitemap.xml')
    sitemap(@BodyParam(VaidationPipe()) body:{user:string, pass:string}){
        return new XmlResponse(...);
    }
 
    @Get(path :'/robots.txt')
    robots(@BodyParam(VaidationPipe()) body:{user:string, pass:string}){
        return new PlanResponse(...);
    }
}
// sample Rest API
// user.controller.ts
 
@Controller({
   name: 'user',  path: '/user'
})
export class UserController {
   @Get(path :'/')
   list(@QueryParam('limit', IntPipe() ) id:number){
       return [...]
   }
 
   @Post(path :'/')
   save(@BodyParam(VaidationPipe()) body:{user:string, pass:string}){
       return {success : 'ok'}
   }
}
// sample server
// src/server.ts
 
async function startup() {
    let app = express();
    let factory = new ClassrouterFactory({
        basePath: '/api',
        logger: (l: string, m: string, o: any) => console.log(l, m, o),
        routerBuilder: () => express.Router(),
        controllers: [aController],
        responseFilters: {
            default: new JsonResponseFilter(),
            filters: [/*XML, Plan, File, */] // your custom response filters
        }
    })
 
    factory.build(app);
 
    app.listen(3000, () => {
        console.log('listen 3000');
    });
}
 
startup().catch(console.log);

routing

supported HttpMethod. Get, Post, Put, Push, Delete, Head

supporting route:

- MethodRoute 
- Classrouting
    // routing path:
    // <basePath>/<MainController-path>/<ChildController-path>/<Action-Path>
// method route
// call addr : [GET]<basePath>/sample/foo/bar
@Controller({name:'sample', path :'/sample'})
class SampleController {
 
    @Get({path: '/foo/bar'})
    sampleMethod(){
        return { ... }
    }
}
 
// class route
// call addr : [GET]<basePath>/foo/bar
@Get({path: '/foo/bar', name:'sample'})
class SampleAction {
 
    
    @Action() // register action method
    doAction(){
        return { ... }
    }
}
 
@Controller({
    name:'sample',
    actions : [SampleAction] //<- register class action
})
class SampleController {
 
    /**
     *  classAction with methodAction supported
     */
    @Get({...})
    foo(){ 
        return { ... }
    }
 
    @Get({...})
    bar(){
        return { ... }
    }
}

param

suporting param types. Query, Path, Body, Request, Header, Cookie;


@Get(filedName?: string | string[], ...pipe: IPipeTransform[])

fieldName empty : All params
fieldName defined : the field name resolved 
@Get({name : 'sample', path: '/user/edit/:id'})
class SampleAction{
 
    @PathParam('id') // "id" - path param name
    idStr:string = 0 // default value = 0;
 
    @PathParam() // not defined param name
    appPathParams : any; // all path params.  {id:string}
 
    @PathParam('id', IntPipe()) // intPipe is  string to number convert
    idNum:number = '' // default value = '';
 
    @RequestParam('session')
    session:any;
 
 
    @Action()
    doAction(){
        return {
            idStr: this.idStr,
            idNum: this.idNum,
            appPathParams: this.appPathParams,
            sessionName: this.session || this.session.name || 'no name',
        }
    }
}
 
 
@Controller({ ... })
class SampleController{
 
    // call addr : <basePath>/list?limit=10
    @Get({path: '/list'})
    list( @QueryParam('limit', IntPipe()) limit ){
        return [...]
    }
 
    @Post({path: '/save', before : [JsonbodyParser] })
    Save( 
        @BodyParam('name') name ,
        @RequestParam(new FlashPipe()) flash 
    ){
 
        try{
            // save process
            ...
 
            flash.message('save process success');
            return 'success result';
        }
        catch(err:Error){
            flash.message('save process fail.' + err.message );
             return 'fail result';
        }
    }
}

pipe

class IntPipe implements IPipeTransform {
    transform(idStr: string) { 
        return parseInt(idStr); 
    }
}
 
class ModelPipe implements IPipeTransform {
    constructor (private EntityType: IModel){
 
    }
    async transform(idNum: number) {
        let entity =  await this.EntityType.findById(idNum);
        if(entity) {
            return entity;
        }
        throw new Error('not found Entity id');
    }
}
 
@QueryParam('id', new IntPipe(), new ModelPipe(UserEntity))
 
// id string ==> id number ==> UserEntity find by id param

Error handle

// class action error handle
 
class SampleAction {
 
 
    @Action({errorHandle : 'onError'})
    doAction(){
        throw new Error("test error")
    }
 
    onError(err:any){
        return new ViewResponse('error.html',{
            title : 'Error page',
            error : err
        });
    }
 
}
// method action error handle
 
class FooError {}
class BaaError {}
 
@Controller({ name:'sample', errorHandle : 'onError' })
class SampleController {
 
    @Get({ path: '/', errorHandle : 'sampleError' })
    sample(){
        throw new Error();
    }
 
    @Get({ })
    foo(){
        throw new FooError();
    }
 
    @Get({ })
    baa(err:any){
       throw new BaaError();
    }
 
    // contoller all action Error handle
    onError(err:any){
        return ...
    }
 
    // only "sample" method action Error handle
    sampleError(err:any){
 
    }
 
 
    // controller all action Error handle. filtering instanceOf FooError
    @ErrorHandle({ instanceOf: FooError })
    fooError(err:any){
        return ...
    }
 
    // all action handle. filtering function
    @ErrorHandle({ when: (err) => err instanceof BaaError })
    baaError(err:any){
        return ...
    }
 
}

response filter

interface IFilterParam {
    actionResult: any
    expressRes: express.Response
    expressReq: express.Request
    handled: boolean
}
 
interface IResponseFilter {
    filter(param: IFilterParam): void | Promise<void>
}
export class RedirectResponse {
    statusCode: number
    constructor(public uri: string, temp: boolean = true) {
        this.statusCode = temp ? 302 : 301
    }
}
 
export class RedirectResponseFilter implements IResponseFilter {
    filter(params: IFilterParam) {
        let { actionResult, expressRes } = params;
        if (actionResult instanceof RedirectResponse) {
            expressRes.redirect(actionResult.statusCode, actionResult.uri);
            params.handled = true;  // this response filter handled.
        }
    }
}
 
// use
&Controller( ... )
class SampleController(){
 
    @Get({path: '/edit/:id'})
    edit(){
        ...
 
        if( checkId ) {
            return new ViewResponse( ... );
        }else{
            return new RedirectResponse(`/error`, false);
        }
    }
    
    @Post({path:'/save'})
    doSave(){
        ...
 
        if(isError) {
            return new RedirectResponse(`/edit/${id}`);
        }
 
        // do save process
        ...
    }
}
 
// register response filter
// classrouterFactory.setupResonsefilter(new JsonResponseFilter())
 

sample React response

// used by react serverside rendering. 
// using the react side render
export class ReactResponseFilter implements IResponseFilter {
    
 
    async buildAppContext(req: express.Request): Promise<IAppContext> {
        let flash = null;
        if (req.session) {
            flash = req.session.flash;
            delete req.session.flash;
        }
 
        return {
            rqPath: req.originalUrl,
            expressReq: req,
            flash,
        }
    }
    async filter(param: IFilterParam) {
        let { actionResult, expressRes, expressReq } = param;
 
        if (React.isValidElement(actionResult)) {
 
            let appContext = await this.buildAppContext(expressReq);
 
            ReactDOM.renderToNodeStream(<AppContext.Provider value={appContext}>{actionResult} </AppContext.Provider>)
                .pipe(expressRes);
 
            param.handled = true;
        }
    }  
}
 
 
@Get( ... )
class SampleAction {
 
    @Action()
    doAction(@QueryParam('name') name:string ){
        return <div>
            name : {{name}}
        </div>
    }
}
 
 
 
// register response filter
// classrouterFactory.setupResonsefilter(new ReactResponseFilter())
 

Package Sidebar

Install

npm i classrouter

Weekly Downloads

0

Version

4.4.2

License

ISC

Unpacked Size

180 kB

Total Files

169

Last publish

Collaborators

  • farcek