You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
At the moment when defining a httpMethod in the controller the metadata is pushed onto an array. This works fine for most cases but it provides two disadvantages in my opinion:
If we want to add custom decorators that alter the metadata (for example by adding a middleware) we need to first make sure our custom decorator is above the httpMethod one, and then we need to look inside the list for a matching key. The only problem with this is readability, I'd like the httpMethod decorator to be at the top personally.
Also the metadata reflect keys aren't exported which makes this a bit of a hack now.
It is impossible to alter the metadata from a method parameter decorator, because these are evaluated before the method decorator and thus no metadata exists at the time.
Skip to context for a better understanding of my usecase.
Expected Behavior / Possible Solution
Maybe the controller method metadata should be an object indexed by key instead of an array (just like the method parameters metadata created by the parameter decorators). And more importantly the httpMethod decorator should merge with any existing metadata for that method.
If my proposal makes sense I'm happy to hammer out a PR
Current Behavior
Covered in the summary. Currently you need to jump through a few loopholes to extend the metadata when using method decorators, and it's virtually impossible from the method param decorators.
Context
What I'm trying to accomplish is to validate request payloads using class-validator. Request payloads can be either params, body or query. The way I'm validating is by unshifting a middleware that checks the payload against a class definition and replaces the payload on the request object with the class instance or executes next with the errors found.
Here is some example code and usage:
// Decorator definitionexportfunctionwithParams<PayloadType>(type: Constructor<PayloadType>){returnwithPayloadType(type,PayloadSource.params);}exportfunctionwithBody<PayloadType>(type: Constructor<PayloadType>){returnwithPayloadType(type,PayloadSource.body);}exportfunctionwithQuery<PayloadType>(type: Constructor<PayloadType>){returnwithPayloadType(type,PayloadSource.query);}functionwithPayloadType<PayloadType>(type: Constructor<PayloadType>,source: PayloadSource){returnfunction(target: any,key: string,value: any){letmetadataList: interfaces.ControllerMethodMetadata[]=Reflect.getMetadata(// https://github.com/inversify/inversify-express-utils/blob/master/src/constants.ts"inversify-express-utils:controller-method",target.constructor);letmethodMetadata=metadataList.find(metadata=>metadata.key===key);if(methodMetadata===undefined){throw`Could not find method definition for ${key} on ${target.constructor.name} `+`when defining ${source} type. Check the order of decorators.`;}else{letvalidateMiddleware=validate(type,source);methodMetadata.middleware.unshift(validateMiddleware);}};}letvalidator=newValidator();exportfunctionvalidate<PayloadType>(type: Constructor<PayloadType>,source: PayloadSource): express.RequestHandler{return(req: express.Request,res: express.Response,next)=>{letpayload=plainToClass(type,req[source]);leterrors=validator.validateSync(payload);if(errors.length>0){next(errors);}else{req[source]=payload;next();}};}// Usage in ControllerexportclassGetByIdParams{
@IsNumberString()id: string;getID(){returnparseInt(this.id,10);}}
@controller("/people")exportclassPeopleController{constructor(@inject("PeopleService")privatepeopleService: PeopleService){}
@withParams(GetByIdParams)
@httpGet("/:id")privateasynchandleGet(
@requestParam()params: GetByIdParams,res: Response){letperson=awaitthis.peopleService.findById(params.ID);res.json(classToPlain(person));}}
This works but it feels like repetition. Theoretically I could only need the param injection decorator to also include the validation middleware, similarly to how NestJS does it.
Unrelated but the @requestParam actually doesn't work as advertised, i.e. it doesn't provide the full params if the attribute name is omitted. The attribute name can't actually be omitted, opening separate issue for this.
The text was updated successfully, but these errors were encountered:
@dcavanagh somehow this reply got lost in the weeds for me. Is this still of interest? I'll try to bang out a proof of concept, I have to reacquaint myself with the issue/solution a bit though it felt pretty straightforward if I remember correctly.
At the moment when defining a
httpMethod
in the controller themetadata
is pushed onto an array. This works fine for most cases but it provides two disadvantages in my opinion:httpMethod
one, and then we need to look inside the list for a matching key. The only problem with this is readability, I'd like thehttpMethod
decorator to be at the top personally.metadata
from a method parameter decorator, because these are evaluated before the method decorator and thus nometadata
exists at the time.Expected Behavior / Possible Solution
Maybe the controller method
metadata
should be an object indexed by key instead of an array (just like the method parameters metadata created by the parameter decorators). And more importantly thehttpMethod
decorator should merge with any existingmetadata
for that method.Current Behavior
Covered in the summary. Currently you need to jump through a few loopholes to extend the metadata when using method decorators, and it's virtually impossible from the method param decorators.
Context
What I'm trying to accomplish is to validate request payloads using
class-validator
. Request payloads can be either params, body or query. The way I'm validating is by unshifting a middleware that checks the payload against a class definition and replaces the payload on the request object with the class instance or executesnext
with the errors found.Here is some example code and usage:
This works but it feels like repetition. Theoretically I could only need the param injection decorator to also include the validation middleware, similarly to how NestJS does it.
The text was updated successfully, but these errors were encountered: