【问题标题】:URI Comparer Ignoring Query String OrderURI 比较器忽略查询字符串顺序
【发布时间】:2017-06-25 22:50:31
【问题描述】:

可以使用System.Uri.Compare 比较两个 URI。
然而,比较http://example.com/pages?pageStart=100&pageSize=50http://example.com/pages?pageSize=50&pageStart=100 表明这两个URI 是不同的。有什么方法可以让比较忽略查询字符串中值出现的顺序;因为这两个 URI 在功能上是相同的。

我正在考虑编写一个包装器方法来在调用标准比较函数之前按顺序排列查询字符串参数,但想先检查是否有现成的解决方案,因为比较似乎很奇怪还没有这个选项。


更新

这是我提出的用于比较两个 URI 的解决方案,无论它们的查询字符串参数顺序如何。如上所述,这是通过首先对每个 URI 的查询字符串进行排序来实现的。但是,如果 .net 中有现有的解决方案,我宁愿放弃我的代码并使用它。

function CompareUri ($GivenUri, $ShouldBeUri) {
    $uriComponentsOptions = ([UriComponents]::AbsoluteUri)
    $uriFormatOptions = ([UriFormat]::SafeUnescaped)
    $stringComparisonOptions = ([StringComparison]::OrdinalIgnoreCase)
    $a = OrderUriQueryString($GivenUri)
    $b = OrderUriQueryString($ShouldBeUri)
    [Uri]::Compare($a, $b, $uriComponentsOptions, $uriFormatOptions, $stringComparisonOptions)
}

function OrderUriQueryString($Uri) {
    [System.UriBuilder]$UriBuilder = New-Object -TypeName 'System.UriBuilder' -ArgumentList $Uri
    [System.Collections.Specialized.NameValueCollection]$Query = [System.Web.HttpUtility]::ParseQueryString($UriBuilder.Query)
    [System.Collections.Specialized.NameValueCollection]$Query2 = [System.Web.HttpUtility]::ParseQueryString('') #we have to initialise this way as HttpValueCollection has no public constructor (https://referencesource.microsoft.com/#system.web/HttpValueCollection.cs,fde6b9ec5f1ed58a,references)  
    $Query.AllKeys | sort | %{ $Query2.Add($_, $Query[$_]) }
    $UriBuilder.Query = $Query2.ToString()
    $UriBuilder.ToString()
}

