从这篇博客文章中,我能够创建一个IDispatchMessageFormatter使用JSON.NET序列化的自定义WCF 。需要注意的是,它的效果很好:与一起使用UriTemplate并不一定能按预期工作。
IDispatchMessageFormatter
UriTemplate
这是博客文章提供的实现:
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) { ... } }
基本上,object[] parameters在DeserializeMethod签名是out参数,该方法需要实例化。
object[] parameters
DeserializeMethod
out
因此,这在处理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) { ... }
微软写在重写GetRequestDispatchFormatter:
GetRequestDispatchFormatter
这是一个扩展点,派生的行为可以用来提供自己的IDispatchMessageFormatter实现,该实现被调用以从请求消息中反序列化服务操作的输入参数。必须从请求消息的To URI中反序列化服务操作的UriTemplate中指定的参数,并且必须从请求消息的主体中反序列化其他参数。
很好。我更新了消息正文中参数的反序列化。但是我不想重写。中的反序列化参数UriTemplate。是否可以使用现有代码以默认方式UriTemplate处理传入的URI请求到参数的方法?
似乎我需要使用类似的东西,UriTemplateDispatchFormatter但是我不确定如何实现它,并且它是非公开的。
UriTemplateDispatchFormatter
好吧,这也许是我要做的最荒谬的事情,但是复制的源代码UriTemplateDispatchFormatter,您可以简单地返回一个UriTemplateDispatchFormatter带有“ inner” IDispatchFormatter(与IDispatchFormatter我在此处提供的代码相对应)的a 。不确定为什么将此类设为内部> _>
IDispatchFormatter
下面的类定义:
class UriTemplateDispatchFormatter : IDispatchMessageFormatter { internal Dictionary<int, string> pathMapping; internal Dictionary<int, KeyValuePair<string, Type>> 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<int, string> pathMapping, out Dictionary<int, KeyValuePair<string, Type>> queryMapping, out int totalNumUTVars, out UriTemplate uriTemplate, OperationDescription operationDescription, QueryStringConverter qsc, string contractName) { pathMapping = new Dictionary<int, string>(); queryMapping = new Dictionary<int, KeyValuePair<string, Type>>(); string utString = GetUTStringOrDefault(operationDescription); uriTemplate = new UriTemplate(utString); List<string> neededPathVars = new List<string>(uriTemplate.PathSegmentVariableNames); List<string> neededQueryVars = new List<string>(uriTemplate.QueryValueVariableNames); Dictionary<string, byte> alreadyGotVars = new Dictionary<string, byte>(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<string> neededPathCopy = new List<string>(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<string> neededQueryCopy = new List<string>(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<string, Type>(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<WebGetAttribute>(); WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>(); 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<WebGetAttribute>(); WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>(); 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); } }
作品