System.Net.HttpClient를 사용하여 http get 요청을 제출하려면 매개 변수를 추가 할 API가없는 것 같습니다. 맞습니까?
이름 값 수집 및 URL 인코딩을 생성 한 다음 연결하는 쿼리 문자열을 작성하는 데 사용할 수있는 간단한 API가 있습니까? RestSharp의 api (예 : AddParameter (..))와 같은 것을 사용하고 싶었습니다.
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
분석법에 안전하게 공급할 수 있습니다 .
#
Fragment 속성을 사용하여 조각 ( )이있는 URL도 처리 합니다. 내장 도구를 사용하는 대신 URL을 수동으로 처리하는 실수를하는 사람들이 너무 많습니다.
NameValueCollection.ToString()
일반적으로 쿼리 문자열을하고 일을한다는에는 문서가 없습니다하지 않는 ToString
결과에하면 ParseQueryString
해당 기능에 보장이 없습니다로 언제든지 깨질 수 따라서 새로운 쿼리 문자열이 발생할 것입니다.
포함하지 않는 사람들을 위해 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;
}
dispose
합니까? 나는 재사용과 같은 좋은 이유가없는 한 항상 처분한다 HttpClient
.
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, Uri
bool 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
.
이와 같이 사용해서는 안되는 코드를 사용하여 자신을 해치는 주요 예
var namedValues = HttpUtility.ParseQueryString(builder.Query)
것이지만 반환 된 NameValueCollection을 사용하는 대신 즉시 다음과 같이 Dictionary로 변환하십시오. 사전에 var dic = values.ToDictionary(x => x, x => values[x]);
새 값을 추가 한 다음 생성자에 전달 FormUrlEncodedContent
하고 호출 ReadAsStringAsync().Result
하십시오. 그러면 올바르게 인코딩 된 쿼리 문자열이 제공되어 UriBuilder에 다시 할당 할 수 있습니다.
<add key="aspnet:DontUsePercentUUrlEncoding" value="true" />
. 이 동작에 의존하지 않으므로 이전 답변에서 알 수 있듯이 FormUrlEncodedContent 클래스를 사용하는 것이 좋습니다. stackoverflow.com/a/26744471/88409
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
Uri
대신 자신의 수업으로 확장 하거나 시작 하지 string
않겠습니까?
Url
수업으로 시작했다 . 위의 내용은 new Url("https://api.com").AppendPathSegment...
개인적으로 키 입력 횟수가 적고 문서에서 표준화되어 있기 때문에 문자열 확장을 선호하지만 어느 쪽이든 할 수 있습니다.
당신에 대한 참조를 포함하지 않는 경우 로스토프의 게시물과 같은 라인을 따라, 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;
kvp.Key
하고 kvp.Value
별도로 인코딩하려고 합니다 (따라서 &
및 =
문자를 인코딩하지 마십시오 ).
server.UrlEncode
과 같이 대체 할 수 있습니다.WebUtility.UrlEncode
또는 단순히 내 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"}
});
내가 개발하고 있는 RFC 6570 URI 템플릿 라이브러리 는이 작업을 수행 할 수 있습니다. 모든 인코딩은 해당 RFC에 따라 처리됩니다. 이 글을 쓰는 시점에서 베타 릴리스를 사용할 수 있으며 안정적인 1.0 릴리스로 간주되지 않는 유일한 이유는 문서가 내 기대를 완전히 충족시키지 못하기 때문입니다 (문제 # 17 , # 18 , # 32 , # 43 참조 ).
쿼리 문자열 만 만들 수도 있습니다.
UriTemplate template = new UriTemplate("{?params*}");
var parameters = new Dictionary<string, string>
{
{ "param1", "value1" },
{ "param2", "value2" },
};
Uri relativeUri = template.BindByName(parameters);
또는 완전한 URI를 작성할 수 있습니다.
UriTemplate template = new UriTemplate("path/to/item{?params*}");
var parameters = new Dictionary<string, string>
{
{ "param1", "value1" },
{ "param2", "value2" },
};
Uri baseAddress = new Uri("http://www.example.com");
Uri relativeUri = template.BindByName(baseAddress, parameters);
이 시간을 재사용해야하므로 쿼리 문자열이 어떻게 구성되는지 추상화하는 데 도움이되는이 클래스를 생각해 냈습니다.
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
HttpUtility.ParseQueryString () 대신 UriBuilder.Uri.ParseQueryString ()을 사용하도록 수정 된 허용 된 응답의 좋은 부분 :
var builder = new UriBuilder("http://example.com");
var query = builder.Uri.ParseQueryString();
query["foo"] = "bar<>&-baz";
query["bar"] = "bazinga";
builder.Query = query.ToString();
string url = builder.ToString();
"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();
}
}
Dictionary를 QueryStringFormat으로 변환하는 확장 메서드를 만드는 것보다 더 나은 솔루션을 찾을 수 없습니다. Waleed AK가 제안한 솔루션도 좋습니다.
내 해결책을 따르십시오 :
확장 메소드를 작성하십시오.
public static class DictionaryExt
{
public static string ToQueryString<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
{
return ToQueryString<TKey, TValue>(dictionary, "?");
}
public static string ToQueryString<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, string startupDelimiter)
{
string result = string.Empty;
foreach (var item in dictionary)
{
if (string.IsNullOrEmpty(result))
result += startupDelimiter; // "?";
else
result += "&";
result += string.Format("{0}={1}", item.Key, item.Value);
}
return result;
}
}
그리고 그들:
var param = new Dictionary<string, string>
{
{ "param1", "value1" },
{ "param2", "value2" },
};
param.ToQueryString(); //By default will add (?) question mark at begining
//"?param1=value1¶m2=value2"
param.ToQueryString("&"); //Will add (&)
//"¶m1=value1¶m2=value2"
param.ToQueryString(""); //Won't add anything
//"param1=value1¶m2=value2"