.NET Core에서 쿼리 문자열 구문 분석 및 수정


113

쿼리 문자열이 포함 된 절대 URI가 제공됩니다. 쿼리 문자열에 값을 안전하게 추가하고 기존 매개 변수를 변경하려고합니다.

나는 택 &foo=bar하거나 정규 표현식을 사용 하지 않는 것을 선호합니다 . URI 이스케이프는 까다 롭습니다. 오히려이 작업을 올바르게 수행하고 이스케이프를 처리 할 수있는 기본 제공 메커니즘을 사용하고 싶습니다.

나는 한 발견 모두 사용이 답변을 . 그러나 이것은 ASP.NET Core이므로 더 이상 System.Web 어셈블리가 없으므로 더 이상 . HttpUtilityHttpUtility

코어 런타임을 대상으로하는 동안 ASP.NET Core에서이 작업을 수행하는 적절한 방법은 무엇입니까?


대안 Microsoft.AspNet.WebUtiltiesMono.HttpUtility라이브러리 일 수 있습니다 .
메이슨

나는 같은 글을 썼고, 여기를보세요 : neelbhatt40.wordpress.com/2017/07/06/…
Neel

2
2017 업데이트 : .NET Core 2.0에는 이제 HttpUtilityParseQueryString메서드가 포함됩니다 .
KTCO

답변:


152

ASP.NET Core 1 또는 2를 사용 Microsoft.AspNetCore.WebUtilities.QueryHelpers하는 경우 Microsoft.AspNetCore.WebUtilities 패키지 에서이 작업을 수행 할 수 있습니다 .

ASP.NET Core 3.0 이상을 사용 WebUtilities하는 경우은 이제 ASP.NET SDK의 일부이며 별도의 nuget 패키지 참조가 필요하지 않습니다.

사전으로 구문 분석하려면 :

var uri = new Uri(context.RedirectUri);
var queryDictionary = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query);

ParseQueryStringSystem.Web 과 달리 이것은 IDictionary<string, string[]>ASP.NET Core 1.x 또는 IDictionary<string, StringValues>ASP.NET Core 2.x 이상에서 유형의 사전을 반환 하므로 값은 문자열 모음입니다. 이것이 사전이 동일한 이름을 가진 여러 쿼리 문자열 매개 변수를 처리하는 방법입니다.

쿼리 문자열에 매개 변수를 추가하려면 다음에서 다른 방법을 사용할 수 있습니다 QueryHelpers.

var parametersToAdd = new System.Collections.Generic.Dictionary<string, string> { { "resource", "foo" } };
var someUrl = "http://www.google.com";
var newUri = Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(someUrl, parametersToAdd);

.net core 2.2를 사용하면 다음을 사용하여 쿼리 문자열을 얻을 수 있습니다.

var request = HttpContext.Request;
var query = request.query;
foreach (var item in query){
   Debug.WriteLine(item) 
}

다음과 같이 키 : 값 쌍 모음을 얻을 수 있습니다.

[0] {[companyName, ]}
[1] {[shop, ]}
[2] {[breath, ]}
[3] {[hand, ]}
[4] {[eye, ]}
[5] {[firstAid, ]}
[6] {[eyeCleaner, ]}

1
참고로 WebUtilities 패키지는 .net core 1.0과 호환되지 않습니다. Microsoft.AspNetCore.WebUtilities대신 필요할 수 있습니다 .
Jaime

6
@Jaime 굉장한 관찰! 그 업데이트로 내 답변을 편집하여 크레딧을받을 수 있습니까?
vcsjones

3
에디션이 완료되었습니다. 레거시 .net 버전의 경우 이전 네임 스페이스도 유지합니다.
Jaime

1
QueryHelpers.AddQueryString을 사용하면 자동으로 문자열을 UrlEscape-편리합니다.
Josh

2
반환 유형은 이제 IDictionary <string, string []>이 아닌 IDictionary <string, StringValues>입니다.
btlog

35

절대 URI를 가져와 ASP.NET Core 패키지 만 사용하여 쿼리 문자열을 조작하는 가장 쉽고 직관적 인 방법은 몇 가지 간단한 단계로 수행 할 수 있습니다.

패키지 설치

PM> 설치 패키지 Microsoft.AspNetCore.WebUtilities
PM> 설치 패키지 Microsoft.AspNetCore.Http.Extensions

중요한 수업

그것들을 지적하기 위해 우리가 사용할 두 가지 중요한 클래스 인 QueryHelpers , StringValues , QueryBuilder가 있습니다.

코드

// Raw URI including query string with multiple parameters
var rawurl = "https://bencull.com/some/path?key1=val1&key2=val2&key2=valdouble&key3=";

// Parse URI, and grab everything except the query string.
var uri = new Uri(rawurl);
var baseUri = uri.GetComponents(UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path, UriFormat.UriEscaped);

// Grab just the query string part
var query = QueryHelpers.ParseQuery(uri.Query);

// Convert the StringValues into a list of KeyValue Pairs to make it easier to manipulate
var items = query.SelectMany(x => x.Value, (col, value) => new KeyValuePair<string, string>(col.Key, value)).ToList();

// At this point you can remove items if you want
items.RemoveAll(x => x.Key == "key3"); // Remove all values for key
items.RemoveAll(x => x.Key == "key2" && x.Value == "val2"); // Remove specific value for key

