C #에서 URL에 대한 쿼리 문자열을 작성하는 방법은 무엇입니까?


473

코드에서 웹 리소스를 호출 할 때 일반적인 작업은 필요한 모든 매개 변수를 포함하도록 쿼리 문자열을 작성하는 것입니다. 로켓 과학은 &아니지만, 첫 번째 매개 변수가 아닌 경우 추가하고 매개 변수를 인코딩하는 등 몇 가지 멋진 세부 정보가 필요합니다 .

그것을하는 코드는 매우 간단하지만 약간 지루합니다.

StringBuilder SB = new StringBuilder();
if (NeedsToAddParameter A) 
{ 
  SB.Append("A="); SB.Append(HttpUtility.UrlEncode("TheValueOfA")); 
}

if (NeedsToAddParameter B) 
{
  if (SB.Length>0) SB.Append("&"); 
  SB.Append("B="); SB.Append(HttpUtility.UrlEncode("TheValueOfB")); }
}

이것은 더 우아하고 읽기 쉬운 유틸리티 클래스가 존재할 것으로 예상되는 일반적인 작업입니다. MSDN을 검색 할 때 하나를 찾지 못했습니다. 다음과 같은 질문이 나타납니다.

위의 작업을 수행하는 가장 우아한 방법은 무엇입니까?


26
현재 시점에서도 쿼리 문자열을 처리하는 간단한 방법 이없는 것 같습니다 . 그리고 간단하게 말하면 OOB, 비 내부 표준 준수 프레임 워크 클래스를 의미합니다. 아니면 내가 뭔가를 놓치고 있습니까?
절망의 그리 모

5
아무것도 빠지지 않았습니다. 쿼리 문자열 작성은 Flurl 으로 채우려 고 시도한 프레임 워크의 주요 격차입니다 .
Todd Menier


당신은 제가 .. 새 UrlBuilder (기존) .AddQuery ( "키", "값")를 하나 구축해야한다 생각하고 있어요 ToString ().
Demetris Leptos

답변:


293

후드를 보면 QueryString 속성은 NameValueCollection입니다. 비슷한 작업을 수행하면 일반적으로 직렬화 및 역 직렬화에 관심이 있었으므로 NameValueCollection을 빌드 한 다음 전달하는 것이 좋습니다.

using System.Linq;
using System.Web;
using System.Collections.Specialized;

private string ToQueryString(NameValueCollection nvc)
{
    var array = (
        from key in nvc.AllKeys
        from value in nvc.GetValues(key)
            select string.Format(
                "{0}={1}",
                HttpUtility.UrlEncode(key),
                HttpUtility.UrlEncode(value))
        ).ToArray();
    return "?" + string.Join("&", array);
}

LINQ 에서도이 작업을 수행하는 매우 우아한 방법이 있다고 생각합니다.


22
HTTP 사양 (RFC 2616)은 쿼리 문자열에 포함될 수있는 내용에 대해서는 아무 것도 말하지 않습니다. 일반 URI 형식을 정의하는 RFC 3986도 없습니다. 일반적으로 사용되는 키 / 값 쌍 형식을이라고 application/x-www-form-urlencoded하며 실제로는 GET요청의 일부로 양식 데이터를 제출할 목적으로 HTML에 의해 정의됩니다 . HTML 5는이 형식에서 키당 여러 값을 금지하지 않으며 실제로 페이지에 동일한 name속성을 가진 여러 필드가 포함되어있는 경우 브라우저가 키당 여러 값을 생성해야 합니다.
Daniel Cassidy

14
@ annakata : 내 의견은 1 년이 넘었고 (2 년이 넘은 대답입니다!) NameValueCollection은 GetValues ​​(key) 메서드를 사용하여 키 당 여러 값을 매우 많이 지원합니다.
Mauricio Scheffer

4
@MauricioScheffer : 그러나 NameValueCollection은 "정확하게"쿼리 문자열로 변환되지 않습니다. 예를 들어, 동일한 키가 여러 번 존재하는 WebClient에서 QueryString 매개 변수를 설정하면 "path? key = value1 & key = value2"대신 "path? key = value1, value2"로 바뀝니다. ?) 패턴.
David Pope

8
키 당 여러 값에 대해 HTML에서 여러 항목을 선택하여 제출 한 다중 선택 목록 상자가 있으면 David가 언급 한 다중 값 형식으로 전송됩니다.
Sam

10
이식성이 뛰어난 HttpUtility.UrlEncode 대신 Uri.EscapeDataString을 사용할 수 있습니다. 참조 stackoverflow.com/questions/2573290/...
PEK

687

HttpValueCollection을 호출 하여 새 쓰기 가능한 인스턴스를 System.Web.HttpUtility.ParseQueryString(string.Empty)만든 다음 any로 사용할 수 있습니다 NameValueCollection. 원하는 값을 추가하면 ToString다음과 같이 컬렉션을 호출 하여 쿼리 문자열을 얻을 수 있습니다.

NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(string.Empty);

queryString.Add("key1", "value1");
queryString.Add("key2", "value2");

return queryString.ToString(); // Returns "key1=value1&key2=value2", all URL-encoded

HttpValueCollection내부에 그래서 당신이 직접 인스턴스를 생성 할 수 없습니다. 그러나 일단 인스턴스를 얻으면 다른 인스턴스처럼 사용할 수 있습니다 NameValueCollection. 작업하고있는 실제 객체는이므로 HttpValueCollectionToString 메서드를 호출하면 재정의 된 메서드를 on에 호출 HttpValueCollection하여 컬렉션을 URL 인코딩 된 쿼리 문자열로 형식화합니다.

비슷한 문제에 대한 답변을 위해 SO와 웹을 검색 한 후에 이것은 내가 찾을 수있는 가장 간단한 솔루션입니다.

.NET 코어

.NET Core에서 작업하는 경우 Microsoft.AspNetCore.WebUtilities.QueryHelpers클래스를 사용 하면이를 단순화 할 수 있습니다 .

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.webutilities.queryhelpers

