Skip to content

Commit 18ee8d8

Browse files
authored
Merge branch 'next' into fix-double-exec
2 parents 17fbbe9 + 98e46b9 commit 18ee8d8

34 files changed

+594
-133
lines changed

README.md

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ You can use routing-controllers with [express.js][1] or [koa.js][2].
4242
- [Throw HTTP errors](#throw-http-errors)
4343
- [Enable CORS](#enable-cors)
4444
- [Default settings](#default-settings)
45+
- [Selectively disabling request/response transform](#selectively-disable-requestresponse-transforming)
4546
* [Using middlewares](#using-middlewares)
4647
+ [Use exist middleware](#use-exist-middleware)
4748
+ [Creating your own express middleware](#creating-your-own-express-middleware)
@@ -262,10 +263,19 @@ import {Controller, Req, Res, Get} from "routing-controllers";
262263
export class UserController {
263264
264265
@Get("/users")
265-
getAll(@Req() request: any, @Res() response: any) {
266+
getAllUsers(@Req() request: any, @Res() response: any) {
266267
return response.send("Hello response!");
267268
}
268269
270+
@Get("/posts")
271+
getAllPosts(@Req() request: any, @Res() response: any) {
272+
// some response functions don't return the response object,
273+
// so it needs to be returned explicitly
274+
response.redirect("/users");
275+
276+
return response;
277+
}
278+
269279
}
270280
```
271281

@@ -733,7 +743,7 @@ There are set of prepared errors you can use:
733743
* UnauthorizedError
734744

735745

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

739749
```typescript
@@ -755,7 +765,7 @@ class DbError extends HttpError {
755765
}
756766
}
757767
}
758-
```
768+
```
759769

760770
#### Enable CORS
761771

@@ -796,7 +806,7 @@ app.listen(3000);
796806

797807
#### Default settings
798808

799-
You can override default status code in routing-controllers options.
809+
You can override default status code in routing-controllers options.
800810

801811
```typescript
802812
import "reflect-metadata";
@@ -809,9 +819,9 @@ const app = createExpressServer({
809819
//with this option, null will return 404 by default
810820
nullResultCode: 404,
811821

812-
//with this option, void or Promise<void> will return 204 by default
822+
//with this option, void or Promise<void> will return 204 by default
813823
undefinedResultCode: 204,
814-
824+
815825
paramOptions: {
816826
//with this option, argument will be required by default
817827
required: true
@@ -822,6 +832,20 @@ const app = createExpressServer({
822832
app.listen(3000);
823833
```
824834

835+
#### Selectively disable request/response transform
836+
837+
To disable `class-transformer` on a per-controller or per-route basis, use the `transformRequest` and `transformResponse` options on your controller and route decorators:
838+
839+
```typescript
840+
@Controller("/users", {transformRequest: false, transformResponse: false})
841+
export class UserController {
842+
843+
@Get("/", {transformResponse: true}) {
844+
// route option overrides controller option
845+
}
846+
}
847+
```
848+
825849
## Using middlewares
826850

827851
You can use any existing express / koa middleware, or create your own.
@@ -1210,7 +1234,7 @@ If its a class - then instance of this class will be created.
12101234
This technique works with `@Body`, `@Param`, `@QueryParam`, `@BodyParam`, and other decorators.
12111235
Learn more about class-transformer and how to handle more complex object constructions [here][4].
12121236
This behaviour is enabled by default.
1213-
If you want to disable it simply pass `classTransformer: false` to createExpressServer method.
1237+
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).
12141238

12151239
## Auto validating action params
12161240

@@ -1264,7 +1288,7 @@ export class UserController {
12641288
}
12651289
```
12661290
If the param doesn't satisfy the requirements defined by class-validator decorators,
1267-
an error will be thrown and captured by routing-controller, so the client will receive 400 Bad Request and JSON with nice detailed [Validation errors](https://github.com/pleerock/class-validator#validation-errors) array.
1291+
an error will be thrown and captured by routing-controller, so the client will receive 400 Bad Request and JSON with nice detailed [Validation errors](https://github.com/typestack/class-validator#validation-errors) array.
12681292
12691293
If you need special options for validation (groups, skipping missing properties, etc.) or transforming (groups, excluding prefixes, versions, etc.), you can pass them as global config as `validation ` in createExpressServer method or as a local `validate` setting for method parameter - `@Body({ validate: localOptions })`.
12701294
@@ -1486,7 +1510,8 @@ export class QuestionController {
14861510
| `@Patch(route: string\|RegExp)` | `@Patch("/users/:id") patch()` | Methods marked with this decorator will register a request made with PATCH HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.patch("/users/:id", patch)` |
14871511
| `@Delete(route: string\|RegExp)` | `@Delete("/users/:id") delete()` | Methods marked with this decorator will register a request made with DELETE HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.delete("/users/:id", delete)` |
14881512
| `@Head(route: string\|RegExp)` | `@Head("/users/:id") head()` | Methods marked with this decorator will register a request made with HEAD HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.head("/users/:id", head)` |
1489-
| `@Method(methodName: string, route: string\|RegExp)` | `@Method("move", "/users/:id") move()` | Methods marked with this decorator will register a request made with given `methodName` HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.move("/users/:id", move)` |
1513+
| `@All(route: string\|RegExp)` | `@All("/users/me") rewrite()` | Methods marked with this decorator will register a request made with any HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.all("/users/me", rewrite)` |
1514+
| `@Method(methodName: string, route: string\|RegExp)` | `@Method("move", "/users/:id") move()` | Methods marked with this decorator will register a request made with given `methodName` HTTP Method to a given route. In action options you can specify if action should response json or regular text response. | `app.move("/users/:id", move)` |
14901515

14911516
#### Method Parameter Decorators
14921517

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import "reflect-metadata";
2+
import * as express from "express";
3+
import {useExpressServer} from "../../src/index";
4+
5+
let app = express(); // create express server
6+
useExpressServer(app, {
7+
controllers: [__dirname + "/controllers/*{.js,.ts}"] // register controllers routes in our express app
8+
});
9+
app.listen(3001); // run express app
10+
11+
console.log("Express server is running on port 3001. Open http://localhost:3001/blogs/ or http://localhost:3002/posts/");
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {Request} from "express";
2+
import {Get} from "../../../src/decorator/Get";
3+
import {Req} from "../../../src/index";
4+
import {Post} from "../../../src/decorator/Post";
5+
import {Put} from "../../../src/decorator/Put";
6+
import {Patch} from "../../../src/decorator/Patch";
7+
import {Delete} from "../../../src/decorator/Delete";
8+
9+
export class BaseControllerClass {
10+
name: string;
11+
constructor(name: string) {
12+
this.name = name;
13+
}
14+
15+
@Get()
16+
getAll() {
17+
return [
18+
{id: 1, name: `First ${this.name}!`},
19+
{id: 2, name: `Second ${this.name}!`}
20+
];
21+
}
22+
23+
@Get("/:id")
24+
getOne() {
25+
return {id: 1, name: `First ${this.name}!`};
26+
}
27+
28+
@Post("")
29+
post(@Req() request: Request) {
30+
let entity = JSON.stringify(request.body);
31+
return `${this.name} ${entity} !saved!`;
32+
}
33+
34+
@Put("/:id")
35+
put(@Req() request: Request) {
36+
return `${this.name} # ${request.params.id} has been putted!`;
37+
}
38+
39+
@Patch("/:id")
40+
patch(@Req() request: Request) {
41+
return `${this.name} # ${request.params.id} has been patched!`;
42+
}
43+
44+
@Delete("/:id")
45+
remove(@Req() request: Request) {
46+
return `${this.name} # ${request.params.id} has been removed!`;
47+
}
48+
}
49+
50+
export function BaseController(name: string): { new(): BaseControllerClass } {
51+
return class extends BaseControllerClass {
52+
constructor() {
53+
super(name);
54+
}
55+
};
56+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {JsonController} from "../../../src/decorator/JsonController";
2+
import {BaseController, BaseControllerClass} from "./BaseController";
3+
import {Get} from "../../../src/decorator/Get";
4+
5+
@JsonController("/blogs")
6+
export class BlogController extends BaseController("blog") {
7+
constructor() {
8+
super();
9+
}
10+
11+
@Get()
12+
getAll() {
13+
return [
14+
{id: 1, name: `First ${this.name}!`},
15+
{id: 2, name: `Second ${this.name}!`},
16+
{id: 3, name: `Third ${this.name}!`}
17+
];
18+
}
19+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {JsonController} from "../../../src/decorator/JsonController";
2+
import {BaseController, BaseControllerClass} from "./BaseController";
3+
4+
@JsonController("/posts")
5+
export class PostController extends BaseController("post") {}

src/ActionParameterHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ export class ActionParameterHandler<T extends BaseDriver> {
209209
*/
210210
protected transformValue(value: any, paramMetadata: ParamMetadata): any {
211211
if (this.driver.useClassTransformer &&
212+
paramMetadata.actionMetadata.options.transformRequest !== false &&
212213
paramMetadata.targetType &&
213214
paramMetadata.targetType !== Object &&
214215
!(value instanceof paramMetadata.targetType)) {

src/RoutingControllersOptions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export interface RoutingControllersOptions {
8585
* Special function used to get currently authorized user.
8686
*/
8787
currentUserChecker?: CurrentUserChecker;
88-
88+
8989
/**
9090
* Default settings
9191
*/
@@ -110,4 +110,4 @@ export interface RoutingControllersOptions {
110110
required?: boolean;
111111
};
112112
};
113-
}
113+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Extra options that apply to each controller action.
3+
*/
4+
export interface ControllerOptions {
5+
/**
6+
* If set to false, class-transformer won't be used to perform request serialization.
7+
*/
8+
transformRequest?: boolean;
9+
10+
/**
11+
* If set to false, class-transformer won't be used to perform response serialization.
12+
*/
13+
transformResponse?: boolean;
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Extra handler-specific options.
3+
*/
4+
export interface HandlerOptions {
5+
/**
6+
* If set to false, class-transformer won't be used to perform request serialization.
7+
*/
8+
transformRequest?: boolean;
9+
10+
/**
11+
* If set to false, class-transformer won't be used to perform response serialization.
12+
*/
13+
transformResponse?: boolean;
14+
}

src/decorator/All.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {getMetadataArgsStorage} from "../index";
2+
import { ControllerOptions } from "../decorator-options/ControllerOptions";
3+
4+
/**
5+
* Registers an action to be executed when a request comes on a given route.
6+
* Must be applied on a controller action.
7+
*/
8+
export function All(route?: RegExp): Function;
9+
10+
/**
11+
* Registers an action to be executed when a request comes on a given route.
12+
* Must be applied on a controller action.
13+
*/
14+
export function All(route?: string): Function;
15+
16+
/**
17+
* Registers an action to be executed when a request comes on a given route.
18+
* Must be applied on a controller action.
19+
*/
20+
export function All(route?: string|RegExp, options?: ControllerOptions): Function {
21+
return function (object: Object, methodName: string) {
22+
getMetadataArgsStorage().actions.push({
23+
type: "all",
24+
target: object.constructor,
25+
method: methodName,
26+
route: route,
27+
options,
28+
});
29+
};
30+
}

0 commit comments

Comments
 (0)