In my continuing series on building my own Blog Application, I next turn to Windows Live Writer (WLW) support.
I have decided to implement WLW support before I even create a web-based editing experience. Primarily, this is because building the web-based editing experience is really just a matter of creating a standard MVC Form based UI, while building the WLW support will introduce new concepts. However, the other reason is that I want Naif.Blog to take WLW support seriously as a first class citizen, so the best way to do that is to do it first – I can have a completely working blog once this is complete.
Most other ASP.NET Blog applications, and I include in that list BlogEngine.NET, MiniBlog, as well as the core DNN Blog module, use the excellent Cook Computing Xml-Rpc library to handle the MetaWeblog API This library is essentially an Http Handler. I could use that same approach, but I would like to try and use an ASP.NET Core Web API service to handle the connection – i.e. do it the MVC way.
Before I dive into the solution, I would like to recognise the work of two people who have already looked at this for earlier versions of ASP.NET MVC. My solution is inspired by their earlier work with modifications to support the new ASP.NET Core APIs.
The first step to look at is routing. I would like to route the xml-rpc calls to a different action method based on the Xpl-Rpc methodCall. In Xml-Rpc the method (or action) is embedded in the body of the message, so we can’t use the standard ASP.NET Core Routing, which expects both controller and action name to be in the URL.
In ASP.NET Core we can extend the default template routing by implementing the IRouter and INamedRouter interfaces. In my case I am going to extend the default TemplateRoute and override the RouteAsync method.
The changes are fairly straightforward, I load the body of the Request (which is the Xml-Rpc packet) into an XDocument object and I retireve the name of the Xml-Rpc method that this packet represents. This is our action, so I add it to the RouteData dictionary together with the controller which is “MetaWeblog”. I then call the base class method to determine the correct Controller and Action to invoke.
In addition, I am also stashing the XDocument object that represents the body of the request into the HttpContext. This was not necessary in the previous MVC 5 based examples, but is necessary in ASP.NET Core as the Stream returned by the Request’s Body property is not seekable, so we can’t rewind the pointer later when we need to parse the body of the xml.
We next need to register this custom route, so in order to follow a similar pattern as used to register normal template based routes I have created a new extension method to the IRouteBuilder interface. This is shown below.
This extension is pretty simple. It just adds a MetaWebLogRoute object to the IRouteBuilder’s Routes collection. This new extension method can be added to the MVC configuration in StartUp.cs
So that's the routing set up. We can now route to the appropriate action method in the MetaWeblogController class.
The next step is to bind the Xml-Rpc packet to the appropriate parameters of our action method. There are a number of ways we could do this. Jono in his earlier work uses an Action Filter to map the Xml-Rpc fields to the action methods parameters, while Michael McKenna uses Model Binding – you could also use a Media Formatter.
I am going to use the Action Filter approach – mainly, because there is very little documentation on how to configure custom model binding or media formatters in ASP.NET Core.
The XmlRpcServiceAttribute class is shown below:
We start by retrieving the XDocument that we stashed in the context earlier. We then parse over the collection of parameters for the action method, find the relevant value node, deserialize the node and assign it to the relevant Action Argument. I have used the XmlRpcData class that Michael McKenna used in his earlier example. As this post is really about the intricacies of serializing and deserializing Xml-Rpc packets, the interested reader can view the code either in Michael McKenna’s Github repository or in the source for this project.
So far we have parsed the Xml-Rpc packet to determine the Controller and Action method to use and we have deserialized the values to bind to our action method’s arguments. All we have to do now to complete the cycle is allow our action methods to return an Xml-Rpc response packet.
To do that I have created an XmlRpcResult class.
Again, this class is pretty straightforward. It inherits from the base ContentResult class, sets the ContentType to “text/xml” and serializes any data using the XmlRpcData class.
Now with everything in place we can actually create our MetaWeblogController’s action methods. In order to configure WLW to Connect to our blog we need to implment the GetUsersBlogs method. This is shown below.
In the source code for this version, I have also created two other action methods GetRecentPosts and GetPost. These two methods allow WLW to download recent posts from the blog site.
The source for Naif.Blog can be found at https://github.com/cnurse/Naif.Blog. If you want to find the state of the repository used in this post then you can find that at the tagged release v0.0.2 (https://github.com/cnurse/Naif.Blog/releases/tag/v0.0.2)