Использование пользовательской десериализации тела WCF без изменения десериализации шаблона URI

Из этого сообщения в блоге мне удалось создать пользовательский WCF IDispatchMessageFormatter который использует сериализацию JSON.NET. Он отлично работает с одним предостережением: использование его с UriTemplate не обязательно работает UriTemplate образом.

Вот реализация, предоставленная блога:

 class NewtonsoftJsonDispatchFormatter : IDispatchMessageFormatter { private readonly OperationDescription od; private readonly ServiceEndpoint ep; private readonly Dictionary parameterNames = new Dictionary(); 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(); 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) { ... } } 

В основном, параметры object[] parameters в сигнатуре DeserializeMethodout параметры, которые этот метод должен создать.

Таким образом, это отличная работа для обработки конечной точки REST следующим образом:

 [WebInvoke(Method="POST", UriTemplate="foo/")] public Foo MakeFoo(Foo foo) { ... } 

или вот так:

 [WebInvoke(Method="POST", UriTemplate="FooBar/")] public FooBar FooBar(Foo foo, Bar bar) { .. } 

но в настоящее время он не отображает параметры шаблона URI для параметров метода, например, что-то вроде этого:

 [WebGet(UriTemplate="Foo/{id}")] public Foo GetFoo(string id) { ... } 

Microsoft пишет об GetRequestDispatchFormatter :

Это точка расширяемости, которую производные поведения могут использовать для предоставления собственной реализации IDispatchMessageFormatter, которая вызывается для десериализации входных параметров операции обслуживания из сообщения запроса. Параметры, указанные в UriTemplate операции службы, должны быть десериализованы из URI сообщения запроса, а другие параметры должны быть десериализованы из тела сообщения запроса.

Так здорово. Я обновил десериализацию параметров из тела сообщения. Но я не хочу переопределять десериализацию параметров в UriTemplate . Можно ли использовать существующий код для сопоставления входящего запроса URI с параметрами по умолчанию, с UriTemplate обрабатывается UriTemplate ?

Кажется, мне нужно использовать что-то вроде UriTemplateDispatchFormatter но я не уверен, как это реализовать, и он не является общедоступным.

Ну, это, наверное, самая смешная вещь, которую я должен был сделать, но копируя исходный код для UriTemplateDispatchFormatter , вы можете просто вернуть UriTemplateDispatchFormatter с «внутренним» IDispatchFormatter который соответствует приведенному здесь IDispatchFormatter . Не знаю, почему этот class был сделан внутренним> _>

