2020/03/04
デッドロックする可能性があったので一部修正しました。
2020/03/11
WordPressの記事一覧取得が10記事までしか取得できなかったので、すべての記事を取得するように修正しました。WPService.cs
リンクはこちら
#シリーズ
Qiitaに投稿した記事のリンクを楽にWordPressに送りたい #1
Qiitaに投稿した記事のリンクを楽にWordPressに送りたい #3
Qiitaに投稿した記事のリンクを楽にWordPressに送りたい #4
#環境
IDE:VisualStudio2019
アプリケーション:コンソールアプリ
フレームワーク:.NET Core 3.1
#アプリケーションパスワードを有効化してAPIをコールしてみたが・・・
前回の続きになります。
WordPressの環境を借りている方に、アプリケーションパスワードの設定を行ってもらった。
以下サイトを参考に設定してもらった。
WordPressのRESTAPIでアプリケーションパスワードの認証がとおらないなら
更新をやってみたところ、まだエラーが起きる。401のエラーであり、権限がないとのこと。
以下で追加した名前と発行されるパスワードを「名前:パスワード」の形で設定しているのになぜエラーに・・・
#TryAddWithoutValidationが動作していない
以下の戻り値がfalseになっており、Authorizationヘッダーがうまく付与されていなかった。
content.Headers.TryAddWithoutValidation("Authorization", $"Basic {credentials}");
HttpRequestMessageを使用するらしい
調べると以下のようにHttpRequestMessageを使用して、ヘッダーに追加するとよいらしい。
public async Task<bool> UpdateWPArticle(string id, string json)
{
var request = new HttpRequestMessage(HttpMethod.Post, URL_FIRST + string.Format(UPDATE_ARTICLE, id));
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes("名前:パスワード"));
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
request.Headers.Add("Authorization", $"Basic {credentials}");
var result = await HpClient.SendAsync(request);
if (result.StatusCode == HttpStatusCode.OK)
{
return true;
}
else
{
return false;
}
}
#HttpRequestMessageを使用してもまだエラーがでる
HttpRequestMessageを使用しても依然エラーとなる。
調べてみるとどうやら
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes("名前:パスワード"));
の「名前:パスワード」は誤りで、「WordPressのログインユーザーID:パスワード」らしい。
しかも、パスワードは「c8cO ABCD UUb9 7DDX Rq22 1shl」のように空白区切りで発行されるが、そのまま使用するらしい。
空白は除去しないで設定することでうまくいった。
めちゃめちゃわかりにくい!!!!!
例
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes("user01:c8cO ABCD UUb9 7DDX Rq22 1shl"));
#記事の更新、追加
認証に成功したので、記事の更新/追加の処理を実装し、全体的に修正しました。
QiitaService
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
namespace TestProject.QiitaToWP
{
/// <summary>
/// QiitaAPI呼び出しクラス
/// </summary>
public class QiitaService : ServiceBase
{
/// <summary>
/// TOPURL
/// </summary>
private const string TOP_URL = "https://qiita.com";
/// <summary>
/// 記事一覧取得定義
/// </summary>
private const string GET_ARTICLELIST = "/api/v2/users/{0}/items";
/// <summary>
/// 記事一覧取得
/// </summary>
/// <param name="param">パラメータ群</param>
/// <returns>返り値JSON配列</returns>
public override async Task<JArray> GetArticleList(params string[] param)
{
var body = await HpClient.GetStringAsync(TOP_URL + string.Format(GET_ARTICLELIST, param[0]));
return JArray.Parse(body);
}
}
}
WPService
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace TestProject.QiitaToWP
{
/// <summary>
/// WordPress API呼び出しクラス
/// </summary>
public class WPService : ServiceBase
{
/// <summary>
/// TOPURL
/// </summary>
private const string TOP_URL = "http://kurosu.s1009.xrea.com";
/// <summary>
/// 記事取得、投稿時の定義
/// </summary>
private const string POSTS = "/wp-json/wp/v2/posts";
/// <summary>
/// 記事更新時の定義
/// </summary>
private const string UPDATE_POSTS = "/wp-json/wp/v2/posts/{0}";
/// <summary>
/// アプリケーションキー(Basic認証に使用するキー)
/// </summary>
private readonly string apllicationKey;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="applicationKey">アプリケーションキー(Basic認証に使用するキー)</param>
public WPService(string applicationKey) : base()
{
this.apllicationKey = applicationKey;
}
/// <summary>
/// 記事一覧取得
/// </summary>
/// <param name="param">パラメータ群</param>
/// <returns>返り値JSON配列</returns>
public override async Task<JArray> GetArticleList(params string[] param)
{
var body = await HpClient.GetStringAsync(TOP_URL + POSTS);
return JArray.Parse(body);
}
/// <summary>
/// 記事更新
/// </summary>
/// <param name="id">記事ID</param>
/// <param name="json">更新情報JSON</param>
/// <returns>処理の成否</returns>
public async Task<bool> UpdateWPArticle(string id, object json)
{
var request = this.CreateHttpRequestMessage(HttpMethod.Post, TOP_URL + string.Format(UPDATE_POSTS, id));
request.Content = new StringContent(JsonConvert.SerializeObject(json), Encoding.UTF8, "application/json"); ;
// リクエスト
var result = await HpClient.SendAsync(request);
if (result.StatusCode == HttpStatusCode.OK)
{
return true;
}
else
{
// エラーメッセージ
var resultJson = JObject.Parse(await result.Content.ReadAsStringAsync());
Console.WriteLine(resultJson["message"].ToString());
return false;
}
}
/// <summary>
/// 記事追加
/// </summary>
/// <param name="json">記事情報JSON</param>
/// <returns>処理の成否</returns>
public async Task<bool> InsertWPArticle(object json)
{
var request = this.CreateHttpRequestMessage(HttpMethod.Post, TOP_URL + POSTS);
request.Content = new StringContent(JsonConvert.SerializeObject(json), Encoding.UTF8, "application/json");
// リクエスト
var result = await HpClient.SendAsync(request);
if (result.StatusCode == HttpStatusCode.Created)
{
return true;
}
else
{
// エラーメッセージ
var resultJson = JObject.Parse(await result.Content.ReadAsStringAsync());
Console.WriteLine(resultJson["message"].ToString());
return false;
}
}
/// <summary>
/// HttpRequestMessage作成
/// </summary>
/// <param name="method">Httpメソッド</param>
/// <param name="url">URL</param>
/// <returns>HttpRequestMessage</returns>
private HttpRequestMessage CreateHttpRequestMessage(HttpMethod method, string url)
{
var request = new HttpRequestMessage(method, url);
// Basi認証ヘッダー
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(this.apllicationKey));
request.Headers.Add("Authorization", $"Basic {credentials}");
return request;
}
}
}
Qiita2WP
using System.Linq;
using System.Threading.Tasks;
namespace TestProject.QiitaToWP
{
/// <summary>
/// QiitaToWordPressクラス
/// </summary>
public class Qiita2WP
{
/// <summary>
/// QiitaからWordPressに記事を転送
/// </summary>
/// <returns>Task</returns>
public async Task Qiita2WPArticle()
{
// Qiita記事取得
var qiitaService = new QiitaService();
var qiitaList = await qiitaService.GetArticleList("GodPhwng");
// WP記事取得
var wpService = new WPService("ユーザID:アプリケーションパスワード");
var wpList = await wpService.GetArticleList();
foreach (var qiita in qiitaList)
{
var url = qiita["url"].ToString();
var title = qiita["title"].ToString();
// Qiitaの記事URLが含まれる物を取得
var matchArticle = wpList.FirstOrDefault(w => w["content"]["rendered"].ToString().Contains(url));
// リクエストBody作成
var json = new
{
// 公開範囲
status= "publish",
// タイトル
title = title,
// 本文
content = $"\n<p>{title}<a href=\"{url}\">{url}</a></p>\n"
};
if (matchArticle != null)
{
// 更新
await wpService.UpdateWPArticle(matchArticle["id"].ToString(), json);
}
else
{
// 新規追加
await wpService.InsertWPArticle(json);
}
}
}
}
}
メインクラス
using System;
using System.Threading.Tasks;
namespace TestProject.QiitaToWP
{
/// <summary>
/// メインクラス
/// </summary>
public class Program
{
/// <summary>
/// メインエントリ
/// </summary>
/// <param name="args">実行時引数</param>
/// <returns>Task</returns>
public static async Task Main(string[] args)
{
try
{
var q2wp = new Qiita2WP();
// Qiitaの記事をWordPressに反映
await q2wp.Qiita2WPArticle();
Console.WriteLine("処理終了");
}
catch (Exception err)
{
Console.WriteLine(err.Message);
}
finally
{
Console.Read();
}
}
}
}
また、既存の記事は更新されています。
気になるのはQiitaの記事投稿順になっていないことです。
WordPress側で記事作成処理が非同期で動いてそうです。
#GitHubのソースリンク
TestProject
#次回はタグを自動設定
現状ではタグが何も設定されていないので、Qiitaから読み取ったタグを連携してみようとおもいます。
→次回