Skip to content

Commit

Permalink
Merge pull request #236 from inversify/feat/add-middleware-page
Browse files Browse the repository at this point in the history
Add middleware page
  • Loading branch information
notaphplover authored Jan 3, 2025
2 parents fb8aac5 + 1e6c12c commit 061d678
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 10 deletions.
6 changes: 3 additions & 3 deletions packages/docs/services/inversify-site/docs/api/container.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,15 @@ Creates a new Container containing the bindings ( cloned bindings ) of two or mo
applyCustomMetadataReader(metadataReader: interfaces.MetadataReader): void;
```

Sets a custom metadata reader. See [middleware](https://github.com/inversify/InversifyJS/blob/master/wiki/middleware.md).
Sets a custom metadata reader. See [middleware](/docs/fundamentals/lifecycle/middleware).

## applyMiddleware

```ts
applyMiddleware(...middleware: interfaces.Middleware[]): void;
```

An advanced feature that can be used for cross cutting concerns. See [middleware](https://github.com/inversify/InversifyJS/blob/master/wiki/middleware.md).
An advanced feature that can be used for cross cutting concerns. See [middleware](/docs/fundamentals/lifecycle/middleware).

## bind

Expand All @@ -115,7 +115,7 @@ Sets a new binding.
createChild(containerOptions?: interfaces.ContainerOptions): Container;
```

Create a [container hierarchy](https://github.com/inversify/InversifyJS/blob/master/wiki/hierarchical_di.md). Parent `ContainerOptions` are provided by default.
Create a [container hierarchy](/docs/fundamentals/di-hierarchy). Parent `ContainerOptions` are provided by default.

## get

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ Inheritance can be accomplished as long as constructor parameters are properly d
```ts
@injectable()
class Warrior {
public rank: string;
constructor(rank: string) { // args count = 1
this.rank = rank;
}
public rank: string;
constructor(rank: string) { // args count = 1
this.rank = rank;
}
}

@injectable()
class SamuraiMaster extends Warrior {
constructor() { // args count = 0
super("master");
}
constructor() { // args count = 0
super("master");
}
}
```

Expand All @@ -33,3 +33,20 @@ When trying to get a `SamuraiMaster`, the container throws an Error alerting the
## Using @unmanaged decorator

The [unmanaged](/docs/api/decorator#unmanaged) decorator is a good way to tell inversify a base type constructor param is should not be managed. This is often the case when dealing with inheritance hierarchies in which only leaf types are injected.

```ts
@injectable()
class Warrior {
public rank: string;
constructor(@unmanaged() rank: string) { // args count = 0, unmanaged args are not included
this.rank = rank;
}
}

@injectable()
class SamuraiMaster extends Warrior {
constructor() { // args count = 0
super("master");
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
sidebar_position: 3
title: Middleware
---
# Middleware

:::warning

Middleware docs are included for historical reasons. They are likely to be remove in favor of more appropiate features.

:::

Middlewares can be added to a container in order to intercept service resolution requests:

## Basic middleware

```ts
import { interfaces, Container } from "inversify";

function logger(planAndResolve: interfaces.Next): interfaces.Next {
return (args: interfaces.NextArgs) => {
let start = new Date().getTime();
let result = planAndResolve(args);
let end = new Date().getTime();
console.log(`wooooo ${end - start}`);
return result;
};
}

let container = new Container();
container.applyMiddleware(logger);
```

## Multiple middleware functions

When multiple middleware functions are applied:

```ts
container.applyMiddleware(middleware1, middleware2);
```

The middleware will be invoked from right to left.
This means that `middleware2` is invoked before `middleware1`.

## Context interceptor

In some cases you may want to intercept the resolution plan.

The default `contextInterceptor` is passed to the middleware as an property of `args`.

```ts
function middleware1(planAndResolve: interfaces.Next): interfaces.Next<unknown> {
return (args: interfaces.NextArgs) => {
// args.nextContextInterceptor
// ...
};
}
```

You can extend the default `contextInterceptor` using a function:

```ts
function middleware1(planAndResolve: interfaces.Next<unknown>): interfaces.Next<unknown> {
return (args: interfaces.NextArgs) => {
let nextContextInterceptor = args.contextInterceptor;
args.contextInterceptor = (context: interfaces.Context) => {
console.log(context);
return nextContextInterceptor(context);
};
return planAndResolve(args);
};
}
```

## Custom metadata reader

:::danger

It is not recommended to create your own custom
metadata reader. We have included this feature to allow library / framework creators
to have a higher level of customization but the average user should not use a custom
metadata reader. In general, a custom metadata reader should only be used when
developing a framework in order to provide users with an annotation APIs
less explicit than the default annotation API.

If you are developing a framework or library and you create a custom metadata reader,
Please remember to provide your framework with support for an alternative for all the
decorators in the default API: `@injectable`, `@inject`, `@multiInject`, `@tagged`,
`@named`, `@optional`, `@postConstruct`, `@preDestroy` `@targetName` & `@unmanaged`.

:::

Middleware allows you to intercept a plan and resolve it but you are not allowed to change the way the annotation phase behaves.

There is a second extension point that allows you to decide what kind of annotation
system you would like to use. The default annotation system is powered by decorators and
reflect-metadata:

```ts
@injectable()
class Ninja implements Ninja {

private _katana: Katana;
private _shuriken: Shuriken;

constructor(
@inject("Katana") katana: Katana,
@inject("Shuriken") shuriken: Shuriken
) {
this._katana = katana;
this._shuriken = shuriken;
}

public fight() { return this._katana.hit(); };
public sneak() { return this._shuriken.throw(); };

}
```

You can use a custom metadata reader to implement a custom annotation system.

For example, you could implement an annotation system based on static properties:

```ts
class Ninja implements Ninja {

public static constructorInjections = [
"Katana", "Shuriken"
];

private _katana: Katana;
private _shuriken: Shuriken;

constructor(
katana: Katana,
shuriken: Shuriken
) {
this._katana = katana;
this._shuriken = shuriken;
}

public fight() { return this._katana.hit(); };
public sneak() { return this._shuriken.throw(); };

}
```

A custom metadata reader must implement the `interfaces.MetadataReader` interface.

A full example [can be found in our unit tests](https://github.com/inversify/InversifyJS/blob/master/test/features/metadata_reader.test.ts).

Once you have a custom metadata reader you will be ready to apply it:

```ts
let container = new Container();
container.applyCustomMetadataReader(new StaticPropsMetadataReader());
```

0 comments on commit 061d678

Please sign in to comment.