Using Custom WCF Body Deserialization without changing URI Template Deserialization

前端 未结 1 1436
广开言路
广开言路 2020-12-01 21:46

From this blog post, I was able to create a custom WCF IDispatchMessageFormatter that uses JSON.NET serialization. It works great with one caveat: using it with

1条回答
  •  鱼传尺愫
    2020-12-01 22:39

    Well, this is maybe the most ridiculous thing I've had to do, but copying the source code for UriTemplateDispatchFormatter, you can simply return a UriTemplateDispatchFormatter with an "inner" IDispatchFormatter that corresponds to the IDispatchFormatter I provided here. Not sure why this class was made internal >_>

    the following class definition:

    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";
            }
        }
    
    }
    

    along with the following behavior:

    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);
        }
    
    }
    

    works

    0 讨论(0)
提交回复
热议问题