샘플 코드 :

const string url = "https://customer-information.azure-api.net/customers/search/taxnbr";
var param = new Dictionary<string, string>() { { "CIKey", "123456789" } };

var newUrl = new Uri(QueryHelpers.AddQueryString(url, param));

6
IDictionary 인터페이스를 위해 ToURLQueryString이라는 확장 메서드를 만들 수 있습니다.public static string ToURLQueryString(this IDictionary dict) { ... }
Roy Tinker

65
이 방법은 멀티 바이트 문자에 대해 표준을 준수 하지 않습니다 . % XX % XX 대신 % uXXXX로 인코딩합니다. 웹 서버에서 결과 쿼리 문자열을 잘못 해석했을 수 있습니다. 이것은 HttpUtility.ParseQueryString ()에 의해 반환되는 내부 프레임 워크 클래스 HttpValueCollection에 문서화되어 있습니다. 주석은 이전 버전과의 호환성을 위해 이러한 동작을 유지한다고 말합니다.
alex

25
참고 HttpUtilityPraseQueryString ( "")과 새로운 NameValueCollection은 (사이에는 중요한 차이)가 - toString를 오버라이드 (override) 해, 할 만 HttpUtility 결과는 () 적절한 쿼리 문자열 생산하는
프랭크 Schwieterman

7
쿼리 문자열에 이름의 여러 인스턴스를 원하는 경우는 어떻습니까? 예를 들어 "type = 10 & type = 21"입니다.
Finster

7
@Finster이 Add메서드를 사용하여 이름의 여러 인스턴스를 쿼리 문자열에 추가 할 수 있습니다 . 즉 queryString.Add("type", "1"); queryString.Add("type", "2"); ,이 Add방법을 사용 하는 것이 실제로 항상 이렇게하는 더 좋은 방법 일 것입니다.
jeremysawesome 2016 년

94

Roy Tinker의 의견에서 영감을 받아 Uri 클래스에서 간단한 확장 메서드를 사용하여 코드를 간결하고 깨끗하게 유지했습니다.

using System.Web;

public static class HttpExtensions
{
    public static Uri AddQuery(this Uri uri, string name, string value)
    {
        var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);

        httpValueCollection.Remove(name);
        httpValueCollection.Add(name, value);

        var ub = new UriBuilder(uri);
        ub.Query = httpValueCollection.ToString();

        return ub.Uri;
    }
}

용법:

Uri url = new Uri("http://localhost/rest/something/browse").
          AddQuery("page", "0").
          AddQuery("pageSize", "200");

편집-표준 호환 변형

여러 사람들이 지적했듯이 httpValueCollection.ToString()유니 코드 문자를 비표준 호환 방식으로 인코딩 합니다. 이것은 HttpUtility.UrlEncode사용되지 않는 HttpUtility.UrlEncodeUnicode메소드 대신 메소드를 호출하여 이러한 문자를 처리하는 동일한 확장 메소드의 변형입니다 .

using System.Web;

public static Uri AddQuery(this Uri uri, string name, string value)
{
    var httpValueCollection = HttpUtility.ParseQueryString(uri.Query);

    httpValueCollection.Remove(name);
    httpValueCollection.Add(name, value);

    var ub = new UriBuilder(uri);

    // this code block is taken from httpValueCollection.ToString() method
    // and modified so it encodes strings with HttpUtility.UrlEncode
    if (httpValueCollection.Count == 0)
        ub.Query = String.Empty;
    else
    {
        var sb = new StringBuilder();

        for (int i = 0; i < httpValueCollection.Count; i++)
        {
            string text = httpValueCollection.GetKey(i);
            {
                text = HttpUtility.UrlEncode(text);

                string val = (text != null) ? (text + "=") : string.Empty;
                string[] vals = httpValueCollection.GetValues(i);

                if (sb.Length > 0)
                    sb.Append('&');

                if (vals == null || vals.Length == 0)
                    sb.Append(val);
                else
                {
                    if (vals.Length == 1)
                    {
                        sb.Append(val);
                        sb.Append(HttpUtility.UrlEncode(vals[0]));
                    }
                    else
                    {
                        for (int j = 0; j < vals.Length; j++)
                        {
                            if (j > 0)
                                sb.Append('&');

                            sb.Append(val);
                            sb.Append(HttpUtility.UrlEncode(vals[j]));
                        }
                    }
                }
            }
        }

        ub.Query = sb.ToString();
    }

    return ub.Uri;
}

3
완전한. 사내 라이브러리에 추가되었습니다. :)
Andy

1
또한 값을 URL 인코딩해야합니다. queryString.Add (이름, Uri.EscapeDataString (value));
Ufuk Hacıoğulları

2
이 답변을 개선해 주셔서 감사합니다. 멀티 바이트 문자 문제를 해결했습니다.
Ufuk Hacıoğulları

9
참고로, 상대 URL에서 UriBuilder를 인스턴스화 할 수 없기 때문에 상대 URL에서는 작동하지 않습니다.
Yuriy Faktorovich

1
중복 키를 추가 할 수 없도록 제거 키를 추가했습니다. dotnetfiddle.net/hTlyAd
Paul Totzke

29

얼마 전에 비슷한 질문 에 대답했습니다 . 기본적으로 가장 좋은 방법은 HttpValueCollectionASP.NET의 Request.QueryString속성이 실제로 클래스 인 클래스를 사용하는 것입니다. 불행히도 .NET 프레임 워크의 내부 클래스 입니다. Reflector를 사용하여 잡아서 (Utils 클래스에 배치) 할 수 있습니다. 이런 식으로 NameValueCollection과 같은 쿼리 문자열을 조작 할 수 있지만 모든 URL 인코딩 / 디코딩 문제가 해결됩니다.

HttpValueCollectionextends NameValueCollection이고 인코딩 된 쿼리 문자열 (앰퍼샌드 및 물음표 포함) 을 취하는 생성자가 있으며 ToString()나중에 기본 컬렉션에서 쿼리 문자열을 다시 작성 하는 메서드를 재정의합니다 .

