Skip to content

Commit c74b2a7

Browse files
SteveL-MSFTdaxian-dbw
authored andcommitted
return HTTP response for error status as part of exception (PowerShell#3201)
1 parent 21c8a90 commit c74b2a7

3 files changed

Lines changed: 84 additions & 2 deletions

File tree

src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,27 @@
1919

2020
namespace Microsoft.PowerShell.Commands
2121
{
22+
/// <summary>
23+
/// Exception class for webcmdlets to enable returning HTTP error response
24+
/// </summary>
25+
public sealed class HttpResponseException : HttpRequestException
26+
{
27+
/// <summary>
28+
/// Constructor for HttpResponseException
29+
/// </summary>
30+
/// <param name="message">Message for the exception</param>
31+
/// <param name="response">Response from the HTTP server</param>
32+
public HttpResponseException (string message, HttpResponseMessage response) : base(message)
33+
{
34+
Response = response;
35+
}
36+
37+
/// <summary>
38+
/// HTTP error response
39+
/// </summary>
40+
public HttpResponseMessage Response { get; private set; }
41+
}
42+
2243
/// <summary>
2344
/// Base class for Invoke-RestMethod and Invoke-WebRequest commands.
2445
/// </summary>
@@ -347,14 +368,46 @@ protected override void ProcessRecord()
347368
WriteVerbose(reqVerboseMsg);
348369

349370
HttpResponseMessage response = GetResponse(client, request);
350-
response.EnsureSuccessStatusCode();
351371

352372
string contentType = ContentHelper.GetContentType(response);
353373
string respVerboseMsg = string.Format(CultureInfo.CurrentCulture,
354374
"received {0}-byte response of content type {1}",
355375
response.Content.Headers.ContentLength,
356376
contentType);
357377
WriteVerbose(respVerboseMsg);
378+
379+
if (!response.IsSuccessStatusCode)
380+
{
381+
string message = String.Format(CultureInfo.CurrentCulture, WebCmdletStrings.ResponseStatusCodeFailure,
382+
(int)response.StatusCode, response.ReasonPhrase);
383+
HttpResponseException httpEx = new HttpResponseException(message, response);
384+
ErrorRecord er = new ErrorRecord(httpEx, "WebCmdletWebResponseException", ErrorCategory.InvalidOperation, request);
385+
string detailMsg = "";
386+
StreamReader reader = null;
387+
try
388+
{
389+
reader = new StreamReader(StreamHelper.GetResponseStream(response));
390+
// remove HTML tags making it easier to read
391+
detailMsg = System.Text.RegularExpressions.Regex.Replace(reader.ReadToEnd(), "<[^>]*>","");
392+
}
393+
catch (Exception)
394+
{
395+
// catch all
396+
}
397+
finally
398+
{
399+
if (reader != null)
400+
{
401+
reader.Dispose();
402+
}
403+
}
404+
if (!String.IsNullOrEmpty(detailMsg))
405+
{
406+
er.ErrorDetails = new ErrorDetails(detailMsg);
407+
}
408+
ThrowTerminatingError(er);
409+
}
410+
358411
ProcessResponse(response);
359412
UpdateSession(response);
360413

@@ -542,4 +595,4 @@ internal long SetRequestContent(HttpRequestMessage request, IDictionary content)
542595
#endregion Helper Methods
543596
}
544597
}
545-
#endif
598+
#endif

src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,7 @@
210210
<data name="JsonDeserializationFailed" xml:space="preserve">
211211
<value>Conversion from JSON failed with error: {0}</value>
212212
</data>
213+
<data name="ResponseStatusCodeFailure" xml:space="preserve">
214+
<value>Response status code does not indicate success: {0} ({1}).</value>
215+
</data>
213216
</root>

test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,19 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
399399
$result = ExecuteWebCommand -command $command
400400
$result.Error | Should BeNullOrEmpty
401401
}
402+
403+
It "Validate Invoke-WebRequest returns HTTP errors in exception" {
404+
405+
$command = "Invoke-WebRequest -Uri http://httpbin.org/status/418"
406+
$result = ExecuteWebCommand -command $command
407+
408+
$result.Error.ErrorDetails.Message | Should Match "\-=\[ teapot \]"
409+
$result.Error.Exception | Should BeOfType Microsoft.PowerShell.Commands.HttpResponseException
410+
$result.Error.Exception.Response.StatusCode | Should Be 418
411+
$result.Error.Exception.Response.ReasonPhrase | Should Be "I'm a teapot"
412+
$result.Error.Exception.Message | Should Match ": 418 \(I'm a teapot\)\."
413+
$result.Error.FullyQualifiedErrorId | Should Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand"
414+
}
402415
}
403416

404417
Describe "Invoke-RestMethod tests" -Tags "Feature" {
@@ -642,6 +655,19 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
642655
$result = ExecuteWebCommand -command $command
643656
$result.Error | Should BeNullOrEmpty
644657
}
658+
659+
It "Validate Invoke-RestMethod returns HTTP errors in exception" {
660+
661+
$command = "Invoke-RestMethod -Uri http://httpbin.org/status/418"
662+
$result = ExecuteWebCommand -command $command
663+
664+
$result.Error.ErrorDetails.Message | Should Match "\-=\[ teapot \]"
665+
$result.Error.Exception | Should BeOfType Microsoft.PowerShell.Commands.HttpResponseException
666+
$result.Error.Exception.Response.StatusCode | Should Be 418
667+
$result.Error.Exception.Response.ReasonPhrase | Should Be "I'm a teapot"
668+
$result.Error.Exception.Message | Should Match ": 418 \(I'm a teapot\)\."
669+
$result.Error.FullyQualifiedErrorId | Should Be "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand"
670+
}
645671
}
646672

647673
Describe "Validate Invoke-WebRequest and Invoke-RestMethod -InFile" -Tags "Feature" {

0 commit comments

Comments
 (0)