Nello script precedente (https://www.aspitalia.com/script/1330/Semplificare-Gestione-Array-Querystring-ASP.NET-Core.aspx) abbiamo visto come, tramite un custom value provider, possiamo modificare il modo in cui rappresentare gli array in querystring sfruttando, per esempio, una sintassi più compatta basata su valori separati da virgole.
Ovviamente questo approccio può risultare non privo di effetti collaterali: se per esempio accettiamo dei valori decimali in querystring, il nostro Value Provider potrebbe erroneamente considerare un numero decimale come un array di stringhe.
Ci sono un paio di accorgimenti che possiamo adottare per migliorare il nostro custom value provider.
Attivazione tramite Resource Filter
Il primo è quello di sfruttare un Resource Filter così da attivarlo solo per le action dove sia effettivamente necessario. Innanzitutto dobbiamo creare una classe che implementi IResourceFilter come la seguente:
public class CommaSeparatedAttribute : Attribute, IResourceFilter { public void OnResourceExecuted(ResourceExecutedContext context) { } public void OnResourceExecuting(ResourceExecutingContext context) { context.ValueProviderFactories.Insert(0, new QueryStringCommaSeparatedValueProviderFactory()); } }
Essa ha il compito di registrare la nostra factory appena prima che la richiesta venga processata dalla action. In questo modo, possiamo disattivare il nostro value provider dalla classe Startup:
public void ConfigureServices(IServiceCollection services) { // non è più necessario configurare QueryStringCommaSeparatedValueProvider services.AddMvc(); // altro codice qui.. }
Solo nelle action in cui ne abbiamo bisogno, possiamo riattivarlo decorandole con l'attributo CommaSeparated che abbiamo appena definito:
[HttpGet] [CommaSeparated] public string Get([FromQuery]IEnumerable<int> values) { return string.Join(" - ", values); }
Attivazione tramite BindingSource
Il resource filter appena creato ha il problema di agire sull'intera richiesta invece che sul singolo parametro, quindi potrebbe non essere sufficientemente preciso per alcuni scenari. Un altro approccio è quello di configurare una BindingSource completamente separata. Quando specifichiamo FromQuery nei parametri di una action, infatti, stiamo indicando ad ASP.NET Core che la BindingSource per quel parametro è di tipo Query. Ad ogni BindingSource possono poi essere associati uno o più ValueProvider che il runtime utilizzerà per leggere questa informazione dalla richiesta.
Nel nostro caso, l'idea è quella di definire una BindingSource completamente nuova, creando un attribute come il seguente:
public class FromCommaSeparatedArrayAttribute : Attribute, IBindingSourceMetadata { public static BindingSource CommaSeparated => new BindingSource( id: "commaSeparated", displayName: "Comma separated array", isGreedy: false, isFromRequest: true); public BindingSource BindingSource => FromCommaSeparatedArrayAttribute.CommaSeparated; }
Il nostro FromCommaSeparatedArrayAttribute implementa IBindingSourceMetadata e restituisce un BindingSource denominato CommaSeparated. A questo punto dobbiamo effettuare una piccola modifica a QueryStringCommaSeparatedValueProviderFactory che abbiamo creato nello script precedente:
public class QueryStringCommaSeparatedValueProviderFactory : IValueProviderFactory { public Task CreateValueProviderAsync(ValueProviderFactoryContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var query = context.ActionContext.HttpContext.Request.Query; if (query != null && query.Count > 0) { var valueProvider = new QueryStringCommaSeparatedValueProvider( // invece di BindingSource.Query FromCommaSeparatedArrayAttribute.CommaSeparated, query, CultureInfo.InvariantCulture); context.ValueProviders.Add(valueProvider); } return Task.CompletedTask; } }
Come possiamo vedere, questa volta quando aggiungiamo QueryStringCommaSeparatedValueProvider alla collezione di value provider disponibili, sfruttiamo il marcatore CommaSeparated invece che il BindingSource.Query standard. In questo modo, ASP.NET Core lo ignorerà per tutti i binding da querystring, a meno che non lo attiviamo in maniera esplicita sul singolo parametro:
public string Get([FromCommaSeparatedArray]IEnumerable<int> values) { return string.Join(" - ", values); }
Ovviamente, affinchè tutto funzioni, dobbiamo registrare la factory allo startup dell'applicazione:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.ValueProviderFactories.Add( new QueryStringCommaSeparatedValueProviderFactory()); }); // .. altro codice qui .. }
Conclusioni
Il sistema di model binding di ASP.NET Core, che traduce il contenuto della richiesta HTTP in parametri dei nostri metodi, è incredibilmente flessibile e personalizzabile. Nel corso di questo script e del precedente (https://www.aspitalia.com/script/1330/Semplificare-Gestione-Array-Querystring-ASP.NET-Core.aspx), abbiamo visto come modificare il modo in cui rappresentiamo Array in querystring pur continuando a sfruttare la complessa infrastruttura di model binding esistente. Abbiamo poi visto un paio di differenti approcci per limitare il più possibile gli effetti collaterali, indicanto in maniera puntuale dove la nostra logica deve essere attivata.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Utilizzare il trigger SQL con le Azure Function
Eseguire query manipolando liste di tipi semplici con Entity Framework Core
Cambiare la chiave di partizionamento di Azure Cosmos DB
Criptare la comunicazione con mTLS in Azure Container Apps
Utilizzare EF.Constant per evitare la parametrizzazione di query SQL
Ottimizzare la latenza in Blazor 8 tramite InteractiveAuto render mode
Gestire il colore CSS con HWB
Sviluppare un'interfaccia utente in React con Tailwind CSS e Preline UI
Utilizzare un service principal per accedere a Azure Container Registry
Utilizzare i primary constructor di C# per inizializzare le proprietà
Ottimizzare le performance delle collection con le classi FrozenSet e FrozenDictionary
Effettuare il refresh dei dati di una QuickGrid di Blazor