[C#] System.Net.HttpClient get에 대한 쿼리 문자열 작성

System.Net.HttpClient를 사용하여 http get 요청을 제출하려면 매개 변수를 추가 할 API가없는 것 같습니다. 맞습니까?

이름 값 수집 및 URL 인코딩을 생성 한 다음 연결하는 쿼리 문자열을 작성하는 데 사용할 수있는 간단한 API가 있습니까? RestSharp의 api (예 : AddParameter (..))와 같은 것을 사용하고 싶었습니다.



답변

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분석법에 안전하게 공급할 수 있습니다 .


답변

포함하지 않는 사람들을 위해 System.Web이미 그것을 사용하지 않는 프로젝트에, 당신은 사용할 수 있습니다 FormUrlEncodedContent에서 System.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;
}


답변

TL; DR : 유니 코드 문자 처리와 관련하여 완전히 손상되어 내부 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”라고 말하지만 실제로 예상대로 작동합니다 (건너 뛰기)

다른 버그처럼 보입니다.

그리고 이것은 심지어 틀린 것입니다-서버가 기대하는 UrlEncoded뿐만 아니라 UrlEncodedUnicode를 서버로 보냅니다.

업데이트 : 한 가지 더, NameValueCollection은 실제로 더 이상 사용되지 않으며 일반 url.encode / decode와 호환되지 않는 UrlEncodeUnicode를 실제로 수행합니다 ( NameValueCollection to URL Query? 참조 ).

결론은 유니 코드 쿼리 매개 변수를 망칠 것이므로이 핵을 사용하지 마십시오NameValueCollection query = HttpUtility.ParseQueryString(builder.Query); . 쿼리를 수동으로 빌드하고 UriBuilder.Query필요한 인코딩을 수행 한 다음 Uri를 사용하여 쿼리를 지정하십시오 UriBuilder.Uri.

이와 같이 사용해서는 안되는 코드를 사용하여 자신을 해치는 주요 예


답변

ASP.NET Core 프로젝트에서는 QueryHelpers 클래스를 사용할 수 있습니다.

// using Microsoft.AspNetCore.WebUtilities;
var query = new Dictionary<string, string>
{
    ["foo"] = "bar",
    ["foo2"] = "bar2",
    // ...
};

var response = await client.GetAsync(QueryHelpers.AddQueryString("/api/", query));


답변

Flurl [disclosure : I ‘m is the author] 을 확인하고 , 컴패니언 라이브러리 (선택 사항)를 사용하여 완전한 REST 클라이언트로 확장하는 유창한 URL 빌더 를 확인할 수 있습니다 .

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


답변

당신에 대한 참조를 포함하지 않는 경우 로스토프의 게시물과 같은 라인을 따라, System.Web당신의 프로젝트를, 당신은 사용할 수 있습니다 FormDataCollection에서 System.Net.Http.Formatting하고 다음과 같은 작업을 수행합니다

사용 System.Net.Http.Formatting.FormDataCollection

var parameters = new Dictionary<string, string>()
{
    { "ham", "Glaced?" },
    { "x-men", "Wolverine + Logan" },
    { "Time", DateTime.UtcNow.ToString() },
};
var query = new FormDataCollection(parameters).ReadAsNameValueCollection().ToString();


답변

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;