Skip to content

feat: add a useResponseClassTransformer global option #329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ You can use routing-controllers with [express.js][1] or [koa.js][2].
- [Throw HTTP errors](#throw-http-errors)
- [Enable CORS](#enable-cors)
- [Default settings](#default-settings)
- [Selectively disabling request/response transform](#selectively-disable-requestresponse-transforming)
* [Using middlewares](#using-middlewares)
+ [Use exist middleware](#use-exist-middleware)
+ [Creating your own express middleware](#creating-your-own-express-middleware)
Expand Down Expand Up @@ -694,7 +695,7 @@ There are set of prepared errors you can use:
* UnauthorizedError


You can also create and use your own errors by extending `HttpError` class.
You can also create and use your own errors by extending `HttpError` class.
To define the data returned to the client, you could define a toJSON method in your error.

```typescript
Expand All @@ -716,7 +717,7 @@ class DbError extends HttpError {
}
}
}
```
```

#### Enable CORS

Expand Down Expand Up @@ -757,7 +758,7 @@ app.listen(3000);

#### Default settings

You can override default status code in routing-controllers options.
You can override default status code in routing-controllers options.

```typescript
import "reflect-metadata";
Expand All @@ -770,9 +771,9 @@ const app = createExpressServer({
//with this option, null will return 404 by default
nullResultCode: 404,

//with this option, void or Promise<void> will return 204 by default
//with this option, void or Promise<void> will return 204 by default
undefinedResultCode: 204,

paramOptions: {
//with this option, argument will be required by default
required: true
Expand All @@ -783,6 +784,20 @@ const app = createExpressServer({
app.listen(3000);
```

#### Selectively disable request/response transform

To disable `class-transformer` on a per-controller or per-route basis, use the `transformRequest` and `transformResponse` options on your controller and route decorators:

```typescript
@Controller("/users", {transformRequest: false, transformResponse: false})
export class UserController {

@Get("/", {transformResponse: true}) {
// route option overrides controller option
}
}
```

## Using middlewares

You can use any exist express / koa middleware, or create your own.
Expand Down Expand Up @@ -1171,7 +1186,7 @@ If its a class - then instance of this class will be created.
This technique works not only with `@Body`, but also with `@Param`, `@QueryParam`, `@BodyParam` and other decorators.
Learn more about class-transformer and how to handle more complex object constructions [here][4].
This behaviour is enabled by default.
If you want to disable it simply pass `classTransformer: false` to createExpressServer method.
If you want to disable it simply pass `classTransformer: false` to createExpressServer method. Alternatively you can disable transforming for [individual controllers or routes](#selectively-disable-requestresponse-transforming).

## Auto validating action params

Expand Down
1 change: 1 addition & 0 deletions src/ActionParameterHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export class ActionParameterHandler<T extends BaseDriver> {
*/
protected transformValue(value: any, paramMetadata: ParamMetadata): any {
if (this.driver.useClassTransformer &&
paramMetadata.actionMetadata.options.transformRequest !== false &&
paramMetadata.targetType &&
paramMetadata.targetType !== Object &&
!(value instanceof paramMetadata.targetType)) {
Expand Down
4 changes: 2 additions & 2 deletions src/RoutingControllersOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export interface RoutingControllersOptions {
* Special function used to get currently authorized user.
*/
currentUserChecker?: CurrentUserChecker;

/**
* Default settings
*/
Expand All @@ -110,4 +110,4 @@ export interface RoutingControllersOptions {
required?: boolean;
};
};
}
}
14 changes: 14 additions & 0 deletions src/decorator-options/ControllerOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Extra options that apply to each controller action.
*/
export interface ControllerOptions {
/**
* If set to false, class-transformer won't be used to perform request serialization.
*/
transformRequest?: boolean;

/**
* If set to false, class-transformer won't be used to perform response serialization.
*/
transformResponse?: boolean;
}
14 changes: 14 additions & 0 deletions src/decorator-options/HandlerOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Extra handler-specific options.
*/
export interface HandlerOptions {
/**
* If set to false, class-transformer won't be used to perform request serialization.
*/
transformRequest?: boolean;

/**
* If set to false, class-transformer won't be used to perform response serialization.
*/
transformResponse?: boolean;
}
9 changes: 6 additions & 3 deletions src/decorator/Controller.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import {getMetadataArgsStorage} from "../index";
import {ControllerOptions} from "../decorator-options/ControllerOptions";

/**
* Defines a class as a controller.
* Each decorated controller method is served as a controller action.
* Controller actions are executed when request come.
*
* @param baseRoute Extra path you can apply as a base route to all controller actions
* @param options Extra options that apply to all controller actions
*/
export function Controller(baseRoute?: string): Function {
export function Controller(baseRoute?: string, options?: ControllerOptions): Function {
return function (object: Function) {
getMetadataArgsStorage().controllers.push({
type: "default",
target: object,
route: baseRoute
route: baseRoute,
options
});
};
}
}
10 changes: 6 additions & 4 deletions src/decorator/Delete.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import {HandlerOptions} from "../decorator-options/HandlerOptions";
import {getMetadataArgsStorage} from "../index";

/**
* Registers a controller method to be executed when DELETE request comes on a given route.
* Must be applied on a controller action.
*/
export function Delete(route?: RegExp): Function;
export function Delete(route?: RegExp, options?: HandlerOptions): Function;

/**
* Registers a controller method to be executed when DELETE request comes on a given route.
* Must be applied on a controller action.
*/
export function Delete(route?: string): Function;
export function Delete(route?: string, options?: HandlerOptions): Function;

/**
* Registers a controller method to be executed when DELETE request comes on a given route.
* Must be applied on a controller action.
*/
export function Delete(route?: string|RegExp): Function {
export function Delete(route?: string|RegExp, options?: HandlerOptions): Function {
return function (object: Object, methodName: string) {
getMetadataArgsStorage().actions.push({
type: "delete",
target: object.constructor,
method: methodName,
route: route
route: route,
options
});
};
}
12 changes: 7 additions & 5 deletions src/decorator/Get.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import {HandlerOptions} from "../decorator-options/HandlerOptions";
import {getMetadataArgsStorage} from "../index";

/**
* Registers an action to be executed when GET request comes on a given route.
* Must be applied on a controller action.
*/
export function Get(route?: RegExp): Function;
export function Get(route?: RegExp, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when GET request comes on a given route.
* Must be applied on a controller action.
*/
export function Get(route?: string): Function;
export function Get(route?: string, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when GET request comes on a given route.
* Must be applied on a controller action.
*/
export function Get(route?: string|RegExp): Function {
export function Get(route?: string|RegExp, options?: HandlerOptions): Function {
return function (object: Object, methodName: string) {
getMetadataArgsStorage().actions.push({
type: "get",
target: object.constructor,
method: methodName,
route: route
options,
route
});
};
}
}
12 changes: 7 additions & 5 deletions src/decorator/Head.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import {HandlerOptions} from "../decorator-options/HandlerOptions";
import {getMetadataArgsStorage} from "../index";

/**
* Registers an action to be executed when HEAD request comes on a given route.
* Must be applied on a controller action.
*/
export function Head(route?: RegExp): Function;
export function Head(route?: RegExp, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when HEAD request comes on a given route.
* Must be applied on a controller action.
*/
export function Head(route?: string): Function;
export function Head(route?: string, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when HEAD request comes on a given route.
* Must be applied on a controller action.
*/
export function Head(route?: string|RegExp): Function {
export function Head(route?: string|RegExp, options?: HandlerOptions): Function {
return function (object: Object, methodName: string) {
getMetadataArgsStorage().actions.push({
type: "head",
target: object.constructor,
method: methodName,
route: route
options,
route
});
};
}
}
7 changes: 5 additions & 2 deletions src/decorator/JsonController.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import {getMetadataArgsStorage} from "../index";
import {ControllerOptions} from "../decorator-options/ControllerOptions";

/**
* Defines a class as a JSON controller. If JSON controller is used, then all controller actions will return
* a serialized json data, and its response content-type always will be application/json.
*
* @param baseRoute Extra path you can apply as a base route to all controller actions
* @param options Extra options that apply to all controller actions
*/
export function JsonController(baseRoute?: string) {
export function JsonController(baseRoute?: string, options?: ControllerOptions) {
return function (object: Function) {
getMetadataArgsStorage().controllers.push({
type: "json",
target: object,
route: baseRoute
route: baseRoute,
options
});
};
}
10 changes: 6 additions & 4 deletions src/decorator/Method.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import {HandlerOptions} from "../decorator-options/HandlerOptions";
import {getMetadataArgsStorage} from "../index";
import {ActionType} from "../metadata/types/ActionType";

/**
* Registers an action to be executed when request with specified method comes on a given route.
* Must be applied on a controller action.
*/
export function Method(method: ActionType, route?: RegExp): Function;
export function Method(method: ActionType, route?: RegExp, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when request with specified method comes on a given route.
* Must be applied on a controller action.
*/
export function Method(method: ActionType, route?: string): Function;
export function Method(method: ActionType, route?: string, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when request with specified method comes on a given route.
* Must be applied on a controller action.
*/
export function Method(method: ActionType, route?: string|RegExp): Function {
export function Method(method: ActionType, route?: string|RegExp, options?: HandlerOptions): Function {
return function (object: Object, methodName: string) {
getMetadataArgsStorage().actions.push({
type: method,
target: object.constructor,
method: methodName,
route: route
options,
route
});
};
}
12 changes: 7 additions & 5 deletions src/decorator/Patch.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import {HandlerOptions} from "../decorator-options/HandlerOptions";
import {getMetadataArgsStorage} from "../index";

/**
* Registers an action to be executed when PATCH request comes on a given route.
* Must be applied on a controller action.
*/
export function Patch(route?: RegExp): Function;
export function Patch(route?: RegExp, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when PATCH request comes on a given route.
* Must be applied on a controller action.
*/
export function Patch(route?: string): Function;
export function Patch(route?: string, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when PATCH request comes on a given route.
* Must be applied on a controller action.
*/
export function Patch(route?: string|RegExp): Function {
export function Patch(route?: string|RegExp, options?: HandlerOptions): Function {
return function (object: Object, methodName: string) {
getMetadataArgsStorage().actions.push({
type: "patch",
target: object.constructor,
method: methodName,
route: route
route: route,
options
});
};
}
}
10 changes: 6 additions & 4 deletions src/decorator/Post.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import {HandlerOptions} from "../decorator-options/HandlerOptions";
import {getMetadataArgsStorage} from "../index";

/**
* Registers an action to be executed when POST request comes on a given route.
* Must be applied on a controller action.
*/
export function Post(route?: RegExp): Function;
export function Post(route?: RegExp, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when POST request comes on a given route.
* Must be applied on a controller action.
*/
export function Post(route?: string): Function;
export function Post(route?: string, options?: HandlerOptions): Function;

/**
* Registers an action to be executed when POST request comes on a given route.
* Must be applied on a controller action.
*/
export function Post(route?: string|RegExp): Function {
export function Post(route?: string|RegExp, options?: HandlerOptions): Function {
return function (object: Object, methodName: string) {
getMetadataArgsStorage().actions.push({
type: "post",
target: object.constructor,
method: methodName,
route: route
options,
route
});
};
}
Loading