Skip to content

Commit

Permalink
docs: Update documentation about controllers, interceptors and middle…
Browse files Browse the repository at this point in the history
…wares
  • Loading branch information
Romakita committed Jun 13, 2019
1 parent c75acc1 commit a4777b4
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 62 deletions.
1 change: 1 addition & 0 deletions docs/assets/middleware-call-sequence.drawio
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<mxfile modified="2019-06-13T06:42:52.808Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:67.0) Gecko/20100101 Firefox/67.0" etag="1uh4_nf9ed0mNrsc8kY8" version="10.7.7" type="device"><diagram id="DG_1D_lNAm6PYI5epRoH" name="Page-1">7ZtRj+I2EIB/DVL7sFUShyQ8Lix3W7VVJVZV754qk0xI7kzMOWaB/fV1iA1JHSA9hfgOjpVW8YztxDP+PM4YBmiy3L5neJX8QSMgA8eKtgP0NHAc3xuK/4VgVwpcNygFC5ZGpcg+Cl7SN5BCS0rXaQR5rSKnlPB0VReGNMsg5DUZZoxu6tViSup3XeEFaIKXEBNd+nca8aSUBo5/lD9DukjUnW1vVGqWWFWWI8kTHNFNRYSmAzRhlPLyarmdAClsp+xStnt3Qnt4MAYZb9OAPYSPT7s3dzl7/u3X3eztz+2Hfx6QHMcrJms5Yvm0fKdMwOg6i6DoxRqg8SZJObyscFhoN8LnQpbwJRElW1zmnNHPMKGEsn1rNPJ8hD2hiVNCKnKwoyH4Qi4fABiH7cmh2QeDiYkGdAmc7UQV2cCxpI3lJLN9Wd4cXeaqOknFXUMlxHKaLA59Hy0pLqQx/49hvWsbNnDmyGswrDBrELkdGdauG/YwmSuGRXaDYd2rGdbXzAiRIFYWKeMJXdAMk+lROq4b+ljnd0pX0ryfgPOdXH7wmtO68WGb8g9F81+GsvSxonnayp73hZ0qZGK4lUZF8WNVd2y2L6l2mqMty3ej0cGhxXDPu1NYh65ZCGesqJZSzBbAz9Rzm6cHA4J5+lp/js5dbWsITUhaDNWxZvBlDTk/g5T9FUgdLB3TjL/Dy5QUTnkG8go8DXE3TCG3zpSrI2U7DUh51yIq+EFUB0S5LYnyTBLlakS9J3SOi3bLNIoIbMTI846pivzR3LL0QBXH4IVhz7TZqCVu6Fq42eh74a0XbryW3NhWs5/7AadpN+cR8bzjKH0Vl4vi8q8cxhBTBoNiQpXqOVNaJRH3r7Rp6KZGYnObTvm8uEPvlc/AOJ/u98LnNx0P1VvBZbAdo3tMSyP7gPEUh8lVI+M3RZ5jPjK2eGfuAcV+8HDa4jE0iodzGo+rouGFAczjhuwGhiDue9PomA9KwR2hMWyLhm8UjWETGvcTL5DxeOHosft2ofDbQjEyCoV+njDNohVN91m7BGeCDNYxFRcT4b1SYTxUKAjugopRSypOebEnKkZNoeIx5gUL97KJcs3HCz39ebNkHA7LL5KBTJKhOr5vMszHDM8ECDeX83JQW+iMvtSrx6xANwPhygI5lXHO1yuVbv7J/rmSh64oumZy/9GZjIfFn2SyIi8/PbM6NB/F9M3E7UaxtqkAJzAKlJ4KeBFAzSBf0SyHE1g5P7A6YmU8BCJ9J3K7WAUtsUJGD12dpqRn06mr3C9+/fnq5VPa283jecYjGtLzePf2tRTP/Pp3GrbCEDXLe1/WVCke8v1S9igq2N5qWyIk9QqfiajHKCE1SMteT/AlLMvP+S+jGfzHdVKESbrIRDEUnhD3Q+PCT8Jv5FEqyhl1YsLU1++TM6BQyCXc9rqZEQfPnvkihNswIZxrTQhXp1KlcnUU79BfLY7PO/KXKB5/JrDXVX5rgab/Ag==</diagram></mxfile>
Binary file removed docs/assets/middleware-call-sequence.png
Binary file not shown.
2 changes: 2 additions & 0 deletions docs/assets/middleware-call-sequence.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/assets/middleware.drawio
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<mxfile modified="2019-06-13T09:48:25.934Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:67.0) Gecko/20100101 Firefox/67.0" etag="zNQGLAyjXp85JX7BV83r" version="10.7.7" type="device"><diagram id="-pNohHKRrXRFJF8rd36P" name="Page-1">5VZNc5swEP01nmkPngFkY3yMidtMJzm0PjRXBa1BiWCpEAb311cYiY+QeJJp04/phZHe7rK7b/UEMxKm9UdJ8+QGGYiZ57B6Ri5nnrd2A/1sgGMLLIkBYslZC7k9sOPfwYCOQUvOoBg5KkSheD4GI8wyiNQIo1JiNXbboxhnzWkME2AXUTFFv3KmkhYNvFWPXwGPE5vZ9detJaXW2XRSJJRhNYDIdkZCiajaVVqHIBruLC9t3IdnrF1hEjL1kgD1KaK3F/Po9vMqvNhfX9X7ZD4n7VsOVJSmYVOsOloGgGlCzBalSjDGjIptj24klhmDJo2jd73PNWKuQVeD96DU0UyXlgo1lKhUGGuhJD5AiALlKSdxnNXict1ZLPOas01bX1PUszwYqMBSRnCmeXueqIxBnfHzumnpUw6YgpJHHSdBUMUP4zqoOW9x59ePRC/MVF4xIXcyoVDwplXP+QLfSijUZGL9PBpyq4Qr2OX0REWlRfqz3B9AKqjPsz9lywZYOZj7IDDbqhdX55IMhOU7b8Tv8n9WgPdCBSz+pAK8yYRuOGMCKt3wLz77gXdHfF9b9lyIAc6WELDFW2mC/G2aWDyhCV/orBvGD3oZN8ttxnLkzUXUmnSmgfWJgHfS3lf645jnPIvfW687+Tju8dv+oStu+fvGqbf9D8TJNvgLI9sf</diagram></mxfile>
2 changes: 2 additions & 0 deletions docs/assets/middleware.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions docs/docs/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ For the session, cookies or locals data attached on the request, is the same thi

