Description
I'm currently on routingcontrollers v 0.7.6, using Typescript 2.7.2. This issue may be better suited as a class-transformer
bug, please advise if so.
Summary
When using QueryParams
with a parameter that should be an array, but has only one value, the type of that parameter becomes the single value. E.g., type string[]
changes to type string
.
Initial Attempt: QueryParam
I originally had a controller that accepted an array of string values similar to QueryParam with array. When you specify more than one value in the query string, it works. When you only specify one, it breaks.
Controller example:
@Get('/route')
public async myController(@QueryParam('value') values: string[]): Promise<ReturnModel> {
}
working url example: myurl/route?value=abc&value=def
not working url example: myurl/route?value=abc
Next Attempt: QueryParams
My next step was to implement the fix I found in issue 320, which involves defining a type and deconstructing all query params.
New Controller example:
@Get('/route')
public async myController(@QueryParams() { value }: SampleModel): Promise<ReturnModel> {
}
SampleModel:
import { Type } from 'class-transformer';
export class SampleModel {
@Type(( ) => String)
public value: string[] = [];
}
The Problem with arrays when using QueryParams
This works, but this is also where my issue is. If I pass in a multiple values, it works as expected. If I pass in a single value, value
becomes a string instead of a string array. I'm trying to type this, since I'm using Typescript, so I tried changing the property type in my model to:
public value: string[] | string = [];
and it works for the single parameter, but class transformer blows up with two or more of the same parameter name.
StackTrace (full paths redacted):
newValue_1.push is not a function
application.js:630
at ...\node_modules\class-transformer\TransformOperationExecutor.js:38:37
at Array.forEach (<anonymous>)
at TransformOperationExecutor.transform (...\node_modules\class-transformer\TransformOperationExecutor.js:30:20)
at _loop_1 (...\node_modules\class-transformer\TransformOperationExecutor.js:154:46)
at TransformOperationExecutor.transform (...\node_modules\class-transformer\TransformOperationExecutor.js:178:18)
at ClassTransformer.plainToClass (...\node_modules\class-transformer\ClassTransformer.js:17:26)
at Object.plainToClass (...\src\index.ts:37:29)
at ActionParameterHandler.transformValue (...\node_modules\routing-controllers\ActionParameterHandler.js:142:42)
at ActionParameterHandler.normalizeParamValue (...\node_modules\routing-controllers\ActionParameterHandler.js:113:35)
at ActionParameterHandler.handle (...\node_modules\routing-controllers\ActionParameterHandler.js:36:27)
Preferred Results, in order of preference:
- Allow what the OP tried in Issue 320-- this is the way it works in some other model binding systems, such as ASP.NET.
- If I'm using my second approach posted here, and I there's only one query parameter for something that should be an array (my
SampleModel.value
property), place that single value into an array. - Allow for multi-types so I can correctly type my model's properties in my apps.
Workaround
The workaround isn't horrible, but it would be nice to have the underlying frameworks updated so we don't have to repeat the workarounds in numerous clients.
My workaround:
// if only one value is passed in, the type will be a string. convert to an array for consistency in handling.
const values: string[] = Array.isArray(value) ? value: [value] || [ ];