你看看这个“function Invoke-CurlRequest {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0)]
[string]$Url,
[string]$Method = "GET",
[hashtable]$Headers = @{},
[string]$Body,
[string]$ContentType = "application/json",
[ValidateSet("Raw", "Object")]
[string]$OutputType = "Raw",
[switch]$IncludeHeaders,
[switch]$FollowRedirect,
[int]$MaxRedirects = 10,
[int]$Timeout = 0,
[string]$OutputFile,
[switch]$Insecure
)
# 内部辅助函数:提取curl错误信息
function ExtractCurlError([string]$content) {
if ($content -match 'curl: \(\d+\) (.*)') {
return $matches[1]
}
return $content
}
# 内部辅助函数:提取API错误信息
function ExtractApiError($responseData) {
$errorKeys = @("error", "message", "detail", "description", "error_message")
foreach ($key in $errorKeys) {
if ($responseData -and $responseData.PSObject.Properties.Name -contains $key) {
return $responseData.$key
}
}
return "Unknown API error"
}
# 保存原始语言环境
$originalLang = $env:LANG
$originalLC_ALL = $env:LC_ALL
# 初始化响应对象
$response = [PSCustomObject]@{
Status = $null
Content = $null
Data = $null
ErrorMessage = $null
ErrorType = $null
Url = $Url
Method = $Method
StatusCode = $null
Headers = $null
Latency = $null
Size = $null
Timestamp = (Get-Date)
RawOutput = $null
}
# 计时器开始
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
try {
# 设置英文环境防止乱码
$env:LANG = 'C'
$env:LC_ALL = 'C'
# 构建 curl 命令参数
$curlArgs = @(
"--url", $Url, # 使用--url参数确保URL完整性
"-X", $Method,
"--silent",
"--show-error"
)
# 添加可选参数
if ($IncludeHeaders) { $curlArgs += "-i" }
if ($FollowRedirect) {
$curlArgs += "-L"
$curlArgs += "--max-redirs"
$curlArgs += $MaxRedirects
}
if ($Timeout -gt 0) {
$curlArgs += "--connect-timeout"
$curlArgs += [math]::Ceiling($Timeout/2)
$curlArgs += "--max-time"
$curlArgs += $Timeout
}
if ($Insecure) {
Write-Warning "SSL certificate validation disabled - security risk!"
$curlArgs += "--insecure"
}
# 添加默认User-Agent
if (-not $Headers.ContainsKey('User-Agent')) {
$Headers['User-Agent'] = "PowerShell-CurlTools/1.0"
}
# 添加内容类型头
if (-not $Headers.ContainsKey("Content-Type") -and $Body) {
$Headers["Content-Type"] = $ContentType
}
# 添加请求头
foreach ($key in $Headers.Keys) {
$headerValue = $Headers[$key]
$curlArgs += "-H"
$curlArgs += "`"$key`: $headerValue`""
}
# 添加请求体
if ($Body) {
$curlArgs += "-d"
$curlArgs += $Body
}
Write-Verbose "Executing curl: $($curlArgs -join ' ')"
# 特殊处理文件输出
if ($OutputFile) {
# 确保输出目录存在
$outputDir = [System.IO.Path]::GetDirectoryName($OutputFile)
if (-not [string]::IsNullOrWhiteSpace($outputDir) -and
-not (Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
}
# 直接保存到文件
$curlArgs += "-o"
$curlArgs += $OutputFile
# 执行命令
$process = Start-Process curl.exe -ArgumentList $curlArgs -NoNewWindow -PassThru -Wait
$curlExitCode = $process.ExitCode
# 处理结果
if ($curlExitCode -eq 0 -and (Test-Path $OutputFile)) {
$response.Status = "Success"
$response.Content = "File saved successfully"
$response.Size = (Get-Item $OutputFile).Length
$response.StatusCode = 200
} else {
$response.Status = "Error"
$response.ErrorMessage = "File save failed (exit $curlExitCode)"
$response.ErrorType = "FileWriteError"
$response.StatusCode = 0
}
return $response
}
# 执行 curl 命令(非文件模式)
$output = curl.exe @curlArgs 2>&1
$curlExitCode = $LASTEXITCODE
# 处理输出
$rawOutput = if ($null -ne $output) {
if ($output -is [array]) {
$output -join "`n"
} else {
$output.ToString()
}
} else {
""
}
$response.RawOutput = $rawOutput
$response.Latency = $stopwatch.ElapsedMilliseconds
# 处理 curl 错误
if ($curlExitCode -ne 0) {
$response.Status = "Error"
$response.ErrorMessage = ExtractCurlError $rawOutput
$response.Content = $rawOutput
$response.ErrorType = switch ($curlExitCode) {
6 { "DNSResolutionFailed" }
7 { "ConnectionFailed" }
23 { "FileWriteError" }
28 { "Timeout" }
default { "CurlError" }
}
$response.StatusCode = 0
return $response
}
# 记录响应大小
$response.Size = $rawOutput.Length
# 分离响应头和内容
$headerSection = $null
$responseBody = $rawOutput
$statusCode = 0
# 增强头部分离逻辑
if ($IncludeHeaders -or $rawOutput -match '^HTTP/') {
# 查找头部结束位置
$headerEnd = $rawOutput.IndexOf("`n`n")
if ($headerEnd -eq -1) { $headerEnd = $rawOutput.IndexOf("`r`n`r`n") }
# 分离头部和主体
if ($headerEnd -gt 0) {
$headerSection = $rawOutput.Substring(0, $headerEnd)
$responseBody = $rawOutput.Substring($headerEnd + 2)
# 解析状态码
if ($headerSection -match 'HTTP/\d+\.\d+\s+(\d{3})') {
$statusCode = [int]$matches[1]
}
# 解析响应头
$response.Headers = @{}
$headerLines = $headerSection -split "`r`n|`n"
foreach ($line in $headerLines) {
if ($line -match '^([^:]+):\s*(.+)') {
$response.Headers[$matches[1]] = $matches[2].Trim()
}
}
}
}
# 设置状态码
$response.StatusCode = $statusCode
# 设置响应状态
if ($statusCode -ge 400) {
$response.Status = "Error"
} elseif ($statusCode -eq 204) {
$response.Status = "NoContent"
} else {
$response.Status = "Success"
}
# 处理空响应
if ([string]::IsNullOrWhiteSpace($responseBody) -and $statusCode -eq 204) {
$response.Content = ""
return $response
}
# 原始输出模式
if ($OutputType -eq "Raw") {
$response.Content = $rawOutput
return $response
}
# 对象模式 - 尝试解析JSON
$response.Content = $responseBody
try {
$parsedData = $responseBody | ConvertFrom-Json -ErrorAction Stop
# 智能提取嵌套数据
if ($parsedData.PSObject.Properties.Name -contains "json") {
$response.Data = $parsedData.json
} elseif ($parsedData.PSObject.Properties.Name -contains "data") {
try {
$response.Data = $parsedData.data | ConvertFrom-Json
} catch {
$response.Data = $parsedData.data
}
} else {
$response.Data = $parsedData
}
# 如果是错误响应,提取错误信息
if ($response.Status -eq "Error") {
$response.ErrorMessage = ExtractApiError $response.Data
}
return $response
} catch {
# 非JSON响应
if ($response.Status -eq "Success") {
$response.Status = "NonJsonResponse"
}
return $response
}
} catch {
$response.Status = "Error"
$response.ErrorMessage = $_.Exception.Message
$response.Content = $_.Exception.Message
return $response
} finally {
# 恢复原始语言环境
$env:LANG = $originalLang
$env:LC_ALL = $originalLC_ALL
$stopwatch.Stop()
}
}
# 导出模块函数
Export-ModuleMember -Function Invoke-CurlRequest”跟你的对比一下 “function Invoke-CurlRequest {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0)]
[string]$Url,
[string]$Method = "GET",
[hashtable]$Headers = @{},
[string]$Body,
[string]$ContentType = "application/json",
[ValidateSet("Raw", "Object")]
[string]$OutputType = "Raw",
[switch]$IncludeHeaders,
[switch]$FollowRedirect,
[int]$MaxRedirects = 10,
[int]$Timeout = 0,
[string]$OutputFile,
[switch]$Insecure,
# 重试参数 (必须放在param块内)
[int]$RetryCount = 0,
[int]$RetryInterval = 1,
[int[]]$RetryOnStatusCodes = @(408, 429, 500, 502, 503, 504),
[switch]$RetryOnTimeout
)
# 保存原始语言环境
$originalLang = $env:LANG
$originalLC_ALL = $env:LC_ALL
# 重试计数器
$attempt = 0
$success = $false
$finalResponse = $null
# 内部辅助函数:提取curl错误信息 (修复作用域问题)
function Script:ExtractCurlError([string]$content) {
if ($content -match 'curl: \(\d+\) (.*)') {
return $matches[1]
}
return $content
}
# 内部辅助函数:提取API错误信息
function Script:ExtractApiError($responseData) {
$errorKeys = @("error", "message", "detail", "description", "error_message")
foreach ($key in $errorKeys) {
if ($null -ne $responseData -and
$responseData.PSObject.Properties.Name -contains $key) {
return $responseData.$key
}
}
return "Unknown API error"
}
do {
$attempt++
try {
# 初始化响应对象
$response = [PSCustomObject]@{
Status = $null
Content = $null
Data = $null
ErrorMessage = $null
ErrorType = $null
Url = $Url
Method = $Method
StatusCode = $null
Headers = $null
Latency = $null
Size = $null
Timestamp = (Get-Date)
RawOutput = $null
Attempt = $attempt
TotalAttempts = $RetryCount + 1
}
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
# 设置英文环境防止乱码 (修复中文乱码)
$env:LANG = 'C'
$env:LC_ALL = 'C'
# 修复URL空格问题:双重转义
$safeUrl = if ($Url -match '\s') {
"`"$($Url -replace '"', '\"')`""
} else {
$Url
}
# 构建curl命令参数
$curlArgs = @(
$safeUrl,
"-X", $Method,
"--silent",
"--show-error",
"--write-out", "`nHTTP_STATUS:%{http_code}`n"
)
# 添加可选参数
if ($IncludeHeaders) { $curlArgs += "-i" }
if ($FollowRedirect) {
$curlArgs += "-L"
$curlArgs += "--max-redirs"
$curlArgs += $MaxRedirects
}
if ($Timeout -gt 0) {
$curlArgs += "--connect-timeout"
$curlArgs += [math]::Ceiling($Timeout/2)
$curlArgs += "--max-time"
$curlArgs += $Timeout
}
if ($Insecure) {
Write-Warning "SSL certificate validation disabled - security risk!"
$curlArgs += "--insecure"
}
# 添加默认User-Agent
if (-not $Headers.ContainsKey('User-Agent')) {
$Headers['User-Agent'] = "PowerShell-CurlTools/1.0"
}
# 添加内容类型头
if (-not $Headers.ContainsKey("Content-Type") -and $Body) {
$Headers["Content-Type"] = $ContentType
}
# 修复特殊字符请求头问题:使用单引号
foreach ($key in $Headers.Keys) {
$headerValue = $Headers[$key] -replace "'", "\'"
$curlArgs += "-H"
$curlArgs += "'$key: $headerValue'"
}
# 添加请求体
if ($Body) {
$curlArgs += "-d"
$curlArgs += $Body
}
Write-Verbose "[Attempt $attempt/$($RetryCount + 1)] Executing curl: $($curlArgs -join ' ')"
# 文件下载处理
if ($OutputFile) {
# 确保输出目录存在 (修复权限问题)
$outputDir = [System.IO.Path]::GetDirectoryName($OutputFile)
if (-not [string]::IsNullOrWhiteSpace($outputDir) -and
-not (Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
}
# 使用临时文件确保原子写入
$tempFile = [System.IO.Path]::GetTempFileName()
$fileArgs = $curlArgs + @("-o", $tempFile)
try {
$process = Start-Process curl.exe -ArgumentList $fileArgs -NoNewWindow -PassThru -Wait
$curlExitCode = $process.ExitCode
if ($curlExitCode -eq 0 -and (Test-Path $tempFile)) {
Move-Item $tempFile $OutputFile -Force
$response.Status = "Success"
$response.Size = (Get-Item $OutputFile).Length
$response.StatusCode = 200
} else {
$response.Status = "Error"
$response.ErrorMessage = "File save failed (exit $curlExitCode)"
}
} finally {
if (Test-Path $tempFile) { Remove-Item $tempFile -Force }
}
$finalResponse = $response
$success = $true
break
}
# 执行curl命令
$tempFile = [System.IO.Path]::GetTempFileName()
try {
$tempArgs = $curlArgs + @("-o", $tempFile)
$process = Start-Process curl.exe -ArgumentList $tempArgs -NoNewWindow -PassThru -Wait
$curlExitCode = $process.ExitCode
if (Test-Path $tempFile) {
$rawOutput = Get-Content $tempFile -Raw -Encoding UTF8
$response.Size = (Get-Item $tempFile).Length
}
} finally {
Remove-Item $tempFile -Force
}
$response.RawOutput = $rawOutput
$response.Latency = $stopwatch.ElapsedMilliseconds
# 提取HTTP状态码
if ($rawOutput -match 'HTTP_STATUS:(\d{3})') {
$response.StatusCode = [int]$matches[1]
$rawOutput = $rawOutput -replace 'HTTP_STATUS:\d{3}', ''
}
# 错误处理
if ($curlExitCode -ne 0) {
$response.Status = "Error"
$response.ErrorMessage = (Script:ExtractCurlError $rawOutput)
$response.ErrorType = switch ($curlExitCode) {
6 { "DNSResolutionFailed" }
7 { "ConnectionFailed" }
23 { "WriteError" }
28 { "Timeout" }
default { "CurlError($curlExitCode)" }
}
# 重试逻辑
if ($attempt -le $RetryCount) {
$shouldRetry = $false
if ($response.ErrorType -in @("ConnectionFailed", "Timeout", "DNSResolutionFailed")) {
$shouldRetry = $true
}
elseif ($RetryOnTimeout -and $response.ErrorType -eq "Timeout") {
$shouldRetry = $true
}
elseif ($response.StatusCode -in $RetryOnStatusCodes) {
$shouldRetry = $true
}
if ($shouldRetry) {
# 指数退避算法
$waitTime = $RetryInterval * [math]::Pow(2, $attempt - 1)
$jitter = Get-Random -Minimum 0.1 -Maximum ($waitTime * 0.3)
$totalWait = [math]::Ceiling($waitTime + $jitter)
Write-Verbose "Retryable error detected. Waiting ${totalWait}s before retry..."
Start-Sleep -Seconds $totalWait
continue
}
}
$finalResponse = $response
$success = $true
break
}
# 响应处理 (简化版)
$response.Status = if ($response.StatusCode -ge 400) { "Error" } else { "Success" }
$response.Content = $rawOutput
# 尝试解析JSON
if ($OutputType -eq "Object" -and $rawOutput.Trim().StartsWith('{')) {
try {
$response.Data = $rawOutput | ConvertFrom-Json
} catch {
$response.ErrorMessage = "JSON parse error: $_"
}
}
$finalResponse = $response
$success = $true
} catch {
$errorMsg = $_.Exception.Message
$finalResponse = [PSCustomObject]@{
Status = "FatalError"
ErrorMessage = $errorMsg
Attempt = $attempt
}
if ($attempt -gt $RetryCount) {
$success = $true
} else {
$waitTime = $RetryInterval * [math]::Pow(2, $attempt - 1)
Start-Sleep -Seconds $waitTime
}
} finally {
# 恢复原始语言环境
$env:LANG = $originalLang
$env:LC_ALL = $originalLC_ALL
if ($stopwatch.IsRunning) { $stopwatch.Stop() }
}
} while ($attempt -le $RetryCount -and -not $success)
return $finalResponse
}
# 导出模块函数
Export-ModuleMember -Function Invoke-CurlRequest
”整合好了 再检查检查 发我完整版哈
最新发布