【问题讨论】:

    标签: c# .net powershell uri query-string


    【解决方案1】:

    我知道这与您所做的基本相同,但如果您真的只是希望比较在代码中看起来更简洁,您可以使用 Update-TypeData 修改 System.Uri 类型并使用它而不是两个单独的函数.它不是原生 .NET,基本上使用与您相同的代码,但至少比较调用看起来更简洁一些。

    Update-TypeData -MemberType "ScriptMethod" -MemberName "SortedQueryStringCompare" -TypeName "System.Uri" -ErrorAction "SilentlyContinue" -Value {
        Param (
            [System.URI]$ComparingURI
        )
    
        $Sorter = {
            $UriBuilder = [System.UriBuilder]$args[0]
    
            $UnsortedQuery = [System.Web.HttpUtility]::ParseQueryString($UriBuilder.Query)
            $SortedQuery = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
            $UnsortedQuery.AllKeys | Sort-Object | ForEach-Object { 
                $SortedQuery.Add($_, $UnsortedQuery[$_])
            }
    
            $UriBuilder.Query = $SortedQuery.ToString()
            return $UriBuilder.ToString()
        }
    
        $UriComponentsOptions = ([UriComponents]::AbsoluteUri)
        $UriFormatOptions = ([UriFormat]::SafeUnescaped)
        $StringComparisonOptions = ([StringComparison]::OrdinalIgnoreCase)
        $OriginUriString = . $Sorter $This
        $ComparingUriString = . $Sorter $ComparingURI
    
        return [System.Uri]::Compare($OriginUriString,
                                     $ComparingUriString,
                                     $UriComponentsOptions,
                                     $UriFormatOptions,
                                     $StringComparisonOptions)
    }
    
    $e1 = [System.Uri]"http://example.com/pages?pageStart=100&pageSize=50"
    $e2 = [System.Uri]"http://example.com/pages?pageSize=50&pageStart=100"
    
    $e1.SortedQueryStringCompare($e2)
    

    或者只是使用SortQueryString 方法来执行此操作,然后使用该方法调用Compare

    Update-TypeData -MemberType "ScriptMethod" -MemberName "SortQueryString" -TypeName "System.Uri" -ErrorAction "SilentlyContinue" -Value {
        $UriBuilder = [System.UriBuilder]$this
    
        $UnsortedQuery = [System.Web.HttpUtility]::ParseQueryString($UriBuilder.Query)
        $SortedQuery = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
        $UnsortedQuery.AllKeys | Sort-Object | ForEach-Object { 
            $SortedQuery.Add($_, $UnsortedQuery[$_])
        }
    
        $UriBuilder.Query = $SortedQuery.ToString()
        return [System.Uri]$UriBuilder.ToString()
    }
    
    $UriComponentsOptions = ([UriComponents]::AbsoluteUri)
    $UriFormatOptions = ([UriFormat]::SafeUnescaped)
    $StringComparisonOptions = ([StringComparison]::OrdinalIgnoreCase)
    
    $e1 = [System.Uri]"http://example.com/pages?pageStart=100&pageSize=50"
    $e2 = [System.Uri]"http://example.com/pages?pageSize=50&pageStart=100"
    
    [System.Uri]::Compare($e1.SortQueryString(), 
                          $e2.SortQueryString(), 
                          $UriComponentsOptions, 
                          $UriFormatOptions, 
                          $StringComparisonOptions)
    

    【讨论】:

    • 感谢Update-TypeData 的提示;我以前没有遇到过那个。绝招。
    【解决方案2】:

    可能是查询参数的有序哈希表

    $queryURI = http://example.com/pages?
    
    $Parameters = [ordered] @{
                startsize = $startsize
                pagesize = $pagesize
    
            }
    

    然后使用给定的参数构建查询

    foreach ($item in $Parameters.GetEnumerator())
            {
    
                if ($item.value -ne $null)
                {
    
                    $query = [System.Web.HttpUtility]::ParseQueryString($uriBuilder.Query)
                    $query[$item.key] = $item.Value
    
                    $uriBuilder = [System.UriBuilder]::new($queryURI)
                    $uriBuilder.Query = $query.ToString()
                    $queryURI = $uriBuilder.ToString()
                }
    
    
    
            }
    

    这是一个完整的例子

    $QueryURI = 'http://example.com/pages?'
    
    
    Function Get-MyQueryURL
    {
        #takes 2 params 
        [CmdletBinding()]
        Param (
            [Parameter(Mandatory = $false,
                       ValueFromPipeline = $true,
                       ValueFromPipelineByPropertyName = $true)]
            [String[]]$startsize,
            [String[]]$pagesize
    
    
    
        ) #end param 
    
        Begin { }
        process
        {
    
            $Parameters = [ordered] @{
                startsize = $startsize
                pagesize = $pagesize
            }
    
    
    
    
            foreach ($item in $Parameters.GetEnumerator())
            {
    
                if ($item.value -ne $null)
                {
    
                    $query = [System.Web.HttpUtility]::ParseQueryString($uriBuilder.Query)
                    $query[$item.key] = $item.Value
    
                    $uriBuilder = [System.UriBuilder]::new($QueryURI)
                    $uriBuilder.Query = $query.ToString()
                    $QueryURI = $uriBuilder.ToString()
                }
    
    
    
            }
                Write-Host $QueryURI
        }
    }
    

    结果如下:查询字符串是有序的

    PS C:\Scripts> Get-MyQueryURL -startsize 100 -pagesize 50
    http://example.com:80/pages?startsize=100&pagesize=50
    
    PS C:\Scripts> Get-MyQueryURL -pagesize 50 -startsize 100
    http://example.com:80/pages?startsize=100&pagesize=50
    

    【讨论】:

    • 感谢您的建议。可悲的是[ordered] 保留了输入顺序,而不是按顺序将键排序,所以我仍然遇到必须以与 NameValueCollection 相同的方式手动排序键的问题/仍然有URI.Compare 需要的问题进行这种排序。
    • 我认为您可以按任何顺序输入查询字符串,但它们总是会变得有序——也就是说,它应该否定一组相同查询字符串的原始问题
    • 同意我使用它来构造 URI。但是,我有现有的 URI,我想将它们相互比较。
    • 你可以在查询字符串上使用-split
    猜你喜欢
    • 2013-05-08
    • 1970-01-01
    • 1970-01-01
    • 2016-11-01
    • 2011-02-24
    • 1970-01-01
    • 2016-01-29
    • 1970-01-01
    相关资源
    最近更新 更多