?
Solved

Why does C#.Net Web service soap extension return empty stream when expecting text/xml?

Posted on 2006-11-01
6
Medium Priority
?
16,486 Views
Last Modified: 2013-11-19
I've implemented a simple soap extension to intercept the servers outgoing soap message stream and replace it with my own (code below).

Everything seems to work in the code step through except the server side afterserialize stage is called twice (2 times!)  but why?  It should only fire once.  In the second fire, afterserialize writes an empty stream to the output stream, but I already wrote to the output stream correctly on the first fire! My client says, expectedly, the above error, the service returns nothing==empty stream.

The only post I found with an answer was this one: http://www.thescripts.com/forum/thread375954.html but I don't think it applies in my case as I rewound the stream correctly (or at least I think I did).

I'm using vs 2005 and I'm running on XP pro. I'm using the embedded webserver in 2005 (cassini?). C#

As I said, everything appears to work fine.  On the server side It takes the input message, transforms it, and then writes it to the output stream.  I checked the stream it copies to the output (in afterserialize on the first time it fires) and everything looks hunky-dory.  However, the second time afterserialize fires everything is messed up and the streams are empty (appoutputstream and httpoutputstream).


all code below here
--*********************
Here is the soap extension (put in app_code folder)
--**********************

using System;
using System.Web;
using System.Web.Services;
using System.IO;
using System.Web.Services.Protocols;
using System.Xml;

