A contrived example:
- Endpoint
"/math/{x:int}"expects a positive integerxand returns2x. - Filter preprocesses argument with
Math.Abs()to produce a positive integer for the endpoint. - Filter also post processes
2xby adding 1 to it. So the final output is an odd number2x+1.
First Attempt (Tampering Argument) --> Bad!
app.MapGet("/math/{x:int}", (int x) =>
{
return ValueTask.FromResult(2 * x);
})
.AddEndpointFilter(async (ctx, next) =>
{
var arg = ctx.GetArgument<int>(0);
arg = Math.Abs(arg);
ctx.Arguments[0] = arg;
var output = await next(ctx);
return (int)output! + 1;
})
;
Second Attempt (Without Tampering Argument) --> Good?
I add the sterilized arg to EndpointFilterInvocationContext.Arguments as follows:
arg = Math.Abs(arg);
ctx.Arguments.Add(arg);
But now the problem, is it possible to retrieve it from the endpoint?
app.MapGet("/math/{x:int}", (int x) =>
{
// I need to retrieve the data from filter here, but how?
// The data is at the last entry in Arguments.
// return oddnumber;
})
.AddEndpointFilter(async (ctx, next) =>
{
var arg = ctx.GetArgument<int>(0);
arg = Math.Abs(arg);
ctx.Arguments.Add(arg);
var output = await next(ctx);
return (int)output! + 1;
})
;
Real Scenario
In my real scenario, I want to make the endpoint only handle database models and filters do preprocessing (converting view model to models) and post processing (converting models to view models).
app.MapGet("/User", ([FromBody]UserCreateDto dto) =>
{
// How to retrieve User passed from filter here?
// return user;
})
.AddEndpointFilter(async (ctx, next) =>
{
var userCreateDto = ctx.GetArgument<UserCreateDto>(0);
var user = Mapper.FromCreateToUser(userCreateDto);
ctx.Arguments.Add(user);
var result = await next(ctx);
var userDisplayDto = Mapper.FromUserToDisplay((result as User)!);
return userCreateDto;
})
;
app.Run();
class User
{
public int Id { get; set; }
public string Name { get; set; } = default!;
public int Age { get; set; }
}
class UserCreateDto
{
public string Name { get; set; } = default!;
public int Age { get; set; }
}
class UserDisplayDto
{
public int Id { get; set; }
public string Name { get; set; } = default!;
}
static class Mapper
{
public static User FromCreateToUser(UserCreateDto dto) =>
new User { Name = dto.Name.ToLower(), Age = dto.Age };
public static UserDisplayDto FromUserToDisplay(User user) =>
new UserDisplayDto { Id = user.Id, Name = user.Name };
}