locked
Nested WCF calls failing RRS feed

  • Question

  • I have a console app that is both a WCF service host (net.pipe binding) and a client for a different service (non-WCF RESTful web service using WebChannelFactory).  I have a test console client which makes a call to a method on the WCF service which in turn calls a method on the non-WCF web service.  The calls to the non-WCF service work when called independently of the hosted WCF service but when they are made from within the hosted WCF service method I get an exception - 'System.Xml.XmlException: Unexpected end of file' when I call the method on the non-WCF proxy (see below for the stack trace from the exception).  Enabling tracing of the service shows a Warning - 'Failed to send request message over HTTP' when the RESTful web service is called.

    Does anyone have any idea what's happening and why? If not, some thoughts on  what else to look at would be appreciated.

    Server stack trace:
       at System.Xml.EncodingStreamWrapper.ProcessBuffer(Byte[] buffer, Int32 offset, Int32 count, Encoding encoding)
       at System.Xml.XmlUTF8TextReader.SetInput(Byte[] buffer, Int32 offset, Int32 count, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
       at System.Xml.XmlDictionaryReader.CreateTextReader(Byte[] buffer, Int32 offset, Int32 count, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
       at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset)
       at System.ServiceModel.Channels.WebMessageEncoderFactory.WebMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset)
       at System.ServiceModel.Channels.HttpOutput.SerializeBufferedMessage(Message message)
       at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
       at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
       at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
       at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

    Exception rethrown at [0]:
       at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       at Facebook.IFacebookAPI.AuthCreateToken()
       at Facebook.FacebookSession.GetAuthToken() in Q:\Projects\Photato2\Facebook\Facebook.cs:line 71
    Wednesday, May 20, 2009 4:45 PM

Answers

  • Ok, I have solved this with the help of a previous forum post

    For some reason the OutgoingWebRequestContext.Method property was defaulting to 'POST' rather than 'GET' when being called as a result of the incoming service request.  The method was annotated with the WebGetAttribute which was correctly being applied when the method was called directly, but not apparently when being called from the incoming request.  Here is the modified code:

                AuthCreateTokenResponse tokenResponse = new AuthCreateTokenResponse();
    
                using (WebChannelFactory<IFacebookAPI> channel = new WebChannelFactory<IFacebookAPI>(_RestServerUri))
                {
                    channel.Endpoint.Behaviors.Add(new FacebookBehavior());
                    IFacebookAPI proxy = channel.CreateChannel();
    
                    using (new OperationContextScope((IContextChannel)proxy))
                    {
                        OutgoingWebRequestContext ctx = WebOperationContext.Current.OutgoingRequest;
                        ctx.Method = "GET";
    
                        try
                        {
                            tokenResponse = proxy.AuthCreateToken();
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine(e.Message);
                        }
                    }
                }

    • Edited by mthornal Thursday, May 21, 2009 11:33 AM
    • Marked as answer by mthornal Thursday, May 21, 2009 11:34 AM
    Thursday, May 21, 2009 11:31 AM

