Minimal API uygulamalarında filtreler
Uyarı
ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.
Fiyaz Bin Hasan, Martin Costello ve Rick Anderson tarafından
En düşük API filtreleri, geliştiricilerin şunları destekleyen iş mantığını uygulamasına olanak sağlar:
- Uç nokta işleyiciden önce ve sonra kod çalıştırma.
- Uç nokta işleyicisi çağırma sırasında sağlanan parametreleri inceleme ve değiştirme.
- Bir uç nokta işleyicisinin yanıt davranışını kesme.
Filtreler aşağıdaki senaryolarda yararlı olabilir:
- Bir uç noktaya gönderilen istek parametrelerini ve gövdesini doğrulama.
- İstek ve yanıt hakkındaki günlük bilgileri.
- İsteğin desteklenen bir API sürümünü hedeflediğini doğrulama.
Filtreler, bir alan EndpointFilterInvocationContext
ve döndüren EndpointFilterDelegate
bir Temsilci sağlanarak kaydedilebilir. , EndpointFilterInvocationContext
isteğin HttpContext
ve işleyicinin bildiriminde göründükleri sırada işleyiciye geçirilen bağımsız değişkenleri gösteren bir Arguments
listeye erişim sağlar.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string ColorName(string color) => $"Color specified: {color}!";
app.MapGet("/colorSelector/{color}", ColorName)
.AddEndpointFilter(async (invocationContext, next) =>
{
var color = invocationContext.GetArgument<string>(0);
if (color == "Red")
{
return Results.Problem("Red not allowed!");
}
return await next(invocationContext);
});
app.Run();
Yukarıdaki kod:
AddEndpointFilter
Uç noktaya filtre/colorSelector/{color}
eklemek için uzantı yöntemini çağırır.- değeri
"Red"
dışında belirtilen rengi döndürür. - İstendiğinde
/colorSelector/Red
Results.Problem döndürür. - İşlem hattında sonraki filtreyi
EndpointFilterDelegate
veya son filtre çağrıldıysa istek temsilcisini çağırmak için veinvocationContext
olarakEndpointFilterInvocationContext
kullanırnext
.
Filtre, uç nokta işleyiciden önce çalıştırılır. İşleyici üzerinde birden çok AddEndpointFilter
çağrı yapıldığında:
- (
next
) çağrılmadan önceEndpointFilterDelegate
çağrılan filtre kodu İlk Gelen, İlk Çıkış (FIFO) sırasına göre yürütülür. - (
next
) çağrıldıktan sonraEndpointFilterDelegate
çağrılan filtre kodu İlk Gelen, Son Çıkış (FILO) sırasına göre yürütülür.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
app.Logger.LogInformation(" Endpoint");
return "Test of multiple filters";
})
.AddEndpointFilter(async (efiContext, next) =>
{
app.Logger.LogInformation("Before first filter");
var result = await next(efiContext);
app.Logger.LogInformation("After first filter");
return result;
})
.AddEndpointFilter(async (efiContext, next) =>
{
app.Logger.LogInformation(" Before 2nd filter");
var result = await next(efiContext);
app.Logger.LogInformation(" After 2nd filter");
return result;
})
.AddEndpointFilter(async (efiContext, next) =>
{
app.Logger.LogInformation(" Before 3rd filter");
var result = await next(efiContext);
app.Logger.LogInformation(" After 3rd filter");
return result;
});
app.Run();
Yukarıdaki kodda filtreler ve uç nokta aşağıdaki çıkışı günlüğe kaydeder:
Before first filter
Before 2nd filter
Before 3rd filter
Endpoint
After 3rd filter
After 2nd filter
After first filter
Aşağıdaki kod arabirimini uygulayan IEndpointFilter
filtreleri kullanır:
using Filters.EndpointFilters;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () =>
{
app.Logger.LogInformation("Endpoint");
return "Test of multiple filters";
})
.AddEndpointFilter<AEndpointFilter>()
.AddEndpointFilter<BEndpointFilter>()
.AddEndpointFilter<CEndpointFilter>();
app.Run();
Yukarıdaki kodda filtreler ve işleyiciler günlükleri çalıştırılacakları sırayı gösterir:
AEndpointFilter Before next
BEndpointFilter Before next
CEndpointFilter Before next
Endpoint
CEndpointFilter After next
BEndpointFilter After next
AEndpointFilter After next
Arabirimi uygulayan IEndpointFilter
filtreler aşağıdaki örnekte gösterilmiştir:
namespace Filters.EndpointFilters;
public abstract class ABCEndpointFilters : IEndpointFilter
{
protected readonly ILogger Logger;
private readonly string _methodName;
protected ABCEndpointFilters(ILoggerFactory loggerFactory)
{
Logger = loggerFactory.CreateLogger<ABCEndpointFilters>();
_methodName = GetType().Name;
}
public virtual async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context,
EndpointFilterDelegate next)
{
Logger.LogInformation("{MethodName} Before next", _methodName);
var result = await next(context);
Logger.LogInformation("{MethodName} After next", _methodName);
return result;
}
}
class AEndpointFilter : ABCEndpointFilters
{
public AEndpointFilter(ILoggerFactory loggerFactory) : base(loggerFactory) { }
}
class BEndpointFilter : ABCEndpointFilters
{
public BEndpointFilter(ILoggerFactory loggerFactory) : base(loggerFactory) { }
}
class CEndpointFilter : ABCEndpointFilters
{
public CEndpointFilter(ILoggerFactory loggerFactory) : base(loggerFactory) { }
}
Filtreyle nesneyi doğrulama
Bir nesneyi doğrulayan bir Todo
filtre düşünün:
app.MapPut("/todoitems/{id}", async (Todo inputTodo, int id, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
}).AddEndpointFilter(async (efiContext, next) =>
{
var tdparam = efiContext.GetArgument<Todo>(0);
var validationError = Utilities.IsValid(tdparam);
if (!string.IsNullOrEmpty(validationError))
{
return Results.Problem(validationError);
}
return await next(efiContext);
});
Önceki kodda:
- nesnesi,
EndpointFilterInvocationContext
yöntemi aracılığıylaGetArguments
uç noktaya verilen belirli bir istekle ilişkili parametrelere erişim sağlar. - Filtre, bir alan
EndpointFilterInvocationContext
ve döndüren birEndpointFilterDelegate
kullanılarakdelegate
kaydedilir.
Temsilci olarak geçirilmeye ek olarak, arabirim uygulanarak IEndpointFilter
filtreler kaydedilebilir. Aşağıdaki kod, uygulayan IEndpointFilter
bir sınıfta kapsüllenen önceki filtreyi gösterir:
public class TodoIsValidFilter : IEndpointFilter
{
private ILogger _logger;
public TodoIsValidFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<TodoIsValidFilter>();
}
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext efiContext,
EndpointFilterDelegate next)
{
var todo = efiContext.GetArgument<Todo>(0);
var validationError = Utilities.IsValid(todo!);
if (!string.IsNullOrEmpty(validationError))
{
_logger.LogWarning(validationError);
return Results.Problem(validationError);
}
return await next(efiContext);
}
}
Arabirimi uygulayan IEndpointFilter
filtreler, bağımlılıkları önceki kodda gösterildiği gibi Bağımlılık Ekleme(DI) ile çözebilir. Filtreler DI'den bağımlılıkları çözümleyese de, filtrelerin kendisi DI'den çözümlenemez.
ToDoIsValidFilter
aşağıdaki uç noktalara uygulanır:
app.MapPut("/todoitems2/{id}", async (Todo inputTodo, int id, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
}).AddEndpointFilter<TodoIsValidFilter>();
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
}).AddEndpointFilter<TodoIsValidFilter>();
Aşağıdaki filtre nesneyi doğrular Todo
ve özelliğini değiştirir Name
:
public class TodoIsValidUcFilter : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext efiContext,
EndpointFilterDelegate next)
{
var todo = efiContext.GetArgument<Todo>(0);
todo.Name = todo.Name!.ToUpper();
var validationError = Utilities.IsValid(todo!);
if (!string.IsNullOrEmpty(validationError))
{
return Results.Problem(validationError);
}
return await next(efiContext);
}
}
Uç nokta filtre fabrikası kullanarak filtre kaydetme
Bazı senaryolarda, filtrede MethodInfo
sağlanan bazı bilgilerin önbelleğe alınması gerekebilir. Örneğin, uç nokta filtresinin eklendiği işleyicinin bir türe göre değerlendirilen ilk parametresi olduğunu doğrulamak istediğimizi Todo
varsayalım.
app.MapPut("/todoitems/{id}", async (Todo inputTodo, int id, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
}).AddEndpointFilterFactory((filterFactoryContext, next) =>
{
var parameters = filterFactoryContext.MethodInfo.GetParameters();
if (parameters.Length >= 1 && parameters[0].ParameterType == typeof(Todo))
{
return async invocationContext =>
{
var todoParam = invocationContext.GetArgument<Todo>(0);
var validationError = Utilities.IsValid(todoParam);
if (!string.IsNullOrEmpty(validationError))
{
return Results.Problem(validationError);
}
return await next(invocationContext);
};
}
return invocationContext => next(invocationContext);
});
Önceki kodda:
- nesnesi,
EndpointFilterFactoryContext
uç noktanınMethodInfo
işleyicisiyle ilişkili öğesine erişim sağlar. - İşleyicinin imzası, beklenen tür imzası denetlenerek
MethodInfo
incelenir. Beklenen imza bulunursa doğrulama filtresi uç noktaya kaydedilir. Bu fabrika düzeni, hedef uç nokta işleyicisinin imzasına bağlı bir filtre kaydetmek için kullanışlıdır. - Eşleşen bir imza bulunamazsa, geçiş filtresi kaydedilir.
Denetleyici eylemlerine filtre kaydetme
Bazı senaryolarda, hem yol işleyicisi tabanlı uç noktalar hem de denetleyici eylemleri için aynı filtre mantığını uygulamak gerekebilir. Bu senaryoda, eylemlerde ve uç noktalarda ControllerActionEndpointConventionBuilder
aynı filtre mantığını yürütmeyi desteklemek için çağrısı AddEndpointFilter
yapmak mümkündür.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapController()
.AddEndpointFilter(async (efiContext, next) =>
{
efiContext.HttpContext.Items["endpointFilterCalled"] = true;
var result = await next(efiContext);
return result;
});
app.Run();
Ek Kaynaklar
ASP.NET Core
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin