Ho un progetto ASP.NET WebApi su cui sto lavorando. Il capo desidera che i ritorni supportino la "risposta parziale", il che significa che sebbene il modello di dati possa contenere 50 campi, il client dovrebbe essere in grado di richiedere campi specifici per la risposta. Il motivo è che se stanno implementando per esempio un elenco semplicemente non hanno bisogno del sovraccarico di tutti i 50 campi, potrebbero semplicemente volere il Nome, il Cognome e l'Id per generare l'elenco. Finora ho implementato una soluzione usando un Resolver Contratto personalizzato (DynamicContractResolver) in modo tale che quando arriva una richiesta ci sto sbirciando attraverso un filtro (FieldListFilter) nel metodo OnActionExecuting e determini se è presente un campo chiamato "FieldList" e quindi se lo è sto sostituendo l'attuale ContractResolver con una nuova istanza del mio DynamicContractResolver e passo la lista dei campi al costruttore.ASP.NET WebApi e risposte parziali
Alcuni codice di esempio
DynamicContractResolver.cs
protected override IList<JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
{
List<String> fieldList = ConvertFieldStringToList();
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
if (fieldList.Count == 0)
{
return properties;
}
// If we have fields, check that FieldList is one of them.
if (!fieldList.Contains("FieldList"))
// If not then add it, FieldList must ALWAYS be a part of any non null field list.
fieldList.Add("FieldList");
if (!fieldList.Contains("Data"))
fieldList.Add("Data");
if (!fieldList.Contains("FilterText"))
fieldList.Add("FilterText");
if (!fieldList.Contains("PageNumber"))
fieldList.Add("PageNumber");
if (!fieldList.Contains("RecordsReturned"))
fieldList.Add("RecordsReturned");
if (!fieldList.Contains("RecordsFound"))
fieldList.Add("RecordsFound");
for (int ctr = properties.Count-1; ctr >= 0; ctr--)
{
foreach (string field in fieldList)
{
if (field.Trim() == properties[ctr].PropertyName)
{
goto Found;
}
}
System.Diagnostics.Debug.WriteLine("Remove Property at Index " + ctr + " Named: " + properties[ctr].PropertyName);
properties.RemoveAt(ctr);
// Exit point for the inner foreach. Nothing to do here.
Found: { }
}
return properties;
}
FieldListFilter.cs
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
// We need to determine if there is a FieldList property of the model that is being used.
// First get a reference to the model.
var modelObject = actionContext.ActionArguments.FirstOrDefault().Value;
string fieldList = string.Empty;
try
{
// Using reflection, attempt to get the value of the FieldList property
var fieldListTemp = modelObject.GetType().GetProperty("FieldList").GetValue(modelObject);
// If it is null then use an empty string
if (fieldListTemp != null)
{
fieldList = fieldListTemp.ToString();
}
}
catch (Exception)
{
fieldList = string.Empty;
}
// Update the global ContractResolver with the fieldList value but for efficiency only do it if they are not the same as the current ContractResolver.
if (((DynamicContractResolver)GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver).FieldList != fieldList)
{
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DynamicContractResolver(fieldList);
}
}
posso quindi inviare una richiesta con il contenuto JSON payload cercando, come ad esempio:
{
"FieldList":"NameFirst,NameLast,Id",
"Data":[
{
"Id":1234
},
{
"Id":1235
}
]
}
e riceverò una risposta in questo modo:
{
"FieldList":"NameFirst,NameLast,Id",
"Data":[
{
"NameFirst":"Brian",
"NameLast":"Mueller",
"Id":1234
},
{
"NameFirst":"Brian",
"NameLast":"Mueller",
"Id":1235
}
]
}
credo che utilizzando il ContractResolver potrebbe incorrere in problemi di threading. Se lo cambio per una richiesta, sarà valida per tutte le richieste successive fino a quando qualcuno non la cambierà su un'altra richiesta (sembra così attraverso i test) Se è così, allora non vedo l'utilità per il mio scopo.
In sintesi, sto cercando un modo per disporre di modelli di dati dinamici in modo che l'output di una richiesta sia configurabile dal client su richiesta in base alle richieste. Google implementa questo nella loro web api e lo chiamano "risposta parziale" e funziona alla grande. La mia implementazione funziona, ma a un certo punto temo che sarà interrotta per più richieste simultanee.
Suggerimenti? Suggerimenti?
Solo per FYI ... controlla il supporto della funzione '$ select' per JSON formattatter che sarà disponibile nella prossima versione: https://aspnetwebstack.codeplex.com/wikipage?title=%24select%20and%20%24expand%20support&referringTitle = Specifiche –
Non credo di avere il tempo di aspettare la prossima versione. Abbiamo una demo in meno di 2 mesi e ho molto da implementare. Più precisamente, il mio metodo è fondamentalmente rotto. Avevo implementato sulla base di suggerimenti e tutorial da tutto il Web, ma ho solo alcune domande sulla base di ciò che non comprendo appieno. –
se crei una nuova istanza di 'DynamicContractResolver' su ogni richiesta e la usi come' ContractResolver', allora non ci dovrebbe essere un problema di concorrenza. – muratgu