All replies

  • Is the code that the service uses to call te REST service which doesn;t work the same as the code used to call teh REST service directly when it does work? Can you show us te code you use to invoke the REST service?
    Richard Blewett, thinktecture - http://www.dotnetconsult.co.uk/weblog2
    Twitter: richardblewett
    Wednesday, May 20, 2009 4:56 PM
  • Yes, I call the same method in each case.

    Below is the code where I make the call that works on its own but fails with the exception when made as a result of an incoming call to the console app:

                AuthCreateTokenResponse tokenResponse = new AuthCreateTokenResponse();
    
                using (WebChannelFactory<IFacebookAPI> channel = new WebChannelFactory<IFacebookAPI>(_RestServerUri))
                {
                    channel.Endpoint.Behaviors.Add(new FacebookBehavior());
                    IFacebookAPI proxy = channel.CreateChannel();
    
                    try
                    {
                        tokenResponse = proxy.AuthCreateToken();
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine(e.Message);
                    }
                }
    

    The FacebookBehavior class derives from WebHttpBehavior and overrides GetRequestClientFormatter and GetReplyClientFormatter returning a custom IClientMessageFormatter which provides specific implementations of SerializeRequest and DeserializeReply.  The implementations of these methods are below:

            public object DeserializeReply(System.ServiceModel.Channels.Message message, object[] parameters)
            {
                MessageBuffer mb = message.CreateBufferedCopy(1048576); // 1MB
    
                try
                {
                    object o = _DefaultMessageFormatter.DeserializeReply(mb.CreateMessage(), parameters);
                    return o;
                }
                catch (Exception)
                {
                }
    
                Debug.Write(message.ToString());
    
                XmlSerializer xs = new XmlSerializer(typeof(ErrorResponse));
                ErrorResponse er = xs.Deserialize(mb.CreateMessage().GetReaderAtBodyContents()) as ErrorResponse;
    
                if (er != null)
                {
                    Debug.Write(er.ErrorMessage);
                    //TODO: handle exceptions
                    //throw new FacebookException(er.ErrorMessage);
                }
    
                return null;
            }
    
    
            public System.ServiceModel.Channels.Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
            {
                System.ServiceModel.Channels.Message m = _DefaultMessageFormatter.SerializeRequest(messageVersion, parameters);
    
                // Add additonal standard parameters to Uri here
                // i.e. call_id, v, api_key, session_key, sig
    
                string query = m.Headers.To.Query;
                SortedDictionary<string, string> sd = new SortedDictionary<string, string>();
    
                if (query != String.Empty)
                {
                    query = query.TrimStart("?".ToCharArray());
                    string[] args = query.Split("&".ToCharArray());
    
                    foreach (string s in args)
                    {
                        string key = s.Split("=".ToCharArray())[0];
                        string value = s.Split("=".ToCharArray())[1];
    
                        key = HttpUtility.UrlDecode(key);
                        value = HttpUtility.UrlDecode(value);
    
                        sd.Add(key, value);
                    }
                }
    
                UriBuilder ub = new UriBuilder(m.Headers.To);
                ub.Query = GenerateQueryString(sd);
                m.Headers.To = ub.Uri;
    
                return m;
            }

    • Edited by mthornal Wednesday, May 20, 2009 8:52 PM
    Wednesday, May 20, 2009 8:38 PM
  • Ok, I have solved this with the help of a previous forum post

    For some reason the OutgoingWebRequestContext.Method property was defaulting to 'POST' rather than 'GET' when being called as a result of the incoming service request.  The method was annotated with the WebGetAttribute which was correctly being applied when the method was called directly, but not apparently when being called from the incoming request.  Here is the modified code:

                AuthCreateTokenResponse tokenResponse = new AuthCreateTokenResponse();
    
                using (WebChannelFactory<IFacebookAPI> channel = new WebChannelFactory<IFacebookAPI>(_RestServerUri))
                {
                    channel.Endpoint.Behaviors.Add(new FacebookBehavior());
                    IFacebookAPI proxy = channel.CreateChannel();
    
                    using (new OperationContextScope((IContextChannel)proxy))
                    {
                        OutgoingWebRequestContext ctx = WebOperationContext.Current.OutgoingRequest;
                        ctx.Method = "GET";
    
                        try
                        {
                            tokenResponse = proxy.AuthCreateToken();
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine(e.Message);
                        }
                    }
                }

    • Edited by mthornal Thursday, May 21, 2009 11:33 AM
    • Marked as answer by mthornal Thursday, May 21, 2009 11:34 AM
    Thursday, May 21, 2009 11:31 AM
  • Thank you.  This was killing me and I wasn't able to see the traffic via fiddler2.  You rock, sir.
    Monday, April 11, 2011 11:54 AM