posted on Monday, May 02, 2005 7:56 PM by kevdaly

Some interim Atom publishing code for Blogger

I still haven't tracked down the source of The Dreaded Supernumerary DIV, and unfortunately my day job has been intruding more into my spare time as we near the end of the current project and make the sprint for the finish line (last week I completed development of all major functionality for a our web-based booking system, and today I've started work on SAP integration for the financials. Yawwwwwwn). So I don't really have much to add Atom-wise. I also have to reinstall the February VS 2005 CTP before I can do any more with Avalon (and anything with Indigo, which I have yet to try), having had an, um, “industrial accident” with my existing installation (let's just say this morning I had the “I Really Wish I Hadn't Clicked That Button” blues). I'll deal with that over the next day or so.
So to keep things fresh, I decided to dish up some crappy code that's currently just sitting there getting stale.

So far for my Atom experiments with Blogger I've knocked together (sorry: “Carefully architected according to what demented blind sheep on medication could be bribed to agree were best practices”) a class to handle most of the heavy lifting, and another to represent an entry. Ta da. And a few other bits. The Entry class is currently just a data bucket: that will probably change somewhat before I'm finished - I'm inclined to make it handle its own reading and writing (I don't believe in doing that with database access, but I think it makes sense for XML serialisation and deserialisation. Sometimes). I haven't used the XmlSerializer for two reasons: firstly, I want this code to be usable from the current version of the .NET Compact Framework, which lacks serialisation support, and secondly, I frankly don't have the faintest idea (without expending actual effort) how to handle the custom serialisation of the date values.

I've therefore implemented the code for writing the entry using an XmlTextWriter (I haven't integrated the reading part with the Entry class at all yet).

Anyway, here's the Entry class:

using System;
using System.Xml;
using System.IO;
using System.Text;

namespace
DalySoftware //See remarks elswhere about the namespace. Sigh.
{
   
public class Entry
    {
        
private string title = string.Empty;
        
private DateTime issued = DateTime.Now;
       
private DateTime created = DateTime.Now;
       
private DateTime modified = DateTime.Now;
       
private string content;
       
private string entryId;

       
public Entry(){}

        public Entry(string title, string content)
        {
           
this.title = title;
            
this.content = content;
        }
   
    public Entry(string title, string content, DateTime issued):this(title, content)
        {
           
this.issued = issued;
        }

        public Entry(string title, string content, string issuedAsString):this(title, content)
        {
            
this.issued = DateTime.Parse(issuedAsString);
        }

        public string Title
        {
           
set {title = value;}
           
get {return title;}
        }
       
public DateTime Issue;
        {
           
set {issued = value;}
           
get {return issued;}
        }
       
public string Content
        {
           
set {content = value;}
           
get {return content;}
        }
        
public string EntryId
        {
            
set {entryId = value;}
           
get {return entryId;}
        }
    }

}

(Indenting as usual is crappy owing to the translation-to-web effect).

And here's the bit that does all the work (and I'm sorry, I'm just too sleepy to even try to repair the line spacing and indenting this time):

using System;

using System.Xml;

using System.Net;

using System.IO;

using System.Text;

namespace DalySoftware //usual tediously dorky namespace

{

public class AtomEngine

{

private string userName;

private string password;

private HttpStatusCode statusCode;

private AuthenticationType authenticationType;

public AtomEngine(string userName, string password):this(userName, password, AuthenticationType.Basic){}

public AtomEngine(string userName, string password, AuthenticationType authenticationType)

{

this.userName = userName;

this.password = password;

this.authenticationType = authenticationType;

}

public XmlDocument GetFeed(string feedUri)

{

return CallAtomHost(AtomMethod.Get,feedUri,null);

}

public XmlDocument PostEntry(string feedUri, Entry entry)

{

return CallAtomHost(AtomMethod.Post,feedUri,entry);

}

public XmlDocument PutEntry(string feedUri, Entry entry)

{

return CallAtomHost(AtomMethod.Put,feedUri,entry);

}

private XmlDocument CallAtomHost(string method, string uri, Entry entry)

{

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);

req.Method = method;

req.ProtocolVersion = new Version(1,1);

if(authenticationType==AuthenticationType.Basic)

{

req.Credentials = new NetworkCredential(userName,password);

}

if(method==AtomMethod.Post||method==AtomMethod.Put)

{

req.ContentType = "application/atom+xml";

}

req.Accept = "application/atom+xml";

req.PreAuthenticate = true;

req.AllowWriteStreamBuffering = true;

req.SendChunked = false;//for Blogger

if(entry!=null)

{

WriteEntry(entry, req.GetRequestStream());

}

XmlDocument response = new XmlDocument();

HttpWebResponse resp = null;

try

{

resp = (HttpWebResponse)req.GetResponse();

}

catch(WebException ex)

{

resp = (HttpWebResponse)ex.Response;

}

finally

{

if(resp.StatusCode==HttpStatusCode.OK)

{

XmlTextReader reader = new XmlTextReader(resp.GetResponseStream());

response.Load(reader);

reader.Close();

}

statusCode = resp.StatusCode;

resp.Close();

}

return response;

}

private void WriteEntry(Entry entry, Stream requestStream)

{

Encoding encoding = Encoding.UTF8;

MemoryStream stream = new MemoryStream();

XmlTextWriter writer = new XmlTextWriter(stream,encoding);

writer.Formatting = Formatting.None;

writer.WriteStartDocument(true);

writer.WriteStartElement("entry", "http://purl.org/atom/ns#");

writer.WriteStartElement("title");

writer.WriteAttributeString("mode","escaped");

writer.WriteAttributeString("type","text/plain");

writer.WriteString(string.Format("{0}",entry.Title));

writer.WriteEndElement();

writer.WriteElementString("issued", string.Format("{0:yyyy-MM-ddTHH:mm:sszzzz}",entry.Issued));

writer.WriteStartElement("generator");

writer.WriteAttributeString("url","http://kevdaly.aspxconnection.com");

writer.WriteString("Atomic Blogger");

writer.WriteEndElement();

writer.WriteStartElement("content");

writer.WriteAttributeString("type","application/xhtml+xml");

writer.WriteStartElement("div","http://www.w3.org/1999/xhtml");

writer.WriteRaw(entry.Content);

writer.WriteEndElement();

writer.WriteEndElement();

writer.WriteEndElement();

writer.WriteEndDocument();

writer.Flush();

requestStream.Write(stream.GetBuffer(),0,(int)stream.Length);

requestStream.Close();

stream.Close();

writer.Close();

}

public XmlDocument DeleteEntry(string feedUri)

{

return CallAtomHost(AtomMethod.Delete,feedUri,null);

}

public HttpStatusCode StatusCode

{

get {return statusCode;}

}

public AuthenticationType AuthenticationType

{

set {authenticationType = value;}

get {return authenticationType;}

}

}

}

The casually observant reader will note that this code does not use normal Atom authentication: since it's designed for Blogger, it assumes common old garden-variety Basic Authentication over SSL (I have made some small provision for imlementing Atom authentication in the future, however). At the moment it's pretty bare, and I'll probably be refactoring it quite drastically before I'm finished, but if you want to have a play around it does give you basic POST, GET, PUT and DELETE functionality.
Sorry again about the indenting (that's where cut and paste into HTML gets you. Yet again, *Sigh*).

Comments