Figure 1 PublishXML Web Method [WebMethod]
public string PublishXML(XmlElement[] contentItems) {
string results = contentItems.Length.ToString() + " documents
published successfully!";
try {
foreach (XmlElement docXML in contentItems) {
DocumentXML doc = new DocumentXML(docXML);
doc.SaveXML(); // validate and save the xml via our
// DocumentXML object
}
}
catch (Exception e) {
results = "Error: " + e.Message;
}
return results;
}
Figure 2 DocumentXML Class public class DocumentXML {
private string FileName; // member variable for the xml file name
private XmlElement Content; // member variable for the content to be
// saved to disk
public DocumentXML(XmlElement doc) {
// use the XmlTextReader to read the XmlElement argument to the
// constructor
XmlTextReader reader = new
XmlTextReader(doc.OuterXml.ToString(),
System.Xml.XmlNodeType.Element,null);
// Position the reader to the file name by reading to the
// <FileName> tag
while(reader.Read()) {
if ("FileName" == reader.Name) break;
}
// if a <FileName> element was not found, the reader will be at
// EOF
if (reader.EOF) {
throw new Exception("The required 'FileName' element was not
found");
}
// if a <FileName> element was found, read the file name and save
// the file name and the raw xml to member variables
else {
FileName = reader.ReadElementString("FileName");
// check to see that we have a non-zero length file name
// (additionally, this function could check to see if the
// file already exists)
if (FileName.Trim().Length > 0) {
Content = doc;
}
else {
throw new Exception("The required 'FileName' element was
empty.");
}
reader.Close();
}
}
public void SaveXML() {
// create the output file and write the xml content to the file
// NOTE: this will overwrite any existing file
XmlTextWriter writer = new
XmlTextWriter(ConfigurationSettings.AppSettings.Get("ContentDirectory")
+ FileName, System.Text.Encoding.Default);
writer.WriteRaw(Content.OuterXml.ToString());
// Close the writer and underlying file.
writer.Close();
}
}
Figure 4 PublishXML.xml SOAP Envelope <?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body> (SOAP envelope body)
<PublishXML xmlns="urn:webservices-paulsoftware-com-content"> (target
method)
<contentItems> (method argument name - one argument in this case)
<Any> (first array element, enclosing the first XML document)
<testDocument>
<FileName>test1.xml</FileName>
<info>
Any well formed xml can be included in the documents
published via the PublishXML method.
<moreinfo>Each document must have a 'FileName' element,
which specifies the file name that will be given to
the file created in the Content Directory
<moreinfo>Each 'document' must be enclosed in a single
xml element
(the 'testDocument' element, in this example).
</moreinfo>
</moreinfo>
</info>
</testDocument>
</Any>
<Any> (second array element, enclosing the second XML document)
<anotherDocument>
<FileName>test2.xml</FileName>
<also>Multiple documents can be published via a single
call to PublishXML.</also>
<note>Documents with the same FileName will be overwritten
in this example but the constructor of the DocumentXML
object could check for this condition.</note>
</anotherDocument>
</Any>
</contentItems>
</PublishXML>
</soap:Body>
</soap:Envelope>
Figure 5 The PublishXML Method public string Publish(Document[] contentItems) {
string results = contentItems.Length.ToString() + " documents
published successfully!";
try {
foreach (Document doc in contentItems) {
doc.Validate(); // Validate the content metadata
doc.Save(); // Ask each content item to save itself
// to disk
}
}
catch (Exception e) {
results = "Error: " + e.Message;
}
return results;
}
Figure 6 The Document Class public class Document
{
//============================================================
// Document constructor - empty
//============================================================
public Document() {
}
// Document metadata that could be stored in a database or index
// (document metadata is only provided as an example and is not
// processed)
public string Author;
public string Title;
public string Abstract;
public string[] Categories;
// the file name (including application extension) used to save the
// binary file in the target Content Directory
public string FileName;
// the base64 encoded binary data
public string Data;
public void Validate() {
if (FileName.Trim().Length == 0) {
throw new Exception ("FileName must be non-blank!);
}
}
public void Save() {
int base64len = 0;
byte[] base64 = new byte[1000];
// create a string Xml fragment called <Content></Content> and
// place the base64 encoded data in the fragment
string dataElement = "<Content>" + Data + "</Content>";
// use the XmlTextReader to read the fragment, and use its
// ReadBase64 method to decode the data
XmlTextReader reader = new XmlTextReader(dataElement,
System.Xml.XmlNodeType.Element,null);
// Position the reader to the base64 data by reading to the
// <Content> tag
while(reader.Read()) {
if ("Content" == reader.Name) break;
}
// we are positioned at the Base64 encoded content now, so:
// 1) create the output file
// (NOTE: if you pass zero length content, a zero length file is
// created)
FileStream fs = new
FileStream(ConfigurationSettings.AppSettings.Get("ContentDirectory")
+ this.FileName, FileMode.CreateNew, FileAccess.Write);
// 2) Create a binary writer, to write the decoded binary data to
// the file
BinaryWriter w = new BinaryWriter(fs);
// 3) read the first chunk of the content
base64len = reader.ReadBase64(base64,0,50);
while (0 != base64len) {
w.Write(base64,0,base64len);
base64len = reader.ReadBase64(base64,0,50);
}
// Close the Reader
reader.Close();
// Close the writer and underlying file.
w.Close();
}
}
Figure 7 Publish.xml SOAP Envelope <?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Publish xmlns="urn:webservices-paulsoftware-com-content">
<contentItems>
<Document>
<Author>Paula Paul</Author>
<Title>The Little Text Document</Title>
<Abstract>This is a test text document</Abstract>
<Categories>
<string>WebServices</string>
<string>Content</string>
</Categories>
<FileName>TestDoc.txt</FileName>
<Data>
VGhpcyBpcyBhIHNpbXBsZSB0ZXh0IGRvY3VtZW50Lg
</Data>
</Document>
<Document>
<Author>Paula Paul</Author>
<Title>The Little Word Document</Title>
<Abstract>This is a test Word document</Abstract>
<Categories>
<string>Webservices</string>
<string>Binary Content</string>
</Categories>
<FileName>TestDoc.doc</FileName>
<Data>
0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAKgAAAAAAAAAA
•••
</Data>
</Document>
</contentItems>
</Publish>
</soap:Body>
</soap:Envelope>
Figure 8 MIMEMessage, SOAP Envelope Plus Content MIME-Version: 1.0
Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml;
start="<soapenvelope.xml@paulsoftware.com>"
--MIME_boundary
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: 8bit
Content-ID: <soapenvelope.xml@paulsoftware.com>
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Publish xmlns="urn:webservices-paulsoftware-com-content">
<contentItems>
<Document>
<Author>Paula Paul</Author>
<Title>The Little Text Document</Title>
<Abstract>This is a test text document</Abstract>
<Categories>
<string>WebServices</string>
<string>Content</string>
</Categories>
<FileName>MIMEtesttext.txt</FileName>
<Data href="cid:MIMEtesttext.txt@paulsoftware.com"></Data>
</Document>
<Document>
<Author>Paula Paul</Author>
<Title>The Little Word Document</Title>
<Abstract>This is a test Word document</Abstract>
<Categories>
<string>Webservices</string>
<string>Binary Content</string>
</Categories>
<FileName>MIMEtestdoc.doc</FileName>
<Data href="cid:MIMEtestdoc.doc@paulsoftware.com"></Data>
</Document>
</contentItems>
</Publish>
</soap:Body>
</soap:Envelope>
--MIME_boundary
Content-Type: text/plain; charset=us-ascii; name="MIMEtesttext.txt"
Content-Transfer-Encoding: Base64
Content-ID: <MIMEtesttext.txt@paulsoftware.com>
VGhpcyBpcyBhIHNpbXBsZSB0ZXh0IGRvY3VtZW50Lg
--MIME_boundary
Content-Type: application/msword; name="MIMEtestdoc.doc"
Content-Transfer-Encoding: Base64
Content-ID: <MIMEtestdoc.doc@paulsoftware.com>
0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAKgAAAAAA
•••
--MIME_boundary
Figure 9 SOAPviaMIME SOAP Extension Class public class PublishViaMIME : SoapExtension {
Stream oldStream; // references to input and output streams
Stream newStream;
TextReader reader; // reader and writer are used to copy streams
TextWriter writer;
string MIMEBoundary = "";
string startContentID = "";
string SOAPenvelope = "";
•••
public override Stream ChainStream( Stream stream ) {
//save a reference to the input stream and the return stream as
//local member variables. The return stream will be modified in
//the ProcessMessage.BeforeDeserialize stage to replace the
// original input stream with a modified SOAP message stream.
switch (stream.GetType().ToString()) {
case "System.Web.HttpInputStream": // we want to modify the
// input
oldStream = stream;
newStream = new MemoryStream();
return newStream;
// and avoid messing with
// the output
case "System.Web.Services.Protocols.SoapExtensionStream":
return stream;
default:
throw new Exception ("Unknown Stream type in
ChainStream!");
}
}
public override void ProcessMessage(SoapMessage message) {
switch (message.Stage) {
// Intercept the request before it is deserialized. Handle the
// case where the SOAP envelope is part of a multipart MIME
// message. Otherwise, let the requst pass through normal
// serialization.
case SoapMessageStage.BeforeDeserialize:
if (isMultiPartMIME(message.ContentType)) {
//extract the SOAP envelope from the MIME Message
SOAPenvelope = getSOAPEnvelope(oldStream);
//extract the encoded content from the MIME message and
// insert the encoded content into the SOAP message
resolveContentRefs(oldStream);
//replace the original (MIME) stream with the SOAP
//message
replaceMessageStream(newStream);
//make sure we reset the request's content type, since
//it is no longer in "Multipart/Related" form
message.ContentType = "text/xml";
}
//if not a multipart message, pass the original stream
//through
else {
reader = new StreamReader(oldStream);
writer = new StreamWriter(newStream);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
newStream.Position = 0;
break;
case SoapMessageStage.AfterDeserialize:
break;
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
break;
default:
throw new Exception("invalid stage in ProcessMessage!");
}
}
|