Thursday, August 20, 2015

Publish Posts on Blogger.com - API v3.0

In January I wrote a post about how to publish posts automatically on Blogger.com. This however used for that the API v2.0, which is now deprecated, therefore I here want to show how to do this with the current API v3.0.
Unfortunately I did not find a way (and I do not know if there is one) to really publish the posts without the user having to interact, now he has to log in in the browser and copy some code.
From a high level view the procedure in this version is as follows:
Via the Google Developer Console a new project has to be created and the Blogger API has to be enabled. Information about this step and the general usage of the API can be found here.
Then, to be able to use the API, we need to authenticate via OAuth 2.0. For this, we have to send a request to Google. We can do so by calling a specific URL in the browser, as parameters we specify amongst others the ID of our project, the scope for which we want to use the access etc. The user then logs in in the browser and a authorization code is presented. This code the program then sends via an HTTP request to Google and we now get back a token. With this we can eventually call the API and thus publish posts on Blogger. This procedure is decribed here.

After this high level overview, let us come to concrete implementation: First we create a new project in the Google Developer Console. Then we look for the API Blogger API v3 in the menu APIs and enable it.
Now, in our C# program, we first have to call an URL to do our initial request. The needed URL is https://accounts.google.com/o/oauth2/auth. One parameter we pass over is scope, which describes for which application we want to authenticate, for this we send https://www.googleapis.com/auth/blogger. The next parameter is the redirect URL (redirect_uri), which determines to where the answer is send. When setting this to urn:ietf:wg:oauth:2.0:oob the answer is shown in the opened browser. Via response_type=code we determine to get a code back. As the last parameter we set client_id to the ID of our created project in the Google Developer Console. This is NOT the Project ID which can be found on the mainpage, but to get the client id one has to navigate to APIs & auth - Credentials and then click on (if not already done) Add credentials - OAuth 2.0 client ID. Then we select Other (because we are designing a native application) and click on Create - then we get the client id.
All in all the URL to be called should look like this:

https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=client-id


We simply use Process.Start on the URL to start the default browser with it. In this, the user is presented with a login and consent screen. If he clicks accept, a success code is presented. We copy this.
With the code, we can get an access token for using the Blogger API. For this we now have to do a HTTP Post Request. The URL to be called is https://www.googleapis.com/oauth2/v3/token, as parameters we have to send the obtained code (code), the client id (client_id), the redirect url (redirect_uri, same as before), the grant type (grant_type=authorization_code - describes how we want to authenticate) and the client secret (client_secret). The latter we can see when clicking on the client id under Credentials.
With the method HTTPPost() presented in the linked post this looks as follows:

            string Code = "code=" + Code1 + "&";
            string ID = "client_id=id&";
            string uri = "redirect_uri=urn:ietf:wg:oauth:2.0:oob&";
            string grant = "grant_type=authorization_code&";
            string secret = "client_secret=secret";

            string Code2 = HTTPPost("https://www.googleapis.com/oauth2/v3/token", Code + ID + uri + grant + secret);

We read out the answer of the server since this contains (if successful) the access token. The answer is given in the JSON format, we use the library Newtonsoft.Json to interpret this. Maybe I will write a post about the library, for now I just refer to this post where it is also used.
Thus we obtain the access token via the following code, where AccessToken is a custom class with the desired attribute:

AccessToken JsonAccToken = (AccessToken)JsonConvert.DeserializeObject(Code2, typeof(AccessToken));
string StrAccToken = JsonAccToken.access_token;
With this token we can now use the API to publish posts on Blogger. We use a WebRequest to send the correct POST request to the Blogger server. As the target adress we select https://www.googleapis.com/blogger/v3/blogs/.
First we set the correct content type, select the authentication header etc:

var http = (HttpWebRequest)WebRequest.Create(new Uri("https://www.googleapis.com/blogger/v3/blogs/" + sid + "/posts/"));
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "POST";
http.Headers.Add("Authorization", "Bearer " + token);

sid is the ID of the blog to which we want to publish. Next we describe the post in JSON format, for that we first write it as a string and then convert it.
var vm = new { kind = "blogger#post", blog = new { id = sid }, title = stitle, content = scontent };
var dataString = JsonConvert.SerializeObject(vm);
string parsedContent = dataString;

In the code stitle denotes the title of the post, scontent the content (which is expected in the HTML format).
Eventually we upload this via the WebRequest.
The complete code looks as follows:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Net;
using System.IO;
using Newtonsoft.Json;

namespace Blogger
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/blogger&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=client-id");
        }

        private string HTTPPost(string url, string postparams)
        {
            string responseString = "";

            // performs the desired http post request for the url and parameters
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            // request.CookieContainer = Cookie; // explicitely use the cookiecontainer to save the session

            string postData = postparams;
            byte[] data = Encoding.UTF8.GetBytes(postData);

            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;

            using (var stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }

            var response = (HttpWebResponse)request.GetResponse();

            responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            return responseString;

        }

        private void Form1_Click(object sender, EventArgs e)
        {
            string Code = "code=" + textBox1.Text + "&";
            string ID = "client_id=client-id&";
            string uri = "redirect_uri=urn:ietf:wg:oauth:2.0:oob&";
            string grant = "grant_type=authorization_code&";
            string secret = "client_secret=secret";

            string Code2 = HTTPPost("https://www.googleapis.com/oauth2/v3/token", Code + ID + uri + grant + secret);

            AccessToken JsonAccToken = (AccessToken)JsonConvert.DeserializeObject(Code2, typeof(AccessToken));
            string StrAccToken = JsonAccToken.access_token;

            JSONPublish(BlogID, "Testpost", "This is a <b>Test</b>.", StrAccToken);
        }

        private void JSONPublish(string sid, string stitle, string scontent, string token)
        {
            var http = (HttpWebRequest)WebRequest.Create(new Uri("https://www.googleapis.com/blogger/v3/blogs/" + sid + "/posts/"));
            http.Accept = "application/json";
            http.ContentType = "application/json";
            http.Method = "POST";
            http.Headers.Add("Authorization", "Bearer " + token);

            var vm = new { kind = "blogger#post", blog = new { id = sid }, title = stitle, content = scontent };
            var dataString = JsonConvert.SerializeObject(vm);
            string parsedContent = dataString;

            Byte[] bytes = Encoding.UTF8.GetBytes(parsedContent);

            Stream newStream = http.GetRequestStream();
            newStream.Write(bytes, 0, bytes.Length);
            newStream.Close();

            var response = http.GetResponse();

            var stream = response.GetResponseStream();
            var sr = new StreamReader(stream);
            var content = sr.ReadToEnd();
        }

        public class AccessToken
        {
            [JsonProperty(PropertyName = "access_token")]
            public string access_token { get; set; }
        }
    }
}