следующее определение classа:

 class UriTemplateDispatchFormatter : IDispatchMessageFormatter { internal Dictionary pathMapping; internal Dictionary> queryMapping; Uri baseAddress; IDispatchMessageFormatter bodyFormatter; string operationName; QueryStringConverter qsc; int totalNumUTVars; UriTemplate uriTemplate; public UriTemplateDispatchFormatter(OperationDescription operationDescription, IDispatchMessageFormatter bodyFormatter, QueryStringConverter qsc, string contractName, Uri baseAddress) { this.bodyFormatter = bodyFormatter; this.qsc = qsc; this.baseAddress = baseAddress; this.operationName = operationDescription.Name; Populate( out this.pathMapping, out this.queryMapping, out this.totalNumUTVars, out this.uriTemplate, operationDescription, qsc, contractName); } public void DeserializeRequest(Message message, object[] parameters) { object[] bodyParameters = new object[parameters.Length - this.totalNumUTVars]; if (bodyParameters.Length != 0) { this.bodyFormatter.DeserializeRequest(message, bodyParameters); } int j = 0; UriTemplateMatch utmr = null; string UTMRName = "UriTemplateMatchResults"; if (message.Properties.ContainsKey(UTMRName)) { utmr = message.Properties[UTMRName] as UriTemplateMatch; } else { if (message.Headers.To != null && message.Headers.To.IsAbsoluteUri) { utmr = this.uriTemplate.Match(this.baseAddress, message.Headers.To); } } NameValueCollection nvc = (utmr == null) ? new NameValueCollection() : utmr.BoundVariables; for (int i = 0; i < parameters.Length; ++i) { if (this.pathMapping.ContainsKey(i) && utmr != null) { parameters[i] = nvc[this.pathMapping[i]]; } else if (this.queryMapping.ContainsKey(i) && utmr != null) { string queryVal = nvc[this.queryMapping[i].Key]; parameters[i] = this.qsc.ConvertStringToValue(queryVal, this.queryMapping[i].Value); } else { parameters[i] = bodyParameters[j]; ++j; } } } public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result) { throw new NotImplementedException(); } private static void Populate(out Dictionary pathMapping, out Dictionary> queryMapping, out int totalNumUTVars, out UriTemplate uriTemplate, OperationDescription operationDescription, QueryStringConverter qsc, string contractName) { pathMapping = new Dictionary(); queryMapping = new Dictionary>(); string utString = GetUTStringOrDefault(operationDescription); uriTemplate = new UriTemplate(utString); List neededPathVars = new List(uriTemplate.PathSegmentVariableNames); List neededQueryVars = new List(uriTemplate.QueryValueVariableNames); Dictionary alreadyGotVars = new Dictionary(StringComparer.OrdinalIgnoreCase); totalNumUTVars = neededPathVars.Count + neededQueryVars.Count; for (int i = 0; i < operationDescription.Messages[0].Body.Parts.Count; ++i) { MessagePartDescription mpd = operationDescription.Messages[0].Body.Parts[i]; string parameterName = XmlConvert.DecodeName(mpd.Name); if (alreadyGotVars.ContainsKey(parameterName)) { throw new InvalidOperationException(); } List neededPathCopy = new List(neededPathVars); foreach (string pathVar in neededPathCopy) { if (string.Compare(parameterName, pathVar, StringComparison.OrdinalIgnoreCase) == 0) { if (mpd.Type != typeof(string)) { throw new InvalidOperationException(); } pathMapping.Add(i, parameterName); alreadyGotVars.Add(parameterName, 0); neededPathVars.Remove(pathVar); } } List neededQueryCopy = new List(neededQueryVars); foreach (string queryVar in neededQueryCopy) { if (string.Compare(parameterName, queryVar, StringComparison.OrdinalIgnoreCase) == 0) { if (!qsc.CanConvert(mpd.Type)) { throw new InvalidOperationException(); } queryMapping.Add(i, new KeyValuePair(parameterName, mpd.Type)); alreadyGotVars.Add(parameterName, 0); neededQueryVars.Remove(queryVar); } } } if (neededPathVars.Count != 0) { throw new InvalidOperationException(); } if (neededQueryVars.Count != 0) { throw new InvalidOperationException(); } } private static string GetUTStringOrDefault(OperationDescription operationDescription) { string utString = GetWebUriTemplate(operationDescription); if (utString == null && GetWebMethod(operationDescription) == "GET") { utString = MakeDefaultGetUTString(operationDescription); } if (utString == null) { utString = operationDescription.Name; } return utString; } private static string MakeDefaultGetUTString(OperationDescription od) { StringBuilder sb = new StringBuilder(XmlConvert.DecodeName(od.Name)); //sb.Append("/*"); // note: not + "/*", see 8988 and 9653 if (!IsUntypedMessage(od.Messages[0])) { sb.Append("?"); foreach (MessagePartDescription mpd in od.Messages[0].Body.Parts) { string parameterName = XmlConvert.DecodeName(mpd.Name); sb.Append(parameterName); sb.Append("={"); sb.Append(parameterName); sb.Append("}&"); } sb.Remove(sb.Length - 1, 1); } return sb.ToString(); } private static bool IsUntypedMessage(MessageDescription message) { if (message == null) { return false; } return (message.Body.ReturnValue != null && message.Body.Parts.Count == 0 && message.Body.ReturnValue.Type == typeof(Message)) || (message.Body.ReturnValue == null && message.Body.Parts.Count == 1 && message.Body.Parts[0].Type == typeof(Message)); } private static void EnsureOk(WebGetAttribute wga, WebInvokeAttribute wia, OperationDescription od) { if (wga != null && wia != null) { throw new InvalidOperationException(); } } private static string GetWebUriTemplate(OperationDescription od) { // return exactly what is on the attribute WebGetAttribute wga = od.Behaviors.Find(); WebInvokeAttribute wia = od.Behaviors.Find(); EnsureOk(wga, wia, od); if (wga != null) { return wga.UriTemplate; } else if (wia != null) { return wia.UriTemplate; } else { return null; } } private static string GetWebMethod(OperationDescription od) { WebGetAttribute wga = od.Behaviors.Find(); WebInvokeAttribute wia = od.Behaviors.Find(); EnsureOk(wga, wia, od); if (wga != null) { return "GET"; } else if (wia != null) { return wia.Method ?? "POST"; } else { return "POST"; } } } 

наряду со следующим поведением:

 class NewtonsoftJsonBehavior : WebHttpBehavior { protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint) { return new UriTemplateDispatchFormatter( operationDescription, new NewtonsoftJsonDispatchFormatter(operationDescription, endpoint, true), GetQueryStringConverter(operationDescription), endpoint.Contract.Name, endpoint.Address.Uri); } protected override IDispatchMessageFormatter GetReplyDispatchFormatter(OperationDescription od, ServiceEndpoint ep) { return new NewtonsoftJsonDispatchFormatter(od, ep, false); } } 

работает

  • Лучший способ поддерживать данные «application / x-www-form-urlencoded» с WCF?
  • Сериализовать словарь .NET в JSON Key Value Pair Object
  • Использование MediaElement для воспроизведения видео из Stream
  • Чтение ввода файла из POST-файла с несколькими данными / формами данных
  • Служба проверки подлинности с использованием WCF
  • Конечные точки REST / SOAP для службы WCF
  • Content Type text / xml; charset = utf-8 не поддерживается службой
  • Как сообщить WCF о пропуске проверки сертификата?
  • Как использовать веб-службу WCF через URL-адрес во время выполнения?
  • Как передать lambda-выражение в службу WCF?
  • Как использовать службу WCF с Android
  • Давайте будем гением компьютера.