// Use the QueryBuilder to add in new items in a safe way (handles multiples and empty values)
var qb = new QueryBuilder(items);
qb.Add("nonce", "testingnonce");
qb.Add("payerId", "pyr_");

// Reconstruct the original URL with new query string
var fullUri = baseUri + qb.ToQueryString();

변경 사항에 대한 최신 정보를 얻으려면 여기에 대한 내 블로그 게시물을 확인하십시오. http://benjii.me/2017/04/parse-modify-query-strings-asp-net-core/


17

HttpRequest인터페이스 Query를 통해 구문 분석 된 쿼리 문자열을 노출 하는 속성이 IReadableStringCollection있습니다.

/// <summary>
/// Gets the query value collection parsed from owin.RequestQueryString.
/// </summary>
/// <returns>The query value collection parsed from owin.RequestQueryString.</returns>
public abstract IReadableStringCollection Query { get; }

GitHub에 대한이 토론 에서도이를 지적합니다.


10

이 함수는 반환 Dictionary<string, string>되며 Microsoft.xxx호환성을 위해 사용하지 않습니다.

양쪽에서 매개 변수 인코딩 허용

중복 키 허용 (마지막 값 반환)

var rawurl = "https://emp.com/some/path?key1.name=a%20line%20with%3D&key2=val2&key2=valdouble&key3=&key%204=44#book1";
var uri = new Uri(rawurl);
Dictionary<string, string> queryString = ParseQueryString(uri.Query);

// queryString return:
// key1.name, a line with=
// key2, valdouble
// key3, 
// key 4, 44

public Dictionary<string, string> ParseQueryString(string requestQueryString)
{
    Dictionary<string, string> rc = new Dictionary<string, string>();
    string[] ar1 = requestQueryString.Split(new char[] { '&', '?' });
    foreach (string row in ar1)
    {
        if (string.IsNullOrEmpty(row)) continue;
        int index = row.IndexOf('=');
        if (index < 0) continue;
        rc[Uri.UnescapeDataString(row.Substring(0, index))] = Uri.UnescapeDataString(row.Substring(index + 1)); // use Unescape only parts          
     }
     return rc;
}

이것은 작동하지만, 해당 행에 '='가 포함되지 않을 가능성이 있으므로 하위 문자열을 시작하기 전에 인덱스 검사를 추가해야합니다. 예외가 발생합니다.
Taurib

1
도움을 주셔서 감사의 @Taurib가 변경
바그너 페레이라

1
경고 : 사전이 <string, string>으로 설정되어 있으므로 쿼리에 배열이있는 경우 작동하지 않습니다! (예 : "? item = 1 & item = 2") Workaraound : .net core 3.1에 대해 IEnumerable <KeyValuePair <string, string >> 또는 Dictionary <string, StringValues> 사용
theCuriousOne

감사합니다. @theCuriousOne,이 루틴에서 "중복 키 허용 (마지막 값 반환)"이라는 마지막 값을 반환합니다. 솔루션은 모든 값을 반환해도 괜찮습니다.
Wagner Pereira

1

Microsoft.AspNetCore.WebUtilities주요 버전 업데이트 (1.xx에서 2.xx로) 가있는 상위 답변이 올바른 것으로 플래그가 지정된 이후로 시간이 지남에 유의하는 것이 중요합니다 .

즉, 빌드 netcoreapp1.1하는 경우 다음을 실행해야 최신 지원 버전이 설치됩니다 1.1.2.

Install-Package Microsoft.AspNetCore.WebUtilities -Version 1.1.2


1

나는 이것을 확장 방법으로 사용하고 여러 매개 변수와 함께 작동합니다.

public static string AddOrReplaceQueryParameter(this HttpContext c, params string[] nameValues)
    {
        if (nameValues.Length%2!=0)
        {
            throw new Exception("nameValues: has more parameters then values or more values then parameters");
        }
        var qps = new Dictionary<string, StringValues>();
        for (int i = 0; i < nameValues.Length; i+=2)
        {
            qps.Add(nameValues[i], nameValues[i + 1]);
        }
        return c.AddOrReplaceQueryParameters(qps);
    }

public static string AddOrReplaceQueryParameters(this HttpContext c, Dictionary<string,StringValues> pvs)
    {
        var request = c.Request;
        UriBuilder uriBuilder = new UriBuilder
        {
            Scheme = request.Scheme,
            Host = request.Host.Host,
            Port = request.Host.Port ?? 0,
            Path = request.Path.ToString(),
            Query = request.QueryString.ToString()
        };

        var queryParams = QueryHelpers.ParseQuery(uriBuilder.Query);

        foreach (var (p,v) in pvs)
        {
            queryParams.Remove(p);
            queryParams.Add(p, v);
        }

        uriBuilder.Query = "";
        var allQPs = queryParams.ToDictionary(k => k.Key, k => k.Value.ToString());
        var url = QueryHelpers.AddQueryString(uriBuilder.ToString(),allQPs);

        return url;
    }

보기의 예를 들어 다음 및 이전 링크 :

var next = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex+1+"");

var prev = Context.Request.HttpContext.AddOrReplaceQueryParameter("page",Model.PageIndex-1+"");
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.