Kenc.dk
Screenshot of Visual Studio showing a C# interface

Supporting MetaWeblog

| 1 Comments | words | minutes | Permalink

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

Writing this post in MarkPad

0 comments

Add comment