4.4.2 • Published 4 years ago

classrouter v4.4.2

Weekly downloads
4
License
ISC
Repository
github
Last release
4 years ago

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())
4.4.2

4 years ago

4.4.1

4 years ago

3.1.5

4 years ago

4.3.2

5 years ago

4.3.1

5 years ago

4.2.3

5 years ago

4.2.2

5 years ago

4.2.1

5 years ago

4.2.0

5 years ago

4.1.2

5 years ago

4.1.1

5 years ago

3.1.4

5 years ago

3.1.3

6 years ago

3.1.2

6 years ago

3.1.1

6 years ago

3.0.4

6 years ago

3.0.3

6 years ago

3.0.2

6 years ago

3.0.1

6 years ago