231

System.Net.HttpClient を使用して http get リクエストを送信したい場合、パラメータを追加する API がないようですが、これは正しいですか?

名前値のコレクションとそれらをエンコードする URL を作成し、最後にそれらを連結することを伴わない、クエリ文字列を作成するために使用できる単純な API はありますか? RestSharp の API (つまり、AddParameter(..)) のようなものを使用したいと思っていました。

4

15 に答える 15

381

System.Net.HttpClient を使用して http get リクエストを送信したい場合、パラメータを追加する API がないようですが、これは正しいですか?

はい。

名前値のコレクションとそれらをエンコードする URL を作成し、最後にそれらを連結することを伴わない、クエリ文字列を作成するために使用できる単純な API はありますか?

もちろん:

var query = HttpUtility.ParseQueryString(string.Empty);
query["foo"] = "bar<>&-baz";
query["bar"] = "bazinga";
string queryString = query.ToString();

期待される結果が得られます:

foo=bar%3c%3e%26-baz&bar=bazinga

UriBuilderクラスが役立つ場合もあります。

var builder = new UriBuilder("http://example.com");
builder.Port = -1;
var query = HttpUtility.ParseQueryString(builder.Query);
query["foo"] = "bar<>&-baz";
query["bar"] = "bazinga";
builder.Query = query.ToString();
string url = builder.ToString();

期待される結果が得られます:

http://example.com/?foo=bar%3c%3e%26-baz&bar=bazinga

メソッドに安全にフィードできることHttpClient.GetAsync

于 2013-06-13T20:20:41.887 に答える
92

まだ使用していないプロジェクトに含めたくない場合は、 fromをSystem.Web使用して次のようにすることができます。FormUrlEncodedContentSystem.Net.Http

キー値ペアのバージョン

string query;
using(var content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]{
    new KeyValuePair<string, string>("ham", "Glazed?"),
    new KeyValuePair<string, string>("x-men", "Wolverine + Logan"),
    new KeyValuePair<string, string>("Time", DateTime.UtcNow.ToString()),
})) {
    query = content.ReadAsStringAsync().Result;
}

辞書バージョン

string query;
using(var content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
    { "ham", "Glaced?"},
    { "x-men", "Wolverine + Logan"},
    { "Time", DateTime.UtcNow.ToString() },
})) {
    query = content.ReadAsStringAsync().Result;
}
于 2014-11-04T20:32:07.460 に答える
47

TL;DR: Unicode 文字の処理に関連して完全に壊れているため、受け入れられたバージョンを使用しないでください。また、内部 API を使用しないでください。

私は実際に、受け入れられた解決策で奇妙な二重エンコーディングの問題を発見しました:

したがって、エンコードする必要がある文字を扱っている場合、受け入れられている解決策は二重エンコードにつながります。

  • NameValueCollectionクエリ パラメーターは、インデクサーを使用して自動エンコードされます(これはUrlEncodeUnicode、通常の期待されるUrlEncode(!)ではなく、 を使用します)
  • 次に、呼び出すと、もう一度エンコーディングをuriBuilder.Uri行う新しいUri使用コンストラクタが作成されます(通常の URL エンコーディング) 。
  • これを回避することはできませんuriBuilder.ToString()Uri(これは、IMO が少なくとも矛盾している正しいものを返しますが、バグかもしれませんが、それは別の質問です)、HttpClient文字列を受け入れるメソッドを使用します。クライアントUriは、渡された文字列から次のように作成します。new Uri(uri, UriKind.RelativeOrAbsolute)

小さくても完全な再現:

var builder = new UriBuilder
{
    Scheme = Uri.UriSchemeHttps,
    Port = -1,
    Host = "127.0.0.1",
    Path = "app"
};

NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);

query["cyrillic"] = "кирилиця";

builder.Query = query.ToString();
Console.WriteLine(builder.Query); //query with cyrillic stuff UrlEncodedUnicode, and that's not what you want

var uri = builder.Uri; // creates new Uri using constructor which does encode and messes cyrillic parameter even more
Console.WriteLine(uri);

// this is still wrong:
var stringUri = builder.ToString(); // returns more 'correct' (still `UrlEncodedUnicode`, but at least once, not twice)
new HttpClient().GetStringAsync(stringUri); // this creates Uri object out of 'stringUri' so we still end up sending double encoded cyrillic text to server. Ouch!

出力:

?cyrillic=%u043a%u0438%u0440%u0438%u043b%u0438%u0446%u044f

https://127.0.0.1/app?cyrillic=%25u043a%25u0438%25u0440%25u0438%25u043b%25u0438%25u0446%25u044f

ご覧のとおり、uribuilder.ToString()+httpClient.GetStringAsync(string)またはuriBuilder.Uri+httpClient.GetStringAsync(Uri)を実行しても、二重にエンコードされたパラメーターを送信することになります

修正例は次のとおりです。

var uri = new Uri(builder.ToString(), dontEscape: true);
new HttpClient().GetStringAsync(uri);

しかし、これは廃止 Uriされたコンストラクターを使用します

Windows Server 上の最新の .NET の PS、Uribool doc コメント付きのコンストラクターは「時代遅れ、dontEscape は常に false」と言っていますが、実際には期待どおりに動作します (エスケープをスキップします)。

だからそれは別のバグのように見えます...

そして、これでさえ明らかに間違っています-サーバーが期待するものだけでなく、UrlEncodedUnicodeをサーバーに送信します

