diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs index 38b4e645ec6..c231aeac5ab 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs @@ -16,6 +16,8 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -1463,10 +1465,8 @@ protected override void ProcessRecord() StreamReader reader = null; try { - reader = new(StreamHelper.GetResponseStream(response)); - - // Remove HTML tags making it easier to read - detailMsg = System.Text.RegularExpressions.Regex.Replace(reader.ReadToEnd(), "<[^>]*>", string.Empty); + reader = new StreamReader(StreamHelper.GetResponseStream(response)); + detailMsg = FormatErrorMessage(reader.ReadToEnd(), contentType); } catch { @@ -1821,6 +1821,60 @@ private static StreamContent GetMultipartFileContent(object fieldName, FileInfo return result; } + + private static string FormatErrorMessage(string error, string contentType) + { + string formattedError = null; + + try + { + if (ContentHelper.IsXml(contentType)) + { + XmlDocument doc = new(); + doc.LoadXml(error); + + XmlWriterSettings settings = new XmlWriterSettings { + Indent = true, + NewLineOnAttributes = true, + OmitXmlDeclaration = true + }; + + if (doc.FirstChild is XmlDeclaration) + { + XmlDeclaration decl = doc.FirstChild as XmlDeclaration; + settings.Encoding = Encoding.GetEncoding(decl.Encoding); + } + + StringBuilder stringBuilder = new(); + using XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, settings); + doc.Save(xmlWriter); + string xmlString = stringBuilder.ToString(); + + formattedError = Environment.NewLine + xmlString; + } + else if (ContentHelper.IsJson(contentType)) + { + JsonNode jsonNode = JsonNode.Parse(error); + JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = true }; + string jsonString = jsonNode.ToJsonString(options); + + formattedError = Environment.NewLine + jsonString; + } + } + catch + { + // Ignore errors + } + + if (string.IsNullOrEmpty(formattedError)) + { + // Remove HTML tags making it easier to read + formattedError = System.Text.RegularExpressions.Regex.Replace(error, "<[^>]*>", string.Empty); + } + + return formattedError; + } + #endregion Helper Methods } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 index 43363edb6eb..35262d8bf41 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 @@ -785,6 +785,35 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" { $result.Error.FullyQualifiedErrorId | Should -Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand" } + It "Validate Invoke-WebRequest returns errors in exception" -TestCases @( + @{ type = "XML" + query = @{ + contenttype = 'application/xml' + body = '418I am a teapot!!!' + statuscode = 418 + responsephrase = "I am a teapot" + } + expectederror = $IsWindows ? "`r`n`r`n 418`r`n I am a teapot!!!`r`n" : "`n`n 418`n I am a teapot!!!`n" + } + + @{ type = "Json" + query = @{ + contenttype = 'application/json' + body = '{"error":{"code":"418", "message":"I am a teapot!!!"}}' + statuscode = 418 + responsephrase = "I am a teapot" + } + expectederror = $IsWindows ? "`r`n{`r`n `"error`": {`r`n `"code`": `"418`",`r`n `"message`": `"I am a teapot!!!`"`r`n }`r`n}" : "`n{`n `"error`": {`n `"code`": `"418`",`n `"message`": `"I am a teapot!!!`"`n }`n}" + } + ) { + param($query, $expectederror) + $uri = Get-WebListenerUrl -Test 'Response' -Query $query + $command = "Invoke-WebRequest -Uri '$uri'" + $result = ExecuteWebCommand -command $command + + $result.Error.ErrorDetails.Message | Should -Be $expectederror + } + It "Validate Invoke-WebRequest returns empty RelationLink property if there is no Link Header" { $uri = $uri = Get-WebListenerUrl -Test 'Get' $command = "Invoke-WebRequest -Uri '$uri'"