Da this blog post, sono stato in grado di creare una WCF personalizzata IDispatchMessageFormatter
che utilizza la serializzazione JSON.NET. Funziona alla grande con un avvertimento: utilizzarlo con UriTemplate
non funziona necessariamente come previsto.Utilizzo della deserializzazione del corpo WCF personalizzata senza modificare la deserializzazione del modello URI
Ecco l'implementazione fornita dal post del blog:
class NewtonsoftJsonDispatchFormatter : IDispatchMessageFormatter
{
private readonly OperationDescription od;
private readonly ServiceEndpoint ep;
private readonly Dictionary<string, int> parameterNames = new Dictionary<string, int>();
public NewtonsoftJsonDispatchFormatter(OperationDescription od, ServiceEndpoint ep, bool isRequest)
{
this.od = od;
this.ep = ep;
if (isRequest)
{
int operationParameterCount = od.Messages[0].Body.Parts.Count;
if (operationParameterCount > 1)
{
this.parameterNames = new Dictionary<string, int>();
for (int i = 0; i < operationParameterCount; i++)
{
this.parameterNames.Add(od.Messages[0].Body.Parts[i].Name, i);
}
}
}
}
public void DeserializeRequest(Message message, object[] parameters)
{
if (message.IsEmpty)
return;
object bodyFormatProperty;
if (!message.Properties.TryGetValue(WebBodyFormatMessageProperty.Name, out bodyFormatProperty) ||
(bodyFormatProperty as WebBodyFormatMessageProperty).Format != WebContentFormat.Raw)
{
throw new InvalidOperationException("Incoming messages must have a body format of Raw. Is a ContentTypeMapper set on the WebHttpBinding?");
}
XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
bodyReader.ReadStartElement("Binary");
byte[] rawBody = bodyReader.ReadContentAsBase64();
using (MemoryStream ms = new MemoryStream(rawBody))
using (StreamReader sr = new StreamReader(ms))
{
if (parameters.Length == 1)
parameters[0] = Helper.serializer.Deserialize(sr, od.Messages[0].Body.Parts[0].Type);
else
{
// multiple parameter, needs to be wrapped
using (Newtonsoft.Json.JsonReader reader = new Newtonsoft.Json.JsonTextReader(sr))
{
reader.Read();
if (reader.TokenType != Newtonsoft.Json.JsonToken.StartObject)
throw new InvalidOperationException("Input needs to be wrapped in an object");
reader.Read();
while (reader.TokenType == Newtonsoft.Json.JsonToken.PropertyName)
{
string parameterName = reader.Value as string;
reader.Read();
if (this.parameterNames.ContainsKey(parameterName))
{
int parameterIndex = this.parameterNames[parameterName];
parameters[parameterIndex] = Helper.serializer.Deserialize(reader, this.od.Messages[0].Body.Parts[parameterIndex].Type);
}
else
reader.Skip();
reader.Read();
}
}
}
}
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result) { ... }
}
In sostanza, il object[] parameters
nella firma DeserializeMethod
sono out
parametri che questo metodo ha bisogno di istanziare.
Quindi, questo fa un grande lavoro per gestire un endpoint REST come questo:
[WebInvoke(Method="POST", UriTemplate="foo/")]
public Foo MakeFoo(Foo foo) { ... }
o come questo:
[WebInvoke(Method="POST", UriTemplate="FooBar/")]
public FooBar FooBar(Foo foo, Bar bar) { .. }
ma attualmente non traccia i parametri di modello URI per la parametri del metodo, ad es qualcosa di simile:
[WebGet(UriTemplate="Foo/{id}")]
public Foo GetFoo(string id) { ... }
Microsoft scrive sul sovrascritto GetRequestDispatchFormatter
:
Questo è un punto di estensibilità che derivano comportamenti possono utilizzare per fornire la propria implementazione di IDispatchMessageFormatter che è chiamata a deserializzare i parametri di ingresso l'operazione di servizio dal messaggio di richiesta. I parametri specificati nel UriTemplate dell'operazione di servizio devono essere deserializzati dall'URL del messaggio di richiesta e gli altri parametri devono essere deserializzati dal corpo del messaggio di richiesta.
Quindi, fantastico. Ho aggiornato la deserializzazione dei parametri dal corpo di un messaggio. Ma non voglio scavalcare la deserializzazione dei parametri nel UriTemplate
. C'è un modo per usare il codice esistente per mappare la richiesta URI in entrata ai parametri con il modo predefinito UriTemplate
viene gestito?
Sembra che ho bisogno di utilizzare qualcosa come il UriTemplateDispatchFormatter
ma non sono sicuro di come implementarlo, ed è non pubblico.
approccio molto pragmatico! Una cosa da tenere a mente è che l'implementazione di @ carlosfigueira NewtonsoftJsonDispatchFormatter si aspetta che la prima parte del corpo sia il corpo del payload. Se il metodo è strutturato in modo tale che la parte del corpo non sia il primo argomento, risulterà probabilmente in un 'JsonReaderException'. – Tedford
Ciao, ho finito per avere lo stesso problema, è ancora la migliore soluzione disponibile?Ci sto provando ma non sono un fan della copia del codice dal framework nella mia soluzione, ma oh ... – Vinhent
@Vinhent la soluzione migliore potrebbe essere quella di utilizzare qualcosa di diverso da WCF per un servizio web RESTful, ma se è necessario attenersi alla WCF sì, lo è –