예:

  var coll = new HttpValueCollection();

  coll["userId"] = "50";
  coll["paramA"] = "A";
  coll["paramB"] = "B";      

  string query = coll.ToString(true); // true means use urlencode

  Console.WriteLine(query); // prints: userId=50&paramA=A&paramB=B

감사합니다 ... 반환하는 NameValueCollection에 다르게 작동하지만 이유를 알 수없는 ToString ()이 있음을 알았습니다.
calebt

httpValueCollection.ToString()실제로 호출 httpValueCollection.ToString(true)하므로 true명시 성을 추가 할 필요가 없습니다.
dav_i

5
HttpValueCollection은 내부 클래스이므로 인스턴스화 할 수 없습니다.
ozba

29

다음은 동일한 키에 대해 여러 값을 지원하는 확장 방법 (이전 게시물의 개념 결합)으로서의 유창하고 복잡한 방법입니다. 개인적으로 선호하는 것은 다른 팀 구성원이 이와 같은 것을 발견 할 수 있도록 래퍼보다 확장 된 것입니다. 인코딩 방법, 스택 오버플로 (하나의에 대한 글을 많이 주위에 논란이 있다고 주 )와 (같은 MSDN 블로거 이 하나 ).

public static string ToQueryString(this NameValueCollection source)
{
    return String.Join("&", source.AllKeys
        .SelectMany(key => source.GetValues(key)
            .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))))
        .ToArray());
}

편집 : null 지원으로 특정 상황에 맞게 조정해야 할 수도 있습니다.

public static string ToQueryString(this NameValueCollection source, bool removeEmptyEntries)
{
    return source != null ? String.Join("&", source.AllKeys
        .Where(key => !removeEmptyEntries || source.GetValues(key)
            .Where(value => !String.IsNullOrEmpty(value))
            .Any())
        .SelectMany(key => source.GetValues(key)
            .Where(value => !removeEmptyEntries || !String.IsNullOrEmpty(value))
            .Select(value => String.Format("{0}={1}", HttpUtility.UrlEncode(key), value != null ? HttpUtility.UrlEncode(value) : string.Empty)))
        .ToArray())
        : string.Empty;
}

1
값이 null 인 경우 실패
Josh Noe

이것은 각 키 값 쌍마다 많은 쿼리 문자열을 생성합니다
Gayan

@GayanRanasinghe : 그게 무슨 뜻입니까?
Matti Virkkunen

22

Flurl [disclosure : I 'm is the author]은 익명 객체를 통해 쿼리 문자열 작성을 지원합니다 (다른 방법 중에서도).

var url = "http://www.some-api.com".SetQueryParams(new
{
    api_key = ConfigurationManager.AppSettings["SomeApiKey"],
    max_results = 20,
    q = "Don't worry, I'll get encoded!"
});

선택적 Flurl.Http 컴패니언 라이브러리를 사용하면 유동적 인 동일한 호출 체인에서 HTTP 호출을 수행하여 완전한 REST 클라이언트로 확장 할 수 있습니다.

T result = await "https://api.mysite.com"
    .AppendPathSegment("person")
    .SetQueryParams(new { ap_key = "my-key" })
    .WithOAuthBearerToken("MyToken")
    .PostJsonAsync(new { first_name = firstName, last_name = lastName })
    .ReceiveJson<T>();

전체 패키지는 NuGet에서 사용할 수 있습니다.

PM> Install-Package Flurl.Http

또는 독립형 URL 작성기 :

PM> Install-Package Flurl


20

여기 내 늦은 입장입니다. 나는 여러 가지 이유로 다른 사람들을 좋아하지 않아서 내 자신을 썼습니다.

이 버전의 특징 :

  • StringBuilder 만 사용하십시오. ToArray () 호출 또는 다른 확장 메소드가 없습니다. 그것은 다른 응답들만큼 아름답게 보이지는 않지만, 이것이 핵심 기능이라고 생각하므로 비 효율성을 숨기는 "유창한", "한 줄짜리"코드를 갖는 것보다 효율성이 더 중요합니다.

  • 키당 여러 값을 처리합니다. (나 자신이 필요하지는 않지만 Mauricio를 침묵시키기 위해)

    public string ToQueryString(NameValueCollection nvc)
    {
        StringBuilder sb = new StringBuilder("?");
    
        bool first = true;
    
        foreach (string key in nvc.AllKeys)
        {
            foreach (string value in nvc.GetValues(key))
            {
                if (!first)
                {
                    sb.Append("&");
                }
    
                sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value));
    
                first = false;
            }
        }
    
        return sb.ToString();
    }

사용법 예

        var queryParams = new NameValueCollection()
        {
            { "x", "1" },
            { "y", "2" },
            { "foo", "bar" },
            { "foo", "baz" },
            { "special chars", "? = &" },
        };

        string url = "http://example.com/stuff" + ToQueryString(queryParams);

        Console.WriteLine(url);

산출

http://example.com/stuff?x=1&y=2&foo=bar&foo=baz&special%20chars=%3F%20%3D%20%26

나는 이것이 System.Web하에 있고 어디서나 사용할 수없는 HttpUtility를 사용하지 않는다는 것을 좋아합니다.
Kugel

linq를 사용하지 않고 HttpUtility를 사용하지 않는 경우 +1입니다. 빈 sb를 만들고 "bool first"변수를 버리고 루프에서 단순히 sb.AppendFormat () 앞에 sb.Append (sb.Length == 0? "?": "&")를 갖습니다. 이제 nvc가 비어 있으면이 메서드는 외로운 "?"대신 빈 문자열을 반환합니다.
Mathew Leger

이 답변은 여러 값을 가진 단일 매개 변수를 처리합니다. 예 :? id = 1 & id = 3 & id = 2 & id = 9
수학

12

이와 같이 유창한 스타일로 매개 변수를 추가 할 수있는 확장 메소드를 작성하는 것은 어떻습니까?