更新: もう 1 つ、NameValueCollection は実際には UrlEncodeUnicode を実行しますが、これはもう使用されることは想定されておらず、通常の url.encode/decode とは互換性がありません (NameValueCollectionから URL クエリへの参照? を参照)。

要するに、Unicode クエリ パラメータが台無しになるため、このハックを決して使用しないでください。NameValueCollection query = HttpUtility.ParseQueryString(builder.Query);クエリを手動で作成UriBuilder.Queryし、必要なエンコーディングを行うに割り当ててから、 を使用して Uri を取得するだけUriBuilder.Uriです。

このような使用を想定されていないコードを使用して自分を傷つける代表的な例

于 2015-07-06T13:34:08.900 に答える
29

本格的な REST クライアントに拡張するオプションのコンパニオン lib を備えた流暢な URL ビルダーであるFlurl [開示: 私が作成者です]を確認することをお勧めします。

var result = await "https://api.com"
    // basic URL building:
    .AppendPathSegment("endpoint")
    .SetQueryParams(new {
        api_key = ConfigurationManager.AppSettings["SomeApiKey"],
        max_results = 20,
        q = "Don't worry, I'll get encoded!"
    })
    .SetQueryParams(myDictionary)
    .SetQueryParam("q", "overwrite q!")

    // extensions provided by Flurl.Http:
    .WithOAuthBearerToken("token")
    .GetJsonAsync<TResult>();

詳細については、ドキュメントを確認してください。完全なパッケージは NuGet で入手できます。

PM> Install-Package Flurl.Http

またはスタンドアロンの URL ビルダーのみ:

PM> Install-Package Flurl

于 2014-02-21T06:37:57.937 に答える
4

この数回再利用する必要があるため、クエリ文字列の構成方法を抽象化するのに役立つこのクラスを思いつきました。

public class UriBuilderExt
{
    private NameValueCollection collection;
    private UriBuilder builder;

    public UriBuilderExt(string uri)
    {
        builder = new UriBuilder(uri);
        collection = System.Web.HttpUtility.ParseQueryString(string.Empty);
    }

    public void AddParameter(string key, string value) {
        collection.Add(key, value);
    }

    public Uri Uri{
        get
        {
            builder.Query = collection.ToString();
            return builder.Uri;
        }
    }

}

使用は次のように単純化されます。

var builder = new UriBuilderExt("http://example.com/");
builder.AddParameter("foo", "bar<>&-baz");
builder.AddParameter("bar", "second");
var uri = builder.Uri;

uri を返します: http://example.com/?foo=bar%3c%3e%26-baz&bar=second

于 2015-02-18T21:05:42.603 に答える
3

Darin は興味深い賢い解決策を提供してくれました。

public class ParameterCollection
{
    private Dictionary<string, string> _parms = new Dictionary<string, string>();

    public void Add(string key, string val)
    {
        if (_parms.ContainsKey(key))
        {
            throw new InvalidOperationException(string.Format("The key {0} already exists.", key));
        }
        _parms.Add(key, val);
    }

    public override string ToString()
    {
        var server = HttpContext.Current.Server;
        var sb = new StringBuilder();
        foreach (var kvp in _parms)
        {
            if (sb.Length > 0) { sb.Append("&"); }
            sb.AppendFormat("{0}={1}",
                server.UrlEncode(kvp.Key),
                server.UrlEncode(kvp.Value));
        }
        return sb.ToString();
    }
}

したがって、それを使用するときは、次のようにすることができます。

var parms = new ParameterCollection();
parms.Add("key", "value");

var url = ...
url += "?" + parms;
于 2013-06-13T20:23:30.373 に答える
1

または単に私の Uri 拡張機能を使用する

コード

public static Uri AttachParameters(this Uri uri, NameValueCollection parameters)
{
    var stringBuilder = new StringBuilder();
    string str = "?";
    for (int index = 0; index < parameters.Count; ++index)
    {
        stringBuilder.Append(str + parameters.AllKeys[index] + "=" + parameters[index]);
        str = "&";
    }
    return new Uri(uri + stringBuilder.ToString());
}

使用法

Uri uri = new Uri("http://www.example.com/index.php").AttachParameters(new NameValueCollection
                                                                           {
                                                                               {"Bill", "Gates"},
                                                                               {"Steve", "Jobs"}
                                                                           });

結果

http://www.example.com/index.php?Bill=Gates&Steve=Jobs

于 2013-07-30T01:30:58.973 に答える
0

"Darin Dimitrov" のおかげで、これは拡張メソッドです。

 public static partial class Ext
{
    public static Uri GetUriWithparameters(this Uri uri,Dictionary<string,string> queryParams = null,int port = -1)
    {
        var builder = new UriBuilder(uri);
        builder.Port = port;
        if(null != queryParams && 0 < queryParams.Count)
        {
            var query = HttpUtility.ParseQueryString(builder.Query);
            foreach(var item in queryParams)
            {
                query[item.Key] = item.Value;
            }
            builder.Query = query.ToString();
        }
        return builder.Uri;
    }

    public static string GetUriWithparameters(string uri,Dictionary<string,string> queryParams = null,int port = -1)
    {
        var builder = new UriBuilder(uri);
        builder.Port = port;
        if(null != queryParams && 0 < queryParams.Count)
        {
            var query = HttpUtility.ParseQueryString(builder.Query);
            foreach(var item in queryParams)
            {
                query[item.Key] = item.Value;
            }
            builder.Query = query.ToString();
        }
        return builder.Uri.ToString();
    }
}
于 2015-03-03T21:37:03.337 に答える