### Decorators

<ApiList query="['All', 'Get', 'Post', 'Patch', 'Put', 'Head', 'Delete', 'Use', 'UseAfter', 'UseBefore'].indexOf(symbolName) === -1 && path.indexOf('mvc/decorators/method') > -1" />
<ApiList query="['All', 'Get', 'Post', 'Patch', 'Put', 'Head', 'Delete', 'Use', 'UseAfter', 'UseBefore', 'UseBeforeEach'].indexOf(symbolName) === -1 && path.indexOf('mvc/decorators/method') > -1" />

### Status

Expand Down Expand Up @@ -204,7 +204,7 @@ For more details about Middleware declaration see the [Middlewares](/docs/middle

The following decorators lets you add custom middleware on a method or on controller:

<ApiList query="['Use', 'UseBefore', 'UseAfter'].indexOf(symbolName) > -1" />
<ApiList query="['Use', 'UseBefore', 'UseAfter', 'UseBeforeEach'].indexOf(symbolName) > -1" />

#### Example

Expand All @@ -216,10 +216,10 @@ The following decorators lets you add custom middleware on a method or on contro
When a request is sent to the server all middlewares added on the ServerLoader, Controller or Endpoint
will be called while a response isn't sent by one of the middleware in the lifecycle.

<figure><img src="./../assets/middleware-call-sequence.png" style="max-width:400px; padding:30px"></figure>
<figure><img src="./../assets/middleware-call-sequence.svg" style="max-width:400px; padding:30px"></figure>

::: tip
See [middleware call sequence](/docs/middlewares/call-sequence.md) for more information.
See [middlewares section](/docs/middlewares.md) for more information.
:::

### Child controllers
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/custom-endpoint-decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This option will be set to `endpoint.get` seen before with AcceptMimesMiddleware
Ts.ED provide API to create you own decorator like @@AcceptMime@@ which register the options and at least one middleware
with theses decorators and utils:

- @@UseBefore@@ or @@UseAfter@@ for middleware registration,
- @@Use@@, @@UseBeforeEach@@, @@UseBefore@@, or @@UseAfter@@ for middleware registration,
- @@applyDecorator@@ if you want to combine different decorators,
- @@StoreMerge@@ or @@StoreGet@@ to register options.

Expand Down
61 changes: 15 additions & 46 deletions docs/docs/interceptors.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
# Interceptors

Creating and consuming an interceptor is two-step process.
An interceptor is a class annotated with the @@Interceptor@@ decorator. Interceptors should implement the @@IInterceptor@@ interface.

Interceptors have a set of useful capabilities which are inspired by the [Aspect Oriented Programming](https://en.wikipedia.org/wiki/Aspect-oriented_programming) (AOP) technique.

Creating and consuming an interceptor is two-step process:
1. Create and annotate the interceptor class that will intercept calls to service methods
2. Decide which methods will be intercepted by which interceptor
2. Decide which methods will be **intercepted** by which **interceptor**

They make it possible to:

- bind extra logic before / after method execution
- transform the result returned from a function
- transform the exception thrown from a function
- extend the basic function behavior
- completely override a function depending on specific conditions

## Decorators

Expand All @@ -14,55 +25,13 @@ Creating and consuming an interceptor is two-step process.
To create interceptor class you need to implement he `IInterceptor` interface and implement the
`aroundInvoke(ctx: IInterceptorContext)` method, and use the `@Interceptor()` annotation to register your interceptor class. Inside your `src/interceptors/MyInterceptor.ts` folder create the following simple interceptor.

```typescript
import { IInterceptor, IInterceptorContext, Interceptor, InjectorService } from '@tsed/common';

@Interceptor()
export class MyInterceptor implements IInterceptor {
// you can inject other components as usual
constructor(injSrv: InjectorService) {
// do some logic
}

/**
* ctx: The context that holds the dynamic data related to the method execution and the proceed method
* to proceed with the original method execution
*
* opts: Static params that can be provided when the interceptor is attached to a specific method
*/
aroundInvoke(ctx: IInterceptorContext<any>, opts?: any) {
console.log(`the method ${ctx.method} will be executed with args ${ctx.args} and static data ${opts}`);
// let the original method proceed
const retValue = ctx.proceed();

console.log(`the method was executed, and returned ${retValue}`);

// must return the returned value back to the caller
// the retValue might be a promise in which case you can use .then to chain other code logic
// or you can use async aroundInvoke and await ctx.proceed() to execute the code in linear fashion
return retValue;
}
}
```
<<< @/docs/docs/snippets/interceptors/interceptor-example.ts

## Use the interceptor

Now that your interceptor logic is in place you can use it in any other service. You need to use the `@Intercept(InterceptorClass, opts)` annotation to register which interceptor should be used for the specific method you want to intercept. An example service in `src/services/MyService.ts`:

```typescript
import { Intercept } from '@tsed/common';
import { MyInterceptor } from '../interceptors/MyInterceptor';

export class MyService {
// MyInterceptor is going to be used to intercept this method whenever called
// 'simple data' is static data that will be passed as second arg the the interceptor aroundInvoke
// this can be any data, you can pass array or object for that matter
@Intercept(MyInterceptor, 'simple data')
mySimpleMethod() {
console.log('the simple method is executed');
}
}
```
<<< @/docs/docs/snippets/interceptors/interceptor-usage.ts

If the service method is executed like `myServiceInstance.mySimpleMethod()` we will get the following output:

Expand Down
58 changes: 57 additions & 1 deletion docs/docs/middlewares.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ to inject other services on his constructor.
All middlewares decorated by @@Middleware@@ have one method named `use()`.
This method can use all parameters decorators as you could see with the [Controllers](/docs/controllers.md) and return promise.

<figure><img src="./../assets/middleware.svg" style="max-height: 300px; padding: 30px"></figure>


## Configuration

To begin, you must adding the `middlewares` folder on `componentsScan` attribute in your server settings as follow :
Expand Down Expand Up @@ -58,10 +61,63 @@ Endpoint middleware must be used on class controller or endpoint method with the

<<< @/docs/docs/snippets/middlewares/endpoint-middleware-usage.ts

> See [middleware call sequence](/docs/middlewares/call-sequence.md) for more information.
:::

## call sequences

As you see in the previous section, a middleware can be use on different context:

- [ServerLoader](/docs/server-loader.md),
- [Controller](/docs/controllers.md),
- [Endpoint](/docs/controllers.md).

Middleware associated to a controller or endpoint as a same constraint that an endpoint.
It'll be played only when the url request match with the path associated to the controller and his endpoint method.

When a request is sent to the server all middlewares added in the [ServerLoader](/docs/server-loader.md), [Controller](/docs/controllers.md) or Endpoint with decorators
will be called while a response isn't sent by one of the handlers/middlewares in the stack.

<figure><img src="./../assets/middleware-call-sequence.svg" style="max-width:400px; padding:30px"></figure>

::: tip Note (1)
Render middleware is called only when a the @@Render@@ decorator is used on the endpoint.
:::

::: tip Note (2)
SendResponse middleware send a response only when a data is return by the endpoint method or if the endpoint is the latest called endpoint for the resolved route.
:::

::: tip
The middlewares shown in the Endpoints box will be replayed as many times as it has endpoint that match
the request url.
:::

::: warning
Only middlewares shown in the Endpoints box can use @@EndpointInfo@@ decorator to retrieve endpoint context execution.
:::

For example:

<<< @/docs/docs/snippets/middlewares/call-sequences.ts

According to the call sequence scheme, the stack calls will be there:

- **Middlewares** added in ServerLoader (logger, express middleware, etc...),
- **MdlwCtrlBefore**,
- **MdlwCtrlBeforeEach**
- **MdlwBefore**,
- **MdlwCtrl**,
- **MyCtrl.endpointA**,
- **MdlwAfter**,
- **SendResponse**, (but nothing data is returned by the endpointA)
- **MdlwCtrlBeforeEach**
- **MdlwCtrl**,
- **MyCtrl.endpointB**,
- **MdlwAfter**,
- **SendResponse**, send a response because endpointB return a data,
- **MdlwCtrlAfter**, but this middleware will not be called because a response is sent.
- **Middleware** added in ServerLoader (not called too).

## Handle error

Express allows you to handle any error when your middleware have 4 parameters like this:
Expand Down
18 changes: 11 additions & 7 deletions docs/docs/middlewares/call-sequence.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,32 @@ It'll be played only when the url request match with the path associated to the
When a request is sent to the server all middlewares added in the [ServerLoader](/docs/server-loader.md), [Controller](/docs/controllers.md) or Endpoint with decorators
will be called while a response isn't sent by one of the middleware in the stack.

<figure><img src="./../../assets/middleware-call-sequence.png" style="max-width:400px; padding:20px"></figure>
<figure><img src="./../../assets/middleware-call-sequence.svg" style="max-width:400px; padding:30px"></figure>

> Note: The middlewares represented in the Endpoint (0-n) box will be replayed as many times as it has endpoint that match
the url of the request.
::: tip Note
The middlewares shown in the Endpoints box will be replayed as many times as it has endpoint that match
the request url.
:::

> \* Render is called only when a the @Render o @ResponseView decorator is used on the endpoint.
> (1) Render middleware is called only when a the @@Render@@ decorator is used on the endpoint.
> \*\* SendResponse middleware send a response only when a data is return by the endpoint method.
> (2) SendResponse middleware send a response only when a data is return by the endpoint method or if the endpoint is the latest called endpoint for the resolved route.
For example:

<<< @/docs/docs/snippets/middlewares/call-sequences.ts

According to the call sequence scheme, the stack will be there:
According to the call sequence scheme, the stack calls will be there:

- **Middlewares** added in ServerLoader (logger, express middleware, etc...),
- **MdlwCtrlBefore**,
- **MdlwCtrl**,
- **MdlwCtrlBeforeEach**
- **MdlwBefore**,
- **MdlwCtrl**,
- **MyCtrl.endpointA**,
- **MdlwAfter**,
- **SendResponse**, (but nothing data is returned by the endpointA)
- **MdlwCtrlBeforeEach**
- **MdlwCtrl**,
- **MyCtrl.endpointB**,
- **MdlwAfter**,
Expand Down
23 changes: 23 additions & 0 deletions docs/docs/snippets/interceptors/interceptor-example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {IInterceptor, IInterceptorContext, IInterceptorNextHandler, Interceptor} from "@tsed/common";

@Interceptor()
export class MyInterceptor implements IInterceptor {
/**
* ctx: The context that holds the dynamic data related to the method execution and the proceed method
* to proceed with the original method execution
*
* opts: Static params that can be provided when the interceptor is attached to a specific method
*/
intercept(context: IInterceptorContext<any>, next: IInterceptorNextHandler) {
console.log(`the method ${context.propertyKey} will be executed with args ${context.args} and static data ${context.options}`);
// let the original method proceed
const result = context.next();

console.log(`the method was executed, and returned ${result}`);

// must return the returned value back to the caller
// the retValue might be a promise in which case you can use .then to chain other code logic
// or you can use async aroundInvoke and await ctx.proceed() to execute the code in linear fashion
return result;
}
}
12 changes: 12 additions & 0 deletions docs/docs/snippets/interceptors/interceptor-usage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {Intercept} from "@tsed/common";
import {MyInterceptor} from "../interceptors/MyInterceptor";

export class MyService {
// MyInterceptor is going to be used to intercept this method whenever called
// 'simple data' is static data that will be passed as second arg the the interceptor aroundInvoke
// this can be any data, you can pass array or object for that matter
@Intercept(MyInterceptor, "simple data")
mySimpleMethod() {
console.log("the simple method is executed");
}
}
Loading

0 comments on commit a4777b4

Please sign in to comment.