string a = "http://www.somedomain.com/somepage.html"
    .AddQueryParam("A", "TheValueOfA")
    .AddQueryParam("B", "TheValueOfB")
    .AddQueryParam("Z", "TheValueOfZ");

string b = new StringBuilder("http://www.somedomain.com/anotherpage.html")
    .AddQueryParam("A", "TheValueOfA")
    .AddQueryParam("B", "TheValueOfB")
    .AddQueryParam("Z", "TheValueOfZ")
    .ToString(); 

다음을 사용하는 과부하가 있습니다 string.

public static string AddQueryParam(
    this string source, string key, string value)
{
    string delim;
    if ((source == null) || !source.Contains("?"))
    {
        delim = "?";
    }
    else if (source.EndsWith("?") || source.EndsWith("&"))
    {
        delim = string.Empty;
    }
    else
    {
        delim = "&";
    }

    return source + delim + HttpUtility.UrlEncode(key)
        + "=" + HttpUtility.UrlEncode(value);
}

그리고 다음을 사용하는 과부하가 있습니다 StringBuilder.

public static StringBuilder AddQueryParam(
    this StringBuilder source, string key, string value)
{
    bool hasQuery = false;
    for (int i = 0; i < source.Length; i++)
    {
        if (source[i] == '?')
        {
            hasQuery = true;
            break;
        }
    }

    string delim;
    if (!hasQuery)
    {
        delim = "?";
    }
    else if ((source[source.Length - 1] == '?')
        || (source[source.Length - 1] == '&'))
    {
        delim = string.Empty;
    }
    else
    {
        delim = "&";
    }

    return source.Append(delim).Append(HttpUtility.UrlEncode(key))
        .Append("=").Append(HttpUtility.UrlEncode(value));
}

: +1 : 간단한 문자열 기반 확장 방법. 다른 답변 중 일부는 더 엣지 케이스를 커버 할 수있다, 그러나 이것은 내 상황에 대한 충분하고, 그것은 나를 구성하는 데 필요하지 않습니다 NameValueCollection, HttpValueCollection또는 Uri먼저. 감사!
Stanley G.

11

작업중 인 PCL (Portable Class Library)에 대해 동일한 문제를 해결해야했습니다. 이 경우 System.Web에 액세스 할 수 없으므로 ParseQueryString을 사용할 수 없습니다.

대신 나는 이렇게 사용 System.Net.Http.FormUrlEncodedContent했다 :

var url = new UriBuilder("http://example.com");

url.Query = new FormUrlEncodedContent(new Dictionary<string,string>()
{
    {"param1", "val1"},
    {"param2", "val2"},
    {"param3", "val3"},
}).ReadAsStringAsync().Result;

이것은 내가 사용하는 기술이며 다른 질문 http://stackoverflow.com/a/26744471/2108310 에서 참조했습니다 . 유일한 차이점은 System에 대한 참조가 아닌 KeyValue 쌍 배열을 사용한다는 것입니다. Net (이것은 당신이 언급 한대로 PCL 가능)이 IMHO는 타사 패키지를 포함하지 않거나 일부 사제 스파게티 엉망으로 함께 노력하지 않고 그것을 수행하는 가장 간단한 방법입니다.
Rostov

9
    public static string ToQueryString(this Dictionary<string, string> source)
    {
        return String.Join("&", source.Select(kvp => String.Format("{0}={1}", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))).ToArray());
    }

    public static string ToQueryString(this NameValueCollection source)
    {
        return String.Join("&", source.Cast<string>().Select(key => String.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(source[key]))).ToArray());
    }

1
좋은! 그러나 당신은 .ToArray()s 가 필요하지 않습니다 .
mpen

7

이 클래스를 프로젝트에 추가

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public class QueryStringBuilder
{
    private readonly List<KeyValuePair<string, object>> _list;

    public QueryStringBuilder()
    {
        _list = new List<KeyValuePair<string, object>>();
    }

    public void Add(string name, object value)
    {
        _list.Add(new KeyValuePair<string, object>(name, value));
    }

    public override string ToString()
    {
        return String.Join("&", _list.Select(kvp => String.Concat(Uri.EscapeDataString(kvp.Key), "=", Uri.EscapeDataString(kvp.Value.ToString()))));
    }
}

그리고 이것을 다음과 같이 사용하십시오 :

var actual = new QueryStringBuilder {
    {"foo", 123},
    {"bar", "val31"},
    {"bar", "val32"}
};

actual.Add("a+b", "c+d");

actual.ToString(); // "foo=123&bar=val31&bar=val32&a%2bb=c%2bd"

이제이 답변이 받아 들여 져야합니다. "foo [] = 1, foo [] = 2"와 같은 배열에서 완벽하게 작동 할뿐만 아니라 매우 중요한 매개 변수의 순서를 유지합니다.
Soroush Falahati 님이

6

내 제안 :

public static Uri AddQuery(this Uri uri, string name, string value)
{
    // this actually returns HttpValueCollection : NameValueCollection
    // which uses unicode compliant encoding on ToString()
    var query = HttpUtility.ParseQueryString(uri.Query);

    query.Add(name, value);

    var uriBuilder = new UriBuilder(uri)
    {
        Query = query.ToString()
    };

    return uriBuilder.Uri;
}

용법:

var uri = new Uri("http://stackoverflow.com").AddQuery("such", "method")
                                             .AddQuery("wow", "soFluent");

// http://stackoverflow.com?such=method&wow=soFluent

귀하의 접근 방식은 단순하고 우아하지만 HttpUtility에는 System.Web이 필요합니다.
Ody

5

테스트를 거치지 않았지만이 라인을 따라 뭔가가 잘 작동한다고 생각합니다.

public class QueryString
{
    private Dictionary<string,string> _Params = new Dictionary<string,string>();

    public overide ToString()
    {
        List<string> returnParams = new List<string>();

        foreach (KeyValuePair param in _Params)
        {
            returnParams.Add(String.Format("{0}={1}", param.Key, param.Value));
        }

        // return String.Format("?{0}", String.Join("&", returnParams.ToArray())); 

        // credit annakata
        return "?" + String.Join("&", returnParams.ToArray());
    }