public class XmlStreamSoapExtension : System.Web.Services.Protocols.SoapExtension
    {
        bool output = false;
        Stream httpOutputStream;
        Stream chainedOutputStream;
        Stream appOutputStream;

        public override Stream ChainStream(Stream stream)
        {
            Stream result = stream;
            if ((output))
            {
                httpOutputStream = stream;
                chainedOutputStream = new MemoryStream();
                result = chainedOutputStream;
            }
            else
            {
                output = true;
            }
            return result;
        }

        public override object GetInitializer(System.Type serviceType)
        {
            return null;
        }

        public override object GetInitializer(System.Web.Services.Protocols.LogicalMethodInfo methodInfo, System.Web.Services.Protocols.SoapExtensionAttribute attribute)
        {
            return null;
        }

        public override void Initialize(object initializer)
        {
        }

        public override void ProcessMessage(SoapMessage message)
        {
            if (message.Stage == SoapMessageStage.AfterDeserialize)
            {
                HttpContext.Current.Request.InputStream.Position = 0;
                HttpContext.Current.Items["SoapInputStream"] = HttpContext.Current.Request.InputStream;
                appOutputStream = new MemoryStream();
                HttpContext.Current.Items["SoapOutputStream"] = appOutputStream;
            }
            else if (message.Stage == SoapMessageStage.AfterSerialize)
            {
                chainedOutputStream.Position = 0;
                XmlReader reader = new XmlTextReader(chainedOutputStream);
                reader.ReadStartElement("Envelope", "http://schemas.xmlsoap.org/soap/envelope/");
                reader.MoveToContent();
                if ((reader.LocalName == "Header"))
                {
                    reader.Skip();
                }
                reader.ReadStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/");
                reader.MoveToContent();
                if ((reader.LocalName == "Fault" & reader.NamespaceURI == "http://schemas.xmlsoap.org/soap/envelope/"))
                {                    
                  chainedOutputStream.Position = 0;
                  CopyStream(chainedOutputStream, httpOutputStream);
                }
                else
                {
                    appOutputStream.Flush();
                    appOutputStream.Position = 0;
                    CopyStream(appOutputStream, httpOutputStream);                    
                }
                appOutputStream.Close();
            }
        }

        private void CopyStream(Stream src, Stream dest)
        {
            StreamReader reader = new StreamReader(src);
            StreamWriter writer = new StreamWriter(dest);
            writer.Write(reader.ReadToEnd());
            writer.Flush();
        }

    public class SoapStreams
    {

        public static Stream InputMessage
        {
            get
            {
                return ((Stream)(HttpContext.Current.Items["SoapInputStream"]));
            }
        }

        public static Stream OutputMessage
        {
            get
            {
                return ((Stream)(HttpContext.Current.Items["SoapOutputStream"]));
            }
        }
    }
    [AttributeUsage(AttributeTargets.Method)]
    public class XmlStreamSoapExtensionAttribute : SoapExtensionAttribute
    {
        private int m_priority;
        public override Type ExtensionType
        {
            get{ return typeof(XmlStreamSoapExtension);}
        }

        public override int Priority
        {
            get {return m_priority;}
            set{m_priority = 1;}
        }
    }

--*********************
end of soap extension class
--**********************

--*********************
--here is the arithmetic.cs file --all it does is a simple transform of the input message (also in app_code directory)  the asmx file only has one line of codes (a directive saying: <%@ WebService class="arithmetic" %>)
--*********************

using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.Serialization;
[System.Web.Services.WebServiceBindingAttribute(Name = "Arithmetic", Namespace = "urn:msdn-microsoft-com:hows")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class arithmetic : WebService {

    private const string soapURI = "http://schemas.xmlsoap.org/soap/envelope/";
    private const string nsURI = "urn:msdn-microsoft-com:hows";

    [XmlStreamSoapExtension]
    [WebMethod]
    [SoapDocumentMethod("urn:msdn-microsoft-com:hows/Add", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Bare)]
    [return: XmlElement("AddResponse", Namespace = "urn:msdn-microsoft-com:hows")]
    public /* AddResponse */ void Add(/* Add Add1 */)
    {
        //************
        //this will transform the input stream (in a reader) directly to output stream
        //************

      XmlReaderSettings settings = new XmlReaderSettings();
        SoapStreams.InputMessage.Position = 0;
        XmlReader xReader = XmlReader.Create(new XmlTextReader(SoapStreams.InputMessage),settings);
        while (xReader.Read())
        {
        }
      XslCompiledTransform docXsl = new XslCompiledTransform();
        XsltSettings xslset = new XsltSettings();
        xslset.EnableDocumentFunction = true;
        string xslsource = "add.xslt";
        XmlUrlResolver resolver = new XmlUrlResolver();
        string pathxsl = HttpContext.Current.Server.MapPath(xslsource);
        docXsl.Load(new XmlTextReader(pathxsl), xslset, resolver);
        docXsl.Transform(xReader, new XmlTextWriter(SoapStreams.OutputMessage, System.Text.Encoding.UTF8));
    }
    public arithmetic () {
        //Uncomment the following line if using designed components
        //InitializeComponent();
    }

}

--***************************
--end of the arithmetic.cs file
--****************************


--**********************
--here is the xml message sent to the service by the client w/soap action of
--"urn:msdn-microsoft-com:hows/Add"
--which works fine as it fires the service up--you can just send this using Microsoft Fiddler
--************************

<?xml version='1.0' encoding='utf-8'?><soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'><soap:Body><Add xmlns='urn:msdn-microsoft-com:hows'><n1>10</n1><n2>20</n2></Add></soap:Body></soap:Envelope>

--*****************
--end of the input message
--*****************

--*****************
--here is the add.xslt file
--****************
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xsl:version="1.0">
<soap:Body>
<ns:AddResponse xmlns:ns="urn:msdn-microsoft-com:hows">
<ns:sum>
<xsl:value-of select="sum(//ns:Add/*/text())"/>
</ns:sum>
</ns:AddResponse>
</soap:Body>
</soap:Envelope>

--******************
--end of add.xslt file
--******************

--******************
--web config
--*******************

<webServices>
<soapExtensionTypes>
<add type="XmlStreamSoapExtension, App_Code" priority="1" group="High"/>
</soapExtensionTypes>
<protocols>
<remove name="HttpGet" />
<remove name="HttpPost" />
<remove name="Documentation" />
</protocols>
</webServices>

--*****************
--web config
--***************
0
Comment
Question by:tomandlis
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
6 Comments
 
LVL 4

Expert Comment

by:boy8964
ID: 17855891
your quesion is too long and proints are too short!
0
 

Author Comment

by:tomandlis
ID: 17856318
There, I increased the point value.  Sorry, but its all I've got and I've no inheritance to pass on.
0
 

Author Comment

by:tomandlis
ID: 17857142
Well, since nobody was up to the challenge (shame on you all) I had to figure it out myself.  The problem was in the configuration--I decorated the method with the soap extention, e.g., [XmlStreamSoapExtension] AND I also put it in the web.config under <soapextensiontypes>.  DOING BOTH IS BAD because of the following which I found at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconAlteringSOAPMessageUsingSOAPExtensions.asp

<clip>
If the SOAP extension is configured using an attribute, GetInitializer is called by the Web services infrastructure the first time a Web service method is accessed.

If the SOAP extension is configured in a configuration file, GetInitializer is called by the Web services infrastructure only the first time the entire Web service is accessed.
</clip>

If you do both GetInitializer is called twice (its not smart enough to know that isn't what you wanted) and thus you get parallel initializations and executions--one containing what you wanted to transmit from your webservice and the other containing nothing.  Unfortunately I was getting the nothing (or fortunately depending on how you look at it).

Once I removed either the method decoration attribute or the web.config soapextensiontype everything worked fine!  So there!

I'm hope this helps someone avoid 5 days of debugging (like what happened to me).
0
 

Author Comment

by:tomandlis
ID: 17881430
One other thing that tripped me up.  Remember, if you are using server side a soap extension then it will likely become your responsibility to write to the output stream.  I once tried to have the normal webservice pipeline serialize and return the output (during test) and I got an empty stream on the client side.  Duh!  you just wrote your stream to the chained output stream and your stream had nothing in it because your were trying to let the normal pipeline handle everything.
0
 
LVL 1

Accepted Solution

by:
Computer101 earned 0 total points
ID: 18410340
PAQed with points refunded (55)

Computer101
EE Admin
0

Featured Post

Get 15 Days FREE Full-Featured Trial

Benefit from a mission critical IT monitoring with Monitis Premium or get it FREE for your entry level monitoring needs.
-Over 200,000 users
-More than 300,000 websites monitored
-Used in 197 countries
-Recommended by 98% of users

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Preface This is the third article about the EE Collaborative Login Project. A Better Website Login System (http://www.experts-exchange.com/A_2902.html) introduces the Login System and shows how to implement a login page. The EE Collaborative Logi…
This article covers the basics of the Sass, which is a CSS extension language. You will learn about variables, mixins, and nesting.
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.
Suggested Courses

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question