Skip to content

Params fixes and features #289

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 15 commits into from
Sep 18, 2017
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog and release notes

### 0.8.0

- rename `@Param` and `@Params` decorators to `@PathParam` and `@PathParams` (#289)
- restore/introduce `@QueryParams()` and `@PathParams()` missing decorators options (they are needed for validation purposes) (#289)
- normalize param object properties (for "queries", "headers", "params" and "cookies") - now you can easily validate query/path params using `class-validator` (#289)
- enhance params normalization - converting from string to primitive types is now more strict and can throw ParamNormalizationError,
e.g. when number is expected but the invalid string (NaN) has been received (#289)

### 0.7.2

- FIXED: Using `@Authorization` decorator with Koa caused 404 responses (ref [#240](https://github.com/pleerock/routing-controllers/pull/240))
Expand Down
79 changes: 55 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ You can use routing-controllers with [express.js][1] or [koa.js][2].
}

@Get("/users/:id")
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
return "This action returns user #" + id;
}

Expand All @@ -138,12 +138,12 @@ You can use routing-controllers with [express.js][1] or [koa.js][2].
}

@Put("/users/:id")
put(@Param("id") id: number, @Body() user: any) {
put(@PathParam("id") id: number, @Body() user: any) {
return "Updating a user...";
}

@Delete("/users/:id")
remove(@Param("id") id: number) {
remove(@PathParam("id") id: number) {
return "Removing user...";
}

Expand Down Expand Up @@ -195,7 +195,7 @@ export class UserController {
}

@Get("/users/:id")
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
return userRepository.findById(id);
}

Expand All @@ -222,7 +222,7 @@ export class UserController {
}

@Get("/users/:id")
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
return userRepository.findById(id);
}

Expand All @@ -232,12 +232,12 @@ export class UserController {
}

@Put("/users/:id")
put(@Param("id") id: number, @Body() user: User) {
put(@PathParam("id") id: number, @Body() user: User) {
return userRepository.updateById(id, user);
}

@Delete("/users/:id")
remove(@Param("id") id: number) {
remove(@PathParam("id") id: number) {
return userRepository.removeById(id);
}

Expand Down Expand Up @@ -346,15 +346,15 @@ export class UserController {

#### Inject routing parameters

You can use `@Param` decorator to inject parameters in your controller actions:
You can use `@PathParam` decorator to inject parameters in your controller actions:

```typescript
@Get("/users/:id")
getOne(@Param("id") id: number) { // id will be automatically casted to "number" because it has type number
getOne(@PathParam("id") id: number) { // id will be automatically casted to "number" because it has type number
}
```

If you want to inject all parameters use `@Params()` decorator.
If you want to inject all parameters use `@PathParams()` decorator.

#### Inject query parameters

Expand All @@ -367,6 +367,37 @@ getUsers(@QueryParam("limit") limit: number) {
```

If you want to inject all query parameters use `@QueryParams()` decorator.
The bigest benefit of this approach is that you can perform validation of the params.

```typescript
enum Roles {
Admin = "admin",
User = "user",
Guest = "guest",
}

class GetUsersQuery {

@IsPositive()
limit: number;

@IsAlpha()
city: string;

@IsEnum(Roles)
role: Roles;

@IsBoolean()
isActive: boolean;

}

@Get("/users")
getUsers(@QueryParams() query: GetUsersQuery) {
// here you can access query.role, query.limit
// and others valid query parameters
}
```

#### Inject request body

Expand Down Expand Up @@ -584,7 +615,7 @@ To prevent this if you need to specify what status code you want to return using
```typescript
@Delete("/users/:id")
@OnUndefined(204)
async remove(@Param("id") id: number): Promise<void> {
async remove(@PathParam("id") id: number): Promise<void> {
return userRepository.removeById(id);
}
```
Expand All @@ -596,7 +627,7 @@ This action will return 404 in the case if user was not found, and regular 200 i
```typescript
@Get("/users/:id")
@OnUndefined(404)
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
return userRepository.findOneById(id);
}
```
Expand All @@ -616,7 +647,7 @@ export class UserNotFoundError extends HttpError {
```typescript
@Get("/users/:id")
@OnUndefined(UserNotFoundError)
saveUser(@Param("id") id: number) {
saveUser(@PathParam("id") id: number) {
return userRepository.findOneById(id);
}
```
Expand All @@ -630,7 +661,7 @@ You can set any custom header in a response:
```typescript
@Get("/users/:id")
@Header("Cache-Control", "none")
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
// ...
}
```
Expand Down Expand Up @@ -660,7 +691,7 @@ If you want to return errors with specific error codes, there is an easy way:

```typescript
@Get("/users/:id")
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {

const user = this.userRepository.findOneById(id);
if (!user)
Expand Down Expand Up @@ -779,7 +810,7 @@ For example, lets try to use [compression](https://github.com/expressjs/compress

@Get("/users/:id")
@UseBefore(compression())
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
// ...
}
```
Expand Down Expand Up @@ -871,7 +902,7 @@ Here is example of creating middleware for express.js:
@Get("/users/:id")
@UseBefore(MyMiddleware)
@UseAfter(loggingMiddleware)
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
// ...
}
```
Expand Down Expand Up @@ -938,7 +969,7 @@ Here is example of creating middleware for koa.js:
@Get("/users/:id")
@UseBefore(MyMiddleware)
@UseAfter(loggingMiddleware)
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
// ...
}
```
Expand Down Expand Up @@ -1044,7 +1075,7 @@ import {Get, Param, UseInterceptor} from "routing-controllers";
// in it and return a replaced result. replaced result will be returned to the user
return content.replace(/Mike/gi, "Michael");
})
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
return "Hello, I am Mike!"; // client will get a "Hello, I am Michael!" response.
}
```
Expand Down Expand Up @@ -1078,7 +1109,7 @@ import {NameCorrectionInterceptor} from "./NameCorrectionInterceptor";

@Get("/users")
@UseInterceptor(NameCorrectionInterceptor)
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
return "Hello, I am Mike!"; // client will get a "Hello, I am Michael!" response.
}
```
Expand Down Expand Up @@ -1142,7 +1173,7 @@ export class UserController {
If `User` is an interface - then simple literal object will be created.
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.
This technique works not only with `@Body`, but also with `@PathParam`, `@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.
Expand Down Expand Up @@ -1203,7 +1234,7 @@ an error will be thrown and captured by routing-controller, so the client will r

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 })`.

This technique works not only with `@Body` but also with `@Param`, `@QueryParam`, `@BodyParam` and other decorators.
This technique works not only with `@Body` but also with `@PathParam`, `@QueryParam`, `@BodyParam` and other decorators.

## Using authorization features

Expand Down Expand Up @@ -1397,8 +1428,8 @@ export class QuestionController {
| `@Req()` | `getAll(@Req() request: Request)` | Injects a Request object. | `function (request, response)` |
| `@Res()` | `getAll(@Res() response: Response)` | Injects a Response object. | `function (request, response)` |
| `@Ctx()` | `getAll(@Ctx() context: Context)` | Injects a Context object (koa-specific) | `function (ctx)` (koa-analogue) |
| `@Param(name: string, options?: ParamOptions)` | `get(@Param("id") id: number)` | Injects a router parameter. | `request.params.id` |
| `@Params()` | `get(@Params() params: any)` | Injects all request parameters. | `request.params` |
| `@PathParam(name: string, options?: ParamOptions)` | `get(@PathParam("id") id: number)` | Injects a router parameter. | `request.params.id` |
| `@PathParams()` | `get(@PathParams() params: any)` | Injects all request parameters. | `request.params` |
| `@QueryParam(name: string, options?: ParamOptions)` | `get(@QueryParam("id") id: number)` | Injects a query string parameter. | `request.query.id` |
| `@QueryParams()` | `get(@QueryParams() params: any)` | Injects all query parameters. | `request.query` |
| `@HeaderParam(name: string, options?: ParamOptions)` | `get(@HeaderParam("token") token: string)` | Injects a specific request headers. | `request.headers.token` |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Request} from "express";
import {JsonController} from "../../../../../src/decorator/JsonController";
import {Get} from "../../../../../src/decorator/Get";
import {Param} from "../../../../../src/decorator/Param";
import {PathParam} from "../../../../../src/decorator/PathParam";
import {Post} from "../../../../../src/decorator/Post";
import {Req} from "../../../../../src/decorator/Req";
import {Put} from "../../../../../src/decorator/Put";
Expand All @@ -20,7 +20,7 @@ export class QuestionController {
}

@Get("/questions/:id")
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
if (!id)
return Promise.reject(new Error("No id is specified"));

Expand Down
4 changes: 2 additions & 2 deletions sample/sample12-session-support/UserController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {Post} from "../../src/decorator/Post";
import {Put} from "../../src/decorator/Put";
import {Patch} from "../../src/decorator/Patch";
import {Delete} from "../../src/decorator/Delete";
import {Param} from "../../src/decorator/Param";
import {PathParam} from "../../src/decorator/PathParam";
import {Session} from "../../src/decorator/Session";
import {ContentType} from "../../src/decorator/ContentType";

Expand Down Expand Up @@ -36,7 +36,7 @@ export class UserController {
}

@Put("/users/:id")
put(@Param("id") id: number, @Session() session: Express.Session) {
put(@PathParam("id") id: number, @Session() session: Express.Session) {
(session as any).user = { name: "test", number: id };
return "User has been putted!";
}
Expand Down
10 changes: 5 additions & 5 deletions sample/sample4-extra-parameters/BlogController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Put} from "../../src/decorator/Put";
import {Patch} from "../../src/decorator/Patch";
import {Delete} from "../../src/decorator/Delete";
import {QueryParam} from "../../src/decorator/QueryParam";
import {Param} from "../../src/decorator/Param";
import {PathParam} from "../../src/decorator/PathParam";
import {Body} from "../../src/decorator/Body";

export interface BlogFilter {
Expand All @@ -26,7 +26,7 @@ export class BlogController {
}

@Get("/blogs/:id")
getOne(@Param("id") id: number, @QueryParam("name") name: string) {
getOne(@PathParam("id") id: number, @QueryParam("name") name: string) {
return { id: id, name: name };
}

Expand All @@ -36,17 +36,17 @@ export class BlogController {
}

@Put("/blogs/:id")
put(@Param("id") id: number) {
put(@PathParam("id") id: number) {
return "Blog #" + id + " has been putted!";
}

@Patch("/blogs/:id")
patch(@Param("id") id: number) {
patch(@PathParam("id") id: number) {
return "Blog #" + id + " has been patched!";
}

@Delete("/blogs/:id")
remove(@Param("id") id: number) {
remove(@PathParam("id") id: number) {
return "Blog #" + id + " has been removed!";
}

Expand Down
4 changes: 2 additions & 2 deletions sample/sample6-global-middlewares/BlogController.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {ForbiddenError} from "../../src/http-error/ForbiddenError";
import {Controller} from "../../src/decorator/Controller";
import {Get} from "../../src/decorator/Get";
import {Param} from "../../src/decorator/Param";
import {PathParam} from "../../src/decorator/PathParam";
import {ContentType} from "../../src/decorator/ContentType";

@Controller()
Expand All @@ -19,7 +19,7 @@ export class BlogController {

@Get("/blogs/:id")
@ContentType("application/json")
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
if (!id)
throw new ForbiddenError();

Expand Down
4 changes: 2 additions & 2 deletions sample/sample9-use-and-middlewares/BlogController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {JsonController} from "../../src/decorator/JsonController";
import {Get} from "../../src/decorator/Get";
import {Param} from "../../src/decorator/Param";
import {PathParam} from "../../src/decorator/PathParam";
import {CompressionMiddleware} from "./CompressionMiddleware";
import {AllControllerActionsMiddleware} from "./AllControllerActionsMiddleware";
import {UseBefore} from "../../src/decorator/UseBefore";
Expand All @@ -24,7 +24,7 @@ export class BlogController {
}

@Get("/blogs/:id")
getOne(@Param("id") id: number) {
getOne(@PathParam("id") id: number) {
return { id: id, firstName: "First", secondName: "blog" };
}

Expand Down
Loading