    public void Add(string key, string value)
    {
        _Params.Add(key, HttpUtility.UrlEncode(value));
    }
}

QueryString query = new QueryString();

query.Add("param1", "value1");
query.Add("param2", "value2");

return query.ToString();

멋지게 캡슐화되었지만 "? {0}"형식은 불필요하게 비싸다 :)
annakata

클래스 이름을 QueryString으로 변경했습니다. 쿼리가 약간 모호합니다.
Nick Allen

4

빠른 확장 방법 기반 버전 :

class Program
{
    static void Main(string[] args)
    {
        var parameters = new List<KeyValuePair<string, string>>
                             {
                                 new KeyValuePair<string, string>("A", "AValue"),
                                 new KeyValuePair<string, string>("B", "BValue")
                             };

        string output = "?" + string.Join("&", parameters.ConvertAll(param => param.ToQueryString()).ToArray());
    }
}

public static class KeyValueExtensions
{
    public static string ToQueryString(this KeyValuePair<string, string> obj)
    {
        return obj.Key + "=" + HttpUtility.UrlEncode(obj.Value);
    }
}

where 절을 사용하여 문자열에 추가 할 매개 변수를 선택할 수 있습니다.


3

다른 어셈블리에 대한 종속성을 줄이고 단순하게 유지하려는 경우 다음을 수행 할 수 있습니다.

var sb = new System.Text.StringBuilder();

sb.Append("a=" + HttpUtility.UrlEncode("TheValueOfA") + "&");
sb.Append("b=" + HttpUtility.UrlEncode("TheValueOfB") + "&");
sb.Append("c=" + HttpUtility.UrlEncode("TheValueOfC") + "&");
sb.Append("d=" + HttpUtility.UrlEncode("TheValueOfD") + "&");

sb.Remove(sb.Length-1, 1); // Remove the final '&'

string result = sb.ToString();

이것은 루프에서도 잘 작동합니다. 최종 앰퍼샌드 제거는 루프 외부로 이동해야합니다.

연결 연산자는 가독성을 향상시키는 데 사용됩니다. StringBuilder를 사용하는 비용과 비교하여 사용 비용이 최소화됩니다 ( Jeff Atwood 가이 주제에 뭔가를 게시 했다고 생각합니다 ).


3

익명의 객체 버전 을 만들기 위해 최고의 답변을 결합했습니다 .

var queryString = HttpUtility2.BuildQueryString(new
{
    key2 = "value2",
    key1 = "value1",
});

이것은 이것을 생성합니다 :

key2 = value2 & key1 = value1

코드는 다음과 같습니다.

public static class HttpUtility2
{
    public static string BuildQueryString<T>(T obj)
    {
        var queryString = HttpUtility.ParseQueryString(string.Empty);

        foreach (var property in TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>())
        {
            var value = (property.GetValue(obj) ?? "").ToString();
            queryString.Add(property.Name, value);
        }

        return queryString.ToString();
    }
}

2

허용 된 솔루션과 동일하지만 LINQ 구문을 "dot"로 변환합니다.

private string ToQueryString(NameValueCollection nvc)
{
    if (nvc == null) return String.Empty;
    var queryParams = 
          string.Join("&", nvc.AllKeys.Select(key => 
              string.Join("&", nvc.GetValues(key).Select(v => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(v))))));
    return "?" + queryParams;
}

2

