When i started out writing my new blogging system, two features I really wanted to support. One was writing the posts in Markdown, the other was to support submitting the posts using MetaWeblog so I could support writing the posts in editors. I had this feature back in the "good old days", when using a different blogging system.
Markdown I started supporting a while ago, now was the time to support MetaWeblog.
###MetaWeblog in C#### I started looking around the web; someone must have done a nice clean implementation of MetaWeblog (or at least XML-RPC!); however the only one I could find haven't been updated in more than a decade and has no support for using it directly in MVC.
I started building a simple parser and ended up extending it quite drastically to ensure it would conform with the XML-RPC standard.
Then on top of that, I build a custom XmlRpcResult class which implements FileResult from MVC. This allows me to make Actions in MVC returning a nice strongly-typed implementation of the object and do XML conversion as part of the framework.
###Kenc.XmlRpc Library### The end result? A small .net library compliant with XmlRpc. Got a few things to sort out first; then I will look into my options of supplying it on GitHub.
###Routing in MVC### The routing is not quite where I want it yet; in the future I will support the actual routing capabilities of MVC; for now all calls to the URL will automatically be forwarded to one controller.
/// <summary>
/// Initializes a new instance of the <see cref="MetaWeblogController"/> class.
/// </summary>
public MetaWeblogController()
{
this.actions = new Dictionary<string, Func<XmlRpcRequest, Task<object>>>
{
{ "newPost", this.NewPost },
{ "getPost", this.GetPost },
{ "getUsersBlogs", this.GetUsersBlogs },
{ "getRecentPosts", this.GetRecentPosts },
};
}
/// <summary>
/// Handle all requests and routing.
/// </summary>
/// <returns><see cref="Task{XmlRpcResult}"/>.</returns>
public async Task<XmlRpcResult> Index()
{
var deserializer = new XmlRpcRequestDeserializer();
var request = (XmlRpcRequest)deserializer.Deserialize(Request.GetBufferedInputStream());
var response = new XmlRpcResponse();
try
{
var func = this.actions.Where(x => x.Key == request.MethodName).Select(x => x.Value).FirstOrDefault();
if (func == null)
{
throw new MissingMethodException(request.MethodObject, request.MethodName);
}
response.Value = await func(request);
}
catch (XmlRpcException exception)
{
response.SetFault(exception.FaultCode, exception.FaultString);
}
catch (Exception)
{
response.SetFault(XmlRpcErrorCodes.APPLICATION_ERROR, XmlRpcErrorCodes.APPLICATION_ERROR_MSG);
}
return new XmlRpcResult(response);
}
Not the cleanest routing, but sufficient for now.
###Dealing with authentication### For my blog i use the capabilities of Owin; I have a local user which has a username and password, but primarily use my Microsoft account to login with. MetaWeblog only supports supplying username and password, so I had to do an implementation that ensured I would authenticate (since you can edit and create blog posts..)
/// <summary>
/// Authenticate the user for RPC.
/// </summary>
/// <param name="username">Supplied username.</param>
/// <param name="password">Supplied password.</param>
/// <returns>Returns a <see cref="Task"/> of the authentication.</returns>
private async Task Authenticate(string username, string password)
{
if (string.IsNullOrEmpty(username))
{
throw new ArgumentException("Username cannot be empty.", "username");
}
if (string.IsNullOrEmpty(password))
{
throw new ArgumentException("Password cannot be empty.", "password");
}
var signinManager = HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
var result = await signinManager.PasswordSignInAsync(username, password, false, shouldLockout: false);
if (result != SignInStatus.Success)
{
throw new Exception("Invalid login attempt.");
}
}
###Simple implementation### The above allows for super simple implementation of the supported calls.
/// <summary>
/// Retrieves a single post.
/// </summary>
/// <param name="request">An instance of the <see cref="XmlRpcRequest"/> request.</param>
/// <returns>An async task with the resulting object.</returns>
private async Task<object> GetPost(XmlRpcRequest request)
{
var username = (string)request.Params[1];
var password = (string)request.Params[2];
await this.Authenticate(username, password);
var guid = Guid.Parse((string)request.Params[0]);
var blogSystem = BlogController.GetBlogSystem();
var post = blogSystem.GetPost(guid);
return post.ToMetaWeblogDictionary();
}
I do expect to move the authentication part out of each function, as the MetaWeblog has consistency in which parameters are username and password.
###Enjoying the luxury of an editor### With all the above implemented, I can start using editors such as MarkPad
0 comments