Skip to content

Commit

Permalink
processor state
Browse files Browse the repository at this point in the history
  • Loading branch information
dj-nitehawk committed Mar 5, 2023
1 parent aca2523 commit 4787cdf
Showing 1 changed file with 76 additions and 0 deletions.
76 changes: 76 additions & 0 deletions src/routes/docs/[...9]pre-post-processors.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,5 +175,81 @@ app.UseFastEndpoints(c =>
```
The **Order** enum specifies whether to run the global processors before or after the endpoint level processors if there's also endpoint level processors configured.

## Sharing State
In order to share state among pre/post processors and the endpoint, you have to first create a class to act as the state holder such as the following:

```cs
public class MyStateBag
{
private readonly Stopwatch _sw = new();

public bool IsValidAge { get; set; }
public string Status { get; set; }
public long DurationMillis => _sw.ElapsedMilliseconds;

public MyStateBag() => _sw.Start();
}
```

Then create processors implementing the following abstract classes instead of the interfaces mentioned above. They will have the state bag passed in to the process method like so:
```cs
public class AgeChecker : PreProcessor<MyRequest, MyStateBag>
{
public override Task PreProcessAsync(MyRequest req, MyStateBag state, HttpContext _, List<ValidationFailure> _, CancellationToken _)
{
if (req.Age >= 18)
state.IsValidAge = true;
state.Status = $"age checked by pre-processor at {state.DurationMillis} ms.";
return Task.CompletedTask;
}
}
```
```cs
public class DurationLogger : PostProcessor<MyRequest, MyStateBag, object>
{
public override Task PostProcessAsync(MyRequest _, MyStateBag state, object _, HttpContext ctx, IReadOnlyCollection<ValidationFailure> _, CancellationToken _)
{
ctx.Resolve<ILogger<DurationLogger>>()
.LogInformation("request took {@duration} ms.", state.DurationMillis);
return Task.CompletedTask;
}
}
```
The endpoint is able to access the same shared/common state by calling the **ProcessorState&lt;MyStateBag&gt;()** method like so:
```cs
public class MyEndpoint : Endpoint<MyRequest>
{
public override void Configure()
{
...
PreProcessors(new AgeChecker());
PostProcessors(new DurationLogger());
}

public override async Task HandleAsync(MyRequest r, CancellationToken c)
{
var state = ProcessorState<MyStateBag>();
Logger.LogInformation("endpoint executed at {@duration} ms.", state.DurationMillis);
await Task.Delay(100);
await SendAsync(new
{
r.Age,
state.Status
});
}
}
```

It's also possible to access the common state when using the processor interfaces, but it has to be done via the http context like so:
```cs
public class MyPreProcessor : IPreProcessor<Request>
{
public Task PreProcessAsync(Request _, HttpContext ctx, List<ValidationFailure> _, CancellationToken _)
{
var state = ctx.ProcessorState<Thingy>();
}
}
```

## Dependency Injection
Processors are singletons for [performance reasons](/benchmarks). I.e. there will only ever be one instance of a processor. You should not maintain state in them. Dependencies can be resolved as shown [here](dependency-injection#pre-post-processor-dependencies).

0 comments on commit 4787cdf

Please sign in to comment.