読者です 読者をやめる 読者になる 読者になる

【C#】HttpClientのPOSTメソッドとGETメソッドのパラメータをお手軽に追加する

private static FormUrlEncodedContent CreatePostContent(params Expression<Func<Object, Object>>[] exprs)
{
    var contents = new List<KeyValuePair<string, string>>();
    foreach (var expr in exprs)
    {
        var obj = expr.Compile().Invoke(null);
        if (obj == null) continue;

        if (obj is Byte[])
        {
            contents.Add(new KeyValuePair<string, string>(expr.Parameters[0].Name, HttpUtility.UrlEncode((Byte[])obj)));
        }
        else
        {
            var param = obj.ToString();
            if (string.IsNullOrEmpty(param)) continue;
            contents.Add(new KeyValuePair<string, string>(expr.Parameters[0].Name, param));
        }
    }
    return new FormUrlEncodedContent(contents);
}

ラムダ式の変数名をキーに、そしてその式の値をValueにし、FormUrlEncodedContentとして返す。これがHttpClientのPostAsyncと非常に相性が良い。

using (var hc = new HttpClient())
{
    var result = hc.PostAsync(url,
        CreatePostContent(api_key => CONSUMER_KEY, offset => 20, type => "text", tag => "hoge")).Result;
}

調子に乗ってGETメソッドでもやろうとしたらFormUrlEncodedContentを引数にするオーバーロードがない。いや、そりゃ当たり前なんだけど。
なので先ほど見つけた方法を組み合わせて同じようなメソッドを作ってみる。

private static string CreateGetContent(params Expression<Func<Object, Object>>[] exprs)
{
    var contents = HttpUtility.ParseQueryString(string.Empty);
    foreach (var expr in exprs)
    {
        var obj = expr.Compile().Invoke(null);
        if (obj == null) continue;

        var param = obj.ToString();
        if (string.IsNullOrEmpty(param)) continue;

        contents[expr.Parameters[0].Name] = HttpUtility.UrlEncode(param);
    }
    return contents.ToString();
}

HttpUtility.ParseQueryStringから返される型はNameValueColectionであり、Dictionaryと言うか、Hashtableに近い形で使える。

呼び出し方はこう。普通のUriだとQueryは読み取り専用なのでUriBuilderを使う。

using (var hc = new HttpClient())
{
    var result = hc.GetAsync(new UriBuilder(url)
                                    {
                                        Query = CreateGetContent(api_key => CONSUMER_KEY, offset => 20, type => "text")
                                    }.ToString()).Result;
}