【问题标题】:Powershell determine new URL of a permanently moved (redirected) resourcePowershell 确定永久移动(重定向)资源的新 URL
【发布时间】:2017-08-08 17:22:47
【问题描述】:

我在 Linux 上通过 AppImage 使用 Powershell Core v6-beta.5。有没有办法找出 301 重定向的“新”位置?

Invoke-WebRequest -Method HEAD http://SomethingThatThrows301.com/ -MaximumRedirection 0 引发错误 (Response status code does not indicate success: 301 (Moved Permanently))。

虽然错误确实提到移动是 301,但我仍然希望有一个合适的对象告诉我这一点,以及新地址。

有办法吗?

【问题讨论】:

    标签: powershell url-redirection http-status-code-301 powershell-v6.0


    【解决方案1】:

    注意:下面的所有代码都适用于 Windows PowerShellPowerShell Core,适用于所有支持的平台,最多 50 个默认重定向。

    假设:

    • 您不关心特定的 3xx 重定向状态代码和
    • 您只需要知道最终目标网址(可能存在重定向链

    使用以下内容:

    [System.Net.HttpWebRequest]::Create('http://cnn.com').GetResponse().ResponseUri.AbsoluteUri
    

    这会产生(注意目标 URL 的 www.):

    http://www.cnn.com
    

    下面是高级便利函数Get-UrlRedirection的源代码,它封装了功能,提供最终目标URL的解析和重定向URL链的枚举。

    示例调用:

    > Get-UrlRedirection http://cnn.com
    http://www.cnn.com
    
    > Get-UrlRedirection -Enumerate http://microsoft.com/about
    http://microsoft.com/about
    https://microsoft.com/about
    https://www.microsoft.com/about
    https://www.microsoft.com/about/
    https://www.microsoft.com/about/default.aspx
    https://www.microsoft.com/en-us/about/
    

    Function Get-UrlRedirection {
      [CmdletBinding()]
      Param (
        [Parameter(Mandatory, ValueFromPipeline)] [Uri] $Url,
        [switch] $Enumerate,
        [int] $MaxRedirections = 50 # Use same default as [System.Net.HttpWebRequest]
      )
    
      process {
        try {
    
          if ($Enumerate) { # Enumerate the whole redirection chain, from input URL to ultimate target,
                            # assuming the max. count of redirects is not exceeded.
            # We must walk the chain of redirections one by one.
            # If we disallow redirections, .GetResponse() fails and we must examine
            # the exception's .Response object to get the redirect target.
            $nextUrl = $Url
            $urls = @( $nextUrl.AbsoluteUri ) # Start with the input Uri
            $ultimateFound = $false
            # Note: We add an extra loop iteration so we can determine whether
            #       the ultimate target URL was reached or not.
            foreach($i in 1..$($MaxRedirections+1)) {
              Write-Verbose "Examining: $nextUrl"
              $request = [System.Net.HttpWebRequest]::Create($nextUrl)
              $request.AllowAutoRedirect = $False
              try {
                $response = $request.GetResponse()
                # Note: In .NET *Core* the .GetResponse() for a redirected resource
                #       with .AllowAutoRedirect -eq $False throws an *exception*.
                #       We only get here on *Windows*, with the full .NET Framework.
                #       We either have the ultimate target URL, or a redirection
                #       whose target URL is reflected in .Headers['Location']
                #       !! Syntax `.Headers.Location` does NOT work.
                $nextUrlStr = $response.Headers['Location']
                $response.Close()
                # If the ultimate target URL was reached (it was already
                # recorded in the previous iteration), and if so, simply exit the loop.
                if (-not $nextUrlStr) {
                  $ultimateFound = $true
                  break
                }
              } catch [System.Net.WebException] {
                # The presence of a 'Location' header implies that the
                # exception must have been triggered by a HTTP redirection 
                # status code (3xx). 
                # $_.Exception.Response.StatusCode contains the specific code
                # (as an enumeration value that can be case to [int]), if needed.
                # !! Syntax `.Headers.Location` does NOT work.
                $nextUrlStr = try { $_.Exception.Response.Headers['Location'] } catch {}
                # Not being able to get a target URL implies that an unexpected
                # error ocurred: re-throw it.
                if (-not $nextUrlStr) { Throw }
              }
              Write-Verbose "Raw target: $nextUrlStr"
              if ($nextUrlStr -match '^https?:') { # absolute URL
                $nextUrl = $prevUrl = [Uri] $nextUrlStr
              } else { # URL without scheme and server component
                $nextUrl = $prevUrl = [Uri] ($prevUrl.Scheme + '://' + $prevUrl.Authority + $nextUrlStr)
              }
              if ($i -le $MaxRedirections) { $urls += $nextUrl.AbsoluteUri }          
            }
            # Output the array of URLs (chain of redirections) as a *single* object.
            Write-Output -NoEnumerate $urls
            if (-not $ultimateFound) { Write-Warning "Enumeration of $Url redirections ended before reaching the ultimate target." }
    
          } else { # Resolve just to the ultimate target,
                    # assuming the max. count of redirects is not exceeded.
    
                    # Note that .AllowAutoRedirect defaults to $True.
            # This will fail, if there are more redirections than the specified 
            # or default maximum.
            $request = [System.Net.HttpWebRequest]::Create($Url)
            if ($PSBoundParameters.ContainsKey('MaxRedirections')) {
              $request.MaximumAutomaticRedirections = $MaxRedirections
            }
            $response = $request.GetResponse()
            # Output the ultimate target URL.
            # If no redirection was involved, this is the same as the input URL.
            $response.ResponseUri.AbsoluteUri
            $response.Close()
    
           }
    
          } catch {
            Write-Error $_ # Report the exception as a non-terminating error.
        }
      } # process
    
    }
    

    为了专注于代码,我省略了上面的基于注释的帮助;在这里 - 只需将其直接粘贴到函数定义上方:

    <#
    .SYNOPSIS
    Gets a URL's redirection target(s).
    
    .DESCRIPTION
    Given a URL, determines its redirection target(s), as indicated by responses
    with 3xx HTTP status codes.
    
    If the URL is not redirected, it is output as-is.
    
    By default, the ultimate target URL is determined (if there's a chain of
    redirections), but the number of redirections that are followed is limited
    to 50 by default, which you may change with -MaxRedirections.
    
    -Enumerate enumerates the redirection chain and returns an array of URLs.
    
    .PARAMETER Url
    The URL whose redirection target to determine.
    You may supply multiple URLs via the pipeline.
    
    .PARAMETER MaxRedirections
    Limits the number of redirections that are followed, 50 by default.
    If the limit is exceeded, a non-terminating error is reported.
    
    .PARAMETER Enumerate
    Enumerates the chain of redirections, if applicable, starting with
    the input URL itself, and outputs it as an array.
    
    If the number of actual redirections doesn't exceed the specified or default
    -MaxRedirections value, the entire chain up to the ultimate target URL is
    enumerated.
    Otherwise, a warning is issued to indicate that the ultimate target URL wasn't
    reached.
    
    All URLs are output in absolute form, even if the targets are defined as
    relative URLs.
    
    Note that, in order to support multiple input URLs via the pipeline, each
    array representing a redirection chain is output as a *single* object, so
    with multiple input URLs you'll get an array of arrays as output.
    
    .EXAMPLE
    > Get-UrlRedirection http://cnn.com
    http://www.cnn.com
    
    .EXAMPLE
    > Get-UrlRedirection -Enumerate http://microsoft.com/about
    http://microsoft.com/about
    https://microsoft.com/about
    https://www.microsoft.com/about
    https://www.microsoft.com/about/
    https://www.microsoft.com/about/default.aspx
    https://www.microsoft.com/en-us/about/
    
    .NOTES
    This function uses the [System.Net.HttpWebRequest] .NET class and was 
    inspired by http://www.powershellmagazine.com/2013/01/29/pstip-retrieve-a-redirected-url/
    #>
    

    【讨论】:

      【解决方案2】:

      如果您忽略抛出的错误,您将能够检查 HTTP 响应。新 URL 将位于 Location 标头中。

      试试下面的方法。

      $url="https://jigsaw.w3.org/HTTP/300/301.html"
      $resp = Invoke-WebRequest -Method HEAD $url -MaximumRedirection 0 -ErrorAction Ignore
      $code = $resp.StatusCode
      Write-Output "URL: $url"
      Write-Output "ErrorCode: $code"
      if($code -eq 301) {
          $loc = $resp.Headers.Location
          Write-Output "New URL: $loc"
      }
      

      【讨论】:

      • 嗯.. 这在 Arch 上的 PowerShell 6b5 AppImage 上不起作用。 resp.getType() You cannot call a method on a null-valued expression. 感谢您的帮助。除非出现更好的情况,否则我会等待并标记为答案。
      • 这在 Windows PowerShell 中运行良好,但在 PowerShell Core(在 Windows 和 Unix 上)中的行为发生了变化。 Whether the change was intentional or not,我不知道。顺便说一句:最好avoid Write-Host
      • 另外值得注意的是,这只会得到 immediate 重定向 URL,它可能是也可能不是 ultimate 目标 URL,因为重定向可以链式的,这并不少见。此外,根据重定向的定义方式,结果可能是 相对 URL。
      猜你喜欢
      • 2012-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多