Uri 의 확장 방법 이 있습니다.

  • 익명 객체를 허용합니다. uri.WithQuery(new { name = "value" })
  • string/string쌍 모음 (예 : Dictionary`2 )을 허용합니다.
  • string/object쌍의 컬렉션을 허용합니다 (예 : RouteValueDictionary ).
  • NameValueCollection을 승인합니다 .
  • 동일한 값이 동일한 URI를 생성하도록 쿼리 값을 키별로 정렬합니다.
  • 원래 순서를 유지하면서 키당 여러 값을 지원합니다.

문서화 된 버전은 여기 에서 찾을 수 있습니다 .

확장 :

public static Uri WithQuery(this Uri uri, object values)
{
    if (uri == null)
        throw new ArgumentNullException(nameof(uri));

    if (values != null)
    {
        var query = string.Join(
            "&", from p in ParseQueryValues(values)
                 where !string.IsNullOrWhiteSpace(p.Key)
                 let k = HttpUtility.UrlEncode(p.Key.Trim())
                 let v = HttpUtility.UrlEncode(p.Value)
                 orderby k
                 select string.IsNullOrEmpty(v) ? k : $"{k}={v}");

        if (query.Length != 0 || uri.Query.Length != 0)
            uri = new UriBuilder(uri) { Query = query }.Uri;
    }

    return uri;
}

쿼리 파서 :

private static IEnumerable<KeyValuePair<string, string>> ParseQueryValues(object values)
{
    // Check if a name/value collection.
    var nvc = values as NameValueCollection;
    if (nvc != null)
        return from key in nvc.AllKeys
               from val in nvc.GetValues(key)
               select new KeyValuePair<string, string>(key, val);

    // Check if a string/string dictionary.
    var ssd = values as IEnumerable<KeyValuePair<string, string>>;
    if (ssd != null)
        return ssd;

    // Check if a string/object dictionary.
    var sod = values as IEnumerable<KeyValuePair<string, object>>;
    if (sod == null)
    {
        // Check if a non-generic dictionary.
        var ngd = values as IDictionary;
        if (ngd != null)
            sod = ngd.Cast<dynamic>().ToDictionary<dynamic, string, object>(
                p => p.Key.ToString(), p => p.Value as object);

        // Convert object properties to dictionary.
        if (sod == null)
            sod = new RouteValueDictionary(values);
    }

    // Normalize and return the values.
    return from pair in sod
           from val in pair.Value as IEnumerable<string>
            ?? new[] { pair.Value?.ToString() }
           select new KeyValuePair<string, string>(pair.Key, val);
}

테스트는 다음과 같습니다.

var uri = new Uri("https://stackoverflow.com/yo?oldKey=oldValue");

// Test with a string/string dictionary.
var q = uri.WithQuery(new Dictionary<string, string>
{
    ["k1"] = string.Empty,
    ["k2"] = null,
    ["k3"] = "v3"
});

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1&k2&k3=v3"));

// Test with a string/object dictionary.
q = uri.WithQuery(new Dictionary<string, object>
{
    ["k1"] = "v1",
    ["k2"] = new[] { "v2a", "v2b" },
    ["k3"] = null
});

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3"));

// Test with a name/value collection.
var nvc = new NameValueCollection()
{
    ["k1"] = string.Empty,
    ["k2"] = "v2a"
};

nvc.Add("k2", "v2b");

q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1&k2=v2a&k2=v2b"));

// Test with any dictionary.
q = uri.WithQuery(new Dictionary<int, HashSet<string>>
{
    [1] = new HashSet<string> { "v1" },
    [2] = new HashSet<string> { "v2a", "v2b" },
    [3] = null
});

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?1=v1&2=v2a&2=v2b&3"));

// Test with an anonymous object.
q = uri.WithQuery(new
{
    k1 = "v1",
    k2 = new[] { "v2a", "v2b" },
    k3 = new List<string> { "v3" },
    k4 = true,
    k5 = null as Queue<string>
});

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1=v1&k2=v2a&k2=v2b&k3=v3&k4=True&k5"));

// Keep existing query using a name/value collection.
nvc = HttpUtility.ParseQueryString(uri.Query);
nvc.Add("newKey", "newValue");

q = uri.WithQuery(nvc);
Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?newKey=newValue&oldKey=oldValue"));

// Merge two query objects using the RouteValueDictionary.
var an1 = new { k1 = "v1" };
var an2 = new { k2 = "v2" };

q = uri.WithQuery(
    new RouteValueDictionary(an1).Concat(
        new RouteValueDictionary(an2)));

Debug.Assert(q == new Uri(
    "https://stackoverflow.com/yo?k1=v1&k2=v2"));

2

HttpValueCollection에 대한 체인 가능 래퍼 클래스 :

namespace System.Web.Mvc {
    public class QueryStringBuilder {
        private NameValueCollection collection;
        public QueryStringBuilder() {
            collection = System.Web.HttpUtility.ParseQueryString(string.Empty);
        }
        public QueryStringBuilder Add(string key, string value) {
            collection.Add(key, value);
            return this;
        }
        public QueryStringBuilder Remove(string key) {
            collection.Remove(key);
            return this;
        }
        public string this[string key] {
            get { return collection[key]; }
            set { collection[key] = value; }
        }
        public string ToString() {
            return collection.ToString();
        }
    }
}

사용법 예 :

QueryStringBuilder parameters = new QueryStringBuilder()
    .Add("view", ViewBag.PageView)
    .Add("page", ViewBag.PageNumber)
    .Add("size", ViewBag.PageSize);
string queryString = parameters.ToString();

1

PageBase 클래스에 다음 메소드를 추가했습니다.

protected void Redirect(string url)
    {
        Response.Redirect(url);
    }
protected void Redirect(string url, NameValueCollection querystrings)
    {
        StringBuilder redirectUrl = new StringBuilder(url);

        if (querystrings != null)
        {
            for (int index = 0; index < querystrings.Count; index++)
            {
                if (index == 0)
                {
                    redirectUrl.Append("?");
                }

                redirectUrl.Append(querystrings.Keys[index]);
                redirectUrl.Append("=");
                redirectUrl.Append(HttpUtility.UrlEncode(querystrings[index]));

                if (index < querystrings.Count - 1)
                {
                    redirectUrl.Append("&");
                }
            }
        }

        this.Redirect(redirectUrl.ToString());
    }

전화 :

NameValueCollection querystrings = new NameValueCollection();    
querystrings.Add("language", "en");
querystrings.Add("id", "134");
this.Redirect("http://www.mypage.com", querystrings);

1

QueryStrings로 작업 할 때 매우 유용한 확장 메소드를 작성했습니다. 종종 현재 QueryString으로 시작하여 사용하기 전에 수정하고 싶습니다. 같은 것

var res = Request.QueryString.Duplicate()
  .ChangeField("field1", "somevalue")
  .ChangeField("field2", "only if following is true", true)
  .ChangeField("id", id, id>0)
  .WriteLocalPathWithQuery(Request.Url)); //Uses context to write the path

자세한 내용 및 출처 : http://www.charlesrcook.com/archive/2008/07/23/c-extension-methods-for-asp.net-query-string-operations.aspx

기본이지만 스타일이 마음에 듭니다.


1

방금 내 2 센트를 던지고 싶었습니다.

public static class HttpClientExt
{
    public static Uri AddQueryParams(this Uri uri, string query)
    {
        var ub = new UriBuilder(uri);
        ub.Query = string.IsNullOrEmpty(uri.Query) ? query : string.Join("&", uri.Query.Substring(1), query);
        return ub.Uri;
    }

    public static Uri AddQueryParams(this Uri uri, IEnumerable<string> query)
    {
        return uri.AddQueryParams(string.Join("&", query));
    } 

    public static Uri AddQueryParams(this Uri uri, string key, string value)
    {
        return uri.AddQueryParams(string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)));
    }

    public static Uri AddQueryParams(this Uri uri, params KeyValuePair<string,string>[] kvps)
    {
        return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
    }

    public static Uri AddQueryParams(this Uri uri, IDictionary<string, string> kvps)
    {
        return uri.AddQueryParams(kvps.Select(kvp => string.Join("=", HttpUtility.UrlEncode(kvp.Key), HttpUtility.UrlEncode(kvp.Value))));
    }

    public static Uri AddQueryParams(this Uri uri, NameValueCollection nvc)
    {
        return uri.AddQueryParams(nvc.AllKeys.SelectMany(nvc.GetValues, (key, value) => string.Join("=", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value))));
    }
}

문서 는 비어 있지 않은 경우로 uri.Query시작하며 ?수정하려는 경우 잘라야한다고 말합니다.

HttpUtility.UrlEncode에서 발견된다 System.Web.

용법:

var uri = new Uri("https://api.del.icio.us/v1/posts/suggest").AddQueryParam("url","http://stackoverflow.com")

1

우아한 아니지만, 내가 사용하지 않는 간단한 버전을 선택했다 NameValueCollecitons감싸 단지 빌더 패턴 - StringBuilder.

public class UrlBuilder
{
    #region Variables / Properties

    private readonly StringBuilder _builder;

    #endregion Variables / Properties

    #region Constructor

    public UrlBuilder(string urlBase)
    {
        _builder = new StringBuilder(urlBase);
    }

    #endregion Constructor

    #region Methods

    public UrlBuilder AppendParameter(string paramName, string value)
    {
        if (_builder.ToString().Contains("?"))
            _builder.Append("&");
        else
            _builder.Append("?");

        _builder.Append(HttpUtility.UrlEncode(paramName));
        _builder.Append("=");
        _builder.Append(HttpUtility.UrlEncode(value));

        return this;
    }

    public override string ToString()
    {
        return _builder.ToString();
    }

    #endregion Methods
}

기존 답변에 따라 HttpUtility.UrlEncode전화 를 사용했습니다 . 다음과 같이 사용됩니다.

string url = new UrlBuilder("http://www.somedomain.com/")
             .AppendParameter("a", "true")
             .AppendParameter("b", "muffin")
             .AppendParameter("c", "muffin button")
             .ToString();
// Result: http://www.somedomain.com?a=true&b=muffin&c=muffin%20button

1
// USAGE
[TestMethod]
public void TestUrlBuilder()
{
    Console.WriteLine(
        new UrlBuilder("http://www.google.com?A=B")
            .AddPath("SomePathName")
            .AddPath("AnotherPathName")
            .SetQuery("SomeQueryKey", "SomeQueryValue")
            .AlterQuery("A", x => x + "C"));
}

산출:

http://www.google.com/SomePathName/AnotherPathName?A=BC&SomeQueryKey=SomeQueryValue

코드; 어딘가에 나에게 고마워 할 수 있습니다. : D

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

// By Demetris Leptos
namespace TheOperator.Foundation.Web
{
    public class UrlBuilder
    {
        public string Scheme { get; set; }

        public string Host { get; set; }

        public int? Port { get; set; }

        public List<string> Paths { get; set; }

        public SortedDictionary<string, string> QueryPairs { get; set; }

        public UrlBuilder(string url)
        {
            this.Paths = new List<string>();
            this.QueryPairs = new SortedDictionary<string, string>();

            string path = null;
            string query = null;
            Uri relativeUri = null;
            if (!Uri.TryCreate(url, UriKind.Relative, out relativeUri))
            {
                var uriBuilder = new UriBuilder(url);
                this.Scheme = uriBuilder.Scheme;
                this.Host = uriBuilder.Host;
                this.Port = uriBuilder.Port;
                path = uriBuilder.Path;
                query = uriBuilder.Query;
            }
            else
            {
                var queryIndex = url.IndexOf('?');
                if (queryIndex >= 0)
                {
                    path = url.Substring(0, queryIndex);
                    query = url.Substring(queryIndex + 1);
                }
                else
                {
                    path = url;
                }
            }
            this.Paths.AddRange(path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries));
            if (query != null)
            {
                var queryKeyValuePairs = HttpUtility.ParseQueryString(query);
                foreach (var queryKey in queryKeyValuePairs.AllKeys)
                {
                    this.QueryPairs[queryKey] = queryKeyValuePairs[queryKey];
                }
            }
        }

        public UrlBuilder AddPath(string value)
        {
            this.Paths.Add(value);
            return this;
        }

        public UrlBuilder SetQuery(string key, string value)
        {
            this.QueryPairs[key] = value;
            return this;
        }

        public UrlBuilder RemoveQuery(string key)
        {
            this.QueryPairs.Remove(key);
            return this;
        }

        public UrlBuilder AlterQuery(string key, Func<string, string> alterMethod, bool removeOnNull = false)
        {
            string value;
            this.QueryPairs.TryGetValue(key, out value);
            value = alterMethod(value);
            if (removeOnNull && value == null)
            {
                return this.RemoveQuery(key);
            }
            else
            {
                return this.SetQuery(key, value);
            }
        }

        public override string ToString()
        {
            var path = !string.IsNullOrWhiteSpace(this.Host)
                ? string.Join("/", this.Host, string.Join("/", this.Paths))
                : string.Join("/", this.Paths);
            var query = string.Join("&", this.QueryPairs.Select(x => string.Concat(x.Key, "=", HttpUtility.UrlEncode(x.Value))));
            return string.Concat(
                !string.IsNullOrWhiteSpace(this.Scheme) ? string.Concat(this.Scheme, "://") : null,
                path,
                !string.IsNullOrWhiteSpace(query) ? string.Concat("?", query) : null);
        }
    }
}

1

나는 DSO가 제안한 솔루션 (7:29의 Aug 2 '11에 답변 됨)과 함께 갔으며 그의 솔루션에는 HttpUtility를 사용할 필요가 없습니다. 그러나 Dotnetpearls에 게시 된 기사에 따르면 NameValueCollection 을 사용하는 것보다 Dictionary를 사용하는 것이 성능이 더 빠릅니다. 다음은 NameValueCollection 대신 Dictionary를 사용하도록 수정 된 DSO의 솔루션입니다.

    public static Dictionary<string, string> QueryParametersDictionary()
    {
        var dictionary = new Dictionary<string, string>();
        dictionary.Add("name", "John Doe");
        dictionary.Add("address.city", "Seattle");
        dictionary.Add("address.state_code", "WA");
        dictionary.Add("api_key", "5352345263456345635");

        return dictionary;
    }

    public static string ToQueryString(Dictionary<string, string> nvc)
    {
        StringBuilder sb = new StringBuilder();

        bool first = true;

        foreach (KeyValuePair<string, string> pair in nvc)
        {
                if (!first)
                {
                    sb.Append("&");
                }

                sb.AppendFormat("{0}={1}", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value));

                first = false;
        }

        return sb.ToString();
    }

1

다음과 같은 방법으로 쿼리 문자열을 URL에 추가 할 수 있습니다.

  1. 이름 값 수집 객체를 만듭니다.
  2. 쿼리 문자열 항목과 해당 값을이 개체에 추가
  3. 이 이름 값 수집 객체를 코드가 아래 링크에 제공된 URL로 인코딩하십시오.

https://blog.codingnovice.com/blog

public ActionResult Create()
{
    //declaring name value collection object
    NameValueCollection collection = new NameValueCollection();

    //adding new value to the name value collection object
    collection.Add("Id1", "wwe323");
    collection.Add("Id2", "454w");
    collection.Add("Id3", "tyt5656");
    collection.Add("Id4", "343wdsd");

    //generating query string
    string url = GenerateQueryString(collection);

    return View();
}

private string GenerateQueryString(NameValueCollection collection)
{
    var querystring = (
        from key in collection.AllKeys
        from value in collection.GetValues(key)
        select string.Format("{0}={1}",
            HttpUtility.UrlEncode(key),
            HttpUtility.UrlEncode(value))
    ).ToArray();
    return "?" + string.Join("&", querystring);
}

0

다른 답변의 힌트를 사용하여 면도기 프로젝트 도우미를 작성했습니다.

ParseQueryString 비즈니스는 현재 요청의 QueryString 오브젝트를 조작 할 수 없으므로 필요합니다.

@helper GetQueryStringWithValue(string key, string value) {
    var queryString = System.Web.HttpUtility.ParseQueryString(HttpContext.Current.Request.QueryString.ToString());
    queryString[key] = value;
    @Html.Raw(queryString.ToString())
}

나는 이것을 다음과 같이 사용한다 :

location.search = '?@Helpers.GetQueryStringWithValue("var-name", "var-value")';

둘 이상의 값을 사용하려면 매개 변수를 Dictionary로 변경하고 쌍을 쿼리 문자열에 추가하십시오.


0

아래 코드는 ILSpy를 통해 의 HttpValueCollection구현 에서 제외되어 ToStringname = value 쿼리 문자열을 제공합니다.

불행히도 HttpValueCollection은를 사용하는 경우에만 반환되는 내부 클래스입니다 HttpUtility.ParseQueryString(). 모든 viewstate 부분을 제거하고 기본적으로 인코딩합니다.

public static class HttpExtensions
{
    public static string ToQueryString(this NameValueCollection collection)
    {
        // This is based off the NameValueCollection.ToString() implementation
        int count = collection.Count;
        if (count == 0)
            return string.Empty;

        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < count; i++)
        {
            string text = collection.GetKey(i);
            text = HttpUtility.UrlEncodeUnicode(text);
            string value = (text != null) ? (text + "=") : string.Empty;
            string[] values = collection.GetValues(i);
            if (stringBuilder.Length > 0)
            {
                stringBuilder.Append('&');
            }
            if (values == null || values.Length == 0)
            {
                stringBuilder.Append(value);
            }
            else
            {
                if (values.Length == 1)
                {
                    stringBuilder.Append(value);
                    string text2 = values[0];
                    text2 = HttpUtility.UrlEncodeUnicode(text2);
                    stringBuilder.Append(text2);
                }
                else
                {
                    for (int j = 0; j < values.Length; j++)
                    {
                        if (j > 0)
                        {
                            stringBuilder.Append('&');
                        }
                        stringBuilder.Append(value);
                        string text2 = values[j];
                        text2 = HttpUtility.UrlEncodeUnicode(text2);
                        stringBuilder.Append(text2);
                    }
                }
            }
        }

        return stringBuilder.ToString();
    }
}

0

이것은 약간 더 컴팩트 한 것을 제외하고는 허용되는 답변과 동일합니다.

private string ToQueryString(NameValueCollection nvc)
{
    return "?" + string.Join("&", nvc.AllKeys.Select(k => string.Format("{0}={1}", 
        HttpUtility.UrlEncode(k), 
        HttpUtility.UrlEncode(nvc[k]))));
}

0

VB.NET 버전의 최고 답변이 필요한 사람들을 위해 :

Public Function ToQueryString(nvc As System.Collections.Specialized.NameValueCollection) As String
    Dim array As String() = nvc.AllKeys.SelectMany(Function(key As String) nvc.GetValues(key), Function(key As String, value As String) String.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(key), System.Web.HttpUtility.UrlEncode(value))).ToArray()
    Return "?" + String.Join("&", array)
End Function

그리고 LINQ가없는 버전 :

Public Function ToQueryString(nvc As System.Collections.Specialized.NameValueCollection) As String
    Dim lsParams As New List(Of String)()

    For Each strKey As String In nvc.AllKeys
        Dim astrValue As String() = nvc.GetValues(strKey)

        For Each strValue As String In astrValue
            lsParams.Add(String.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(strKey), System.Web.HttpUtility.UrlEncode(strValue)))
        Next ' Next strValue
    Next ' strKey
    Dim astrParams As String() = lsParams.ToArray()
    lsParams.Clear()
    lsParams = Nothing

    Return "?" + String.Join("&", astrParams)
End Function ' ToQueryString

그리고 LINQ가없는 C # 버전 :

    public static string ToQueryString(System.Collections.Specialized.NameValueCollection nvc)
    {
        List<string> lsParams = new List<string>();

        foreach (string strKey in nvc.AllKeys)
        {
            string[] astrValue = nvc.GetValues(strKey);

            foreach (string strValue in astrValue)
            {
                lsParams.Add(string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(strKey), System.Web.HttpUtility.UrlEncode(strValue)));
            } // Next strValue

        } // Next strKey

        string[] astrParams =lsParams.ToArray();
        lsParams.Clear();
        lsParams = null;

        return "?" + string.Join("&", astrParams);
    } // End Function ToQueryString
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.