Instead of bringing in another package, you can just pass your function code as an argument in to a wrapper method.
//business logic
[FunctionName("PostWidget")]
public async Task<IActionResult> PostWidget(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "widgets")] Widget item, HttpRequest req, ILogger log)
{
return await _functionWrapper.Execute(req, item, async () =>
{
log.LogInformation($"posting widget: ${item.Id}");
var newItem = await dbContext.Widgets.AddAsync(item);
await dbContext.SaveChangesAsync();
return new ResponseEnvelopeResult<Widget>(HttpStatusCode.Created, newItem.Entity);
});
}
//functionWrapper class
public async Task<IActionResult> Execute(T model, HttpRequest req, Func<Task<IActionResult>> azureFunction)
{
var results = await _validator.ValidateAsync(model, ruleSet: $"default,audit,{req.Method}");
if (!results.IsValid)
{
var errors = results.Errors.Select(x => x.ErrorMessage).ToList();
_log.LogWarning($"Model validation failed for type '{typeof(T).Name}'. Validation errors: [{errors.Join()}] ");
return new ResponseEnvelopeResult<T>(HttpStatusCode.BadRequest, null, errors);
}
try
{
return await azureFunction();
}
catch (Exception e)
{
_log.LogError(e, "Unhandled exception occured in FunctionWrapper");
return new ResponseEnvelopeResult<T>(HttpStatusCode.InternalServerError, null, new[] { e.Message });
}
}
Then your wrapper can be setup to do validation, retrieve user info, etc. If you need items passed back to your function layer, you can easily do so without obscuring your function intent. I've got a large example of this implementation on my blog.
https://blog.bruceleeharrison.com/2019/09/04/azure-v2-functions-with-fluentvalidation/