Optiize binder and api key retrieval caching
This commit is contained in:
parent
c5a9db661b
commit
bc4b04e378
|
|
@ -83,38 +83,34 @@ namespace NejCommon.Models
|
|||
throw new ArgumentNullException(nameof(bindingContext));
|
||||
}
|
||||
using var activity = Telemetry.Start();
|
||||
|
||||
|
||||
var cancellationToken = bindingContext.HttpContext.RequestAborted;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
//get the binding parameter attributes
|
||||
|
||||
// Fetch the route values from the route data
|
||||
var capturedRouteData = new Dictionary<string, object>();
|
||||
foreach (var routeValue in _routeValues)
|
||||
var values = new string[_routeValues.Length];
|
||||
for (var i = 0; i < _routeValues.Length; i++)
|
||||
{
|
||||
var name = routeValue.Name;
|
||||
if (routeValue.Primary && !string.IsNullOrWhiteSpace(bindingContext.ModelName))
|
||||
{
|
||||
name = bindingContext.ModelName;
|
||||
}
|
||||
var routeValue = _routeValues[i];
|
||||
|
||||
/*
|
||||
Console.WriteLine("Route value: " + name);
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(bindingContext.ActionContext.RouteData.Values[name]);
|
||||
*/
|
||||
var name =
|
||||
routeValue.Primary && !string.IsNullOrWhiteSpace(bindingContext.ModelName)
|
||||
? bindingContext.ModelName
|
||||
: routeValue.Name;
|
||||
|
||||
var value = bindingContext.ValueProvider.GetValue(name).FirstOrDefault();//bindingContext.ActionContext.RouteData.Values[name];
|
||||
|
||||
if (value is null)
|
||||
// If these really are route values, don't go through the entire
|
||||
// MVC ValueProvider infrastructure.
|
||||
if (!bindingContext.ActionContext.RouteData.Values.TryGetValue(name, out var rawValue) ||
|
||||
rawValue is null)
|
||||
{
|
||||
bindingContext.ModelState.AddModelError(name, "Route value not found");
|
||||
bindingContext.Result = ModelBindingResult.Failed();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
capturedRouteData[routeValue.Name] = value;
|
||||
}
|
||||
|
||||
values[i] = rawValue.ToString()!;
|
||||
}
|
||||
|
||||
// Check if all the required route values are present
|
||||
|
|
@ -124,21 +120,39 @@ namespace NejCommon.Models
|
|||
return;
|
||||
}
|
||||
|
||||
var entityParameter = Expression.Parameter(typeof(TEntity), "entity");
|
||||
|
||||
// Find the entity with the specified route values
|
||||
var query = _db.Set<TEntity>().AsQueryable();
|
||||
foreach (var routeValue in _routeValues)
|
||||
Expression? predicateBody = null;
|
||||
for (var i = 0; i < _routeValues.Length; i++)
|
||||
{
|
||||
var value = capturedRouteData[routeValue.Name];
|
||||
var routeValue = _routeValues[i];
|
||||
|
||||
var par = Expression.Parameter(typeof(TEntity));
|
||||
var exp = Expression.Invoke(routeValue.Expression, par, Expression.Constant((string)value));
|
||||
var redExp = exp.Reduce();
|
||||
var compiledExp = Expression.Lambda<Func<TEntity, bool>>(redExp, par);
|
||||
// IMPORTANT:
|
||||
// captured variable -> EF parameter
|
||||
// rather than Expression.Constant(value)
|
||||
var value = values[i];
|
||||
|
||||
query = query.Where(compiledExp);
|
||||
Expression<Func<string>> valueExpression = () => value;
|
||||
|
||||
var expression = Expression.Invoke(
|
||||
routeValue.Expression,
|
||||
entityParameter,
|
||||
valueExpression.Body);
|
||||
|
||||
var body = expression.Reduce();
|
||||
|
||||
predicateBody = predicateBody is null
|
||||
? body
|
||||
: Expression.AndAlso(predicateBody, body);
|
||||
}
|
||||
|
||||
var model = await query.FirstOrDefaultAsync(cancellationToken);
|
||||
var predicate =
|
||||
Expression.Lambda<Func<TEntity, bool>>(
|
||||
predicateBody!,
|
||||
entityParameter);
|
||||
|
||||
var model = await _db.Set<TEntity>().AsQueryable().FirstOrDefaultAsync(predicate, cancellationToken);
|
||||
|
||||
if (model == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
using AspNetCore.Authentication.ApiKey;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NejCommon.Models;
|
||||
using NejCommon.Utils;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
|
||||
namespace NejCommon.Services;
|
||||
|
||||
public partial class ApiKeyProvider : IApiKeyProvider, IScopedService
|
||||
{
|
||||
private readonly ILogger<ApiKeyProvider> _logger;
|
||||
private readonly IFusionCache _cache;
|
||||
|
||||
public async Task<IApiKey> ProvideAsync(string key)
|
||||
public async Task<IApiKey?> ProvideAsync(string key)
|
||||
{
|
||||
try
|
||||
using var activity = Telemetry.Start("ApiKeyProvider.ProvideAsync");
|
||||
|
||||
return await _cache.GetOrSetAsync($"ApiKeyProvider.ProvideAsync.{key}", async entry =>
|
||||
{
|
||||
// write your validation implementation here and return an instance of a valid ApiKey or retun null for an invalid key.
|
||||
var lookup = ApiKey.ComputeLookupHash(key);
|
||||
|
||||
var apiKey = await GetApiKeySet().FirstOrDefaultAsync(x => x.LookupHash.SequenceEqual(lookup));
|
||||
|
|
@ -25,17 +31,22 @@ public partial class ApiKeyProvider : IApiKeyProvider, IScopedService
|
|||
return null;
|
||||
}
|
||||
|
||||
if (apiKey.Revoked)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
apiKey.Key = key;
|
||||
|
||||
// write your validation implementation here and return an instance of a valid ApiKey or retun null for an invalid key.
|
||||
// return await _apiKeyRepository.GetApiKeyAsync(key);
|
||||
return apiKey;
|
||||
}
|
||||
catch (System.Exception exception)
|
||||
}, new FusionCacheEntryOptions()
|
||||
{
|
||||
_logger.LogError(exception, exception.Message);
|
||||
throw;
|
||||
}
|
||||
Duration = TimeSpan.FromMinutes(1),
|
||||
IsFailSafeEnabled = true,
|
||||
FailSafeMaxDuration = TimeSpan.FromMinutes(5),
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static string GenerateKey() => "nej-" + NanoidDotNet.Nanoid.Generate(size: 256);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user