はてなダイアリーにAtomPudで投稿する
1年以上前にはてなダイアリーに新しい記事を投稿するコードを書いたんですが、今回はてなダイアリーがAtomPub(Atom Publishing Protocol)に対応したと言うことで、急に新しくコードを書いてみた。
認証部分が「WSSE認証」になり、いきなり戸惑ったんだけど先駆者達のコードを参考に書いてみました。
とりあえずこれでPost出来ます。Postする時のXmlをXDocumentで作ろうとした形跡がありますが、XML宣言がXDocument(XElement)のsaveメソッドを読んだときにしか追加されない(オブジェクト自体に追加されるわけではなく、保存されたファイルに追加される)ので、結局StringBuilderでXMLを書いています。XDocumentの方から宣言を設定もしくは取得するプロパティも有るんだけど、それを使ってもダメ。これはどうすれば。。。
public string Post( string username, string password, string title, string content ) { string header = CreateWsseHeader( username, password ); // XML宣言を追加することが出来ない。。。 /* XNamespace ns = "http://purl.org.atom/ns#"; XDocument article = new XDocument( new XDeclaration( "1.0", "utf-8", "yes" ), new XElement( ns + "entry", new XElement( "title", "title2" ), new XElement( "content", new XAttribute( "type", "text/plain" ), "content2" ), new XElement( "updated", DateTime.Now.ToString( "o", new CultureInfo( "ja-jp" ) ) ) ) ); article.Declaration = new XDeclaration( "1.0", "utf-8", "no" ); */ StringBuilder xml = new StringBuilder(); xml.Append( "<?xml version=\"1.0\" encoding=\"utf-8\"?>" ); xml.Append( "<entry xmlns=\"http://purl.org/atom/ns#\">" ); xml.Append( "<title>" ); xml.Append( title ); xml.Append( "</title>" ); xml.Append( "<content type=\"text/plain\">" ); xml.Append( content ); xml.Append( "</content>" ); xml.Append( "<updated>" ); xml.Append( DateTime.Now.ToString( "o", new CultureInfo( "ja-jp" ) ) ); xml.Append( "</updated></entry>" ); HttpWebRequest request = ( HttpWebRequest )WebRequest.Create( "http://d.hatena.ne.jp/InoHiro/atom/blo request.Method = "POST"; request.Headers.Add( "X-WSSE", header ); request.ContentType = "application/x.atom+xml"; Stream requestream = request.GetRequestStream(); byte[] data = Encoding.UTF8.GetBytes( xml.ToString() ); requestream.Write( data, 0, data.Length ); requestream.Close(); HttpWebResponse response = ( HttpWebResponse ) request.GetResponse(); if( response.StatusCode == HttpStatusCode.Created ) return "created!"; else return "failed..."; } private string CreateWsseHeader( string username, string password ) { // HTTPリクエスト毎に生成するセキュリティ・トークン(ランダム文字列) byte[] b_nonce = new byte[8]; Random rand = new Random(); rand.NextBytes( b_nonce ); // nonce 生成時の日時 string created = DateTime.Now.ToUniversalTime().ToString( "o" ); byte[] b_created = Encoding.UTF8.GetBytes( created ); byte[] b_password = Encoding.UTF8.GetBytes( password ); SHA1Managed sh1 = new SHA1Managed(); sh1.Initialize(); // Nonce, Created, パスワードを連結 byte[] origin = new byte[b_nonce.Length + b_created.Length + b_password.Length]; Array.Copy( b_nonce, 0, origin, 0, b_nonce.Length ); Array.Copy( b_created, 0, origin, b_nonce.Length, b_created.Length ); Array.Copy( b_password, 0, origin, b_nonce.Length + b_created.Length, b_password.Length ); // ハッシュ値を生成 byte[] passwordDigest = sh1.ComputeHash( origin ); string header = string.Format( "UsernameToken Username=\"{0}\", PasswordDigest=\"{1}\", Nonce=\"{2}\", Created=\"{3}\"", username, Convert.ToBase64String( passwordDigest ), Convert.ToBase64String( b_nonce ), created ); return header; }
とりあえず今後編集(Put)とか削除(Delete)のところも書いていきたいなーと。実はこれまでのコードをクラスライブラリにして公開しようと思って、コードを書き直していたところだったので、残念。あとはRubyでも書いてみますかね。忘れないように。
まあ認証のところとか簡単になって、わかりやすくなったので良いと思う。
あと例外処理をちゃんと書いておかないと400が返ってきたときに例外で落ちる。
参考情報
C#
WSSE認証について。サンプルが欲しかったのでググったんですが、コードを公開してくださっていたので大変参考になりました。
C#でWSSE認証クライアント