Client found response content type of '', but expected 'text/xml'

H

hugedifference

On day 3 of debugging this. 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 afaik. 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.

I've found many hits on the net, but no answers. Day 4 is coming.
Argggh! 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?)

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
--**********************

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 asmx file --all it does is a simple transform of the
input message
--*********************

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
//************
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 asmx 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
--************************

<?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
--******************
 
H

hugedifference

sorry forgot to include the xreader creation in the asmx code file.
Here it is, insert this into the asmx code before you use the xreader

XmlReaderSettings settings = new XmlReaderSettings();
SoapStreams.InputMessage.Position = 0;
XmlReader xReader = XmlReader.Create(new
XmlTextReader(SoapStreams.InputMessage),settings);
while (xReader.Read())
{
}
 
H

hugedifference

could this be due to the new plumbing in vs 2005 webservices? It wants
all the code in the app_code directory and maybe when you put classes
in there they don't know what is really going on, i.e., in the process
they are losing their connection to the webservices http pipeline?
maybe there is a parallel pipeline created that is running too and that
is why I hit afterserialize twice?

btw, in case you didn't figure it out, all the .cs code for this app is
in the app_code directory except for the asmx file (even the asmx.cs is
in app_code) per the recommendation of VS when you create these
classes.

If app_code classes are running in a parallel (disconnected) process
then why do my soap exceptions work? For example, if I purposely put a
exception in the asmx.cs file, e.g,, int x = 1/0;, then the
afterserialize event sees the exception and correctly sends the regular
output stream (do you call this the chained stream?) and not my
appoutputstream.
 
H

hugedifference

BTW, the only changes I made to the web.config are below. I really
don't like using 'App_Code' as the class for my soap extension type,
but that is what it took to make it work and apparently that is what VS
2005 wants you to do, i.e., put everything in that directory.

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,705
Latest member
Stefkari24

Latest Threads

Top