Skip to content

Commit 04a1fc3

Browse files
vdamewoodTravisEz13
authored andcommitted
Allow Web Cmdlets to Ignore HTTP Error Statuses (PowerShell#10466)
* Add -SkipHttpErrorCheck to web request cmdlets The -SkipHttpErrorCheck flag causes web request cmdlets (Invoke-WebRequest and Invoke-RestMethod) to ignore HTTP statuses that are error statuses and treat them as successful requests. This allows users to handle the responses using their own error handler and gives them access to the full, unmodified response body and headers. * Add -ResponseStatusVariable to Invoke-RestMethod This allows the user to specify a variable to set to the integer value of the respons's status code, Analogous to using -ResponseHeadersVariable to retrieve the headers of the response. This can be used to distinguish error messages from success messages when used with -SkipHttpErrorCheck. * Fix coding-style error The summary for the SkipHttpErrorCheck property didn't conform to style guidelines. This changes the summary to start with "Gets or sets". * Add tests for -SkipHttpErrorCheck This flag supresses terminating errors on web cmdlets. The tests are written to check that it properly supressed the errors. * Add test for -StatusCodeVariable Th -StatusCodeVariable parameter specifies an output variable for the status code with Invoke-RestMethod. This test makes sure it functions properly. * Fix typos and style for -SkipHttpErrorCheck tests Variables had different cases from each other and some parameter names were lower case. * Fix typos and style for -StatusCodeVariable test Variables had different cases from each other and some parameter names were lower case. * Add failure tests when missing -SkipHttpErrorCheck These tests ensure that Web Cmdlets fail when -SkipHttpErrorCheck is missing. * Clean up tests for -SkipHttpErrorCheck Per discussion on the pull requests. This commit fixes up style problems with the tests for -SkipHttpErrorCheck. * Add more status tests for -StatusCodeVariable Previously, the -StatusCodeVariable flag in Invoke-RestMethod only had tests for 200 status. This commit adds tests for 404 and 500 statuses and removes -SkipHttpErrorCheck from the 200 check. * Fix response body in -StatusCodeVariable test The test was copy/pastes from the 200 status test. The body indicated success. This commit fixes that so it is also an error indicator.
1 parent 8081e76 commit 04a1fc3

File tree

3 files changed

+114
-2
lines changed

3 files changed

+114
-2
lines changed

src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ public int MaximumFollowRelLink
7575
[Alias("RHV")]
7676
public string ResponseHeadersVariable { get; set; }
7777

78+
/// <summary>
79+
/// Gets or sets the variable name to use for storing the status code from the response.
80+
/// </summary>
81+
[Parameter]
82+
public string StatusCodeVariable { get; set; }
83+
7884
#endregion Parameters
7985

8086
#region Helper Methods
@@ -454,6 +460,12 @@ internal override void ProcessResponse(HttpResponseMessage response)
454460
StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this);
455461
}
456462

463+
if (!string.IsNullOrEmpty(StatusCodeVariable))
464+
{
465+
PSVariableIntrinsics vi = SessionState.PSVariable;
466+
vi.Set(StatusCodeVariable, (int)response.StatusCode);
467+
}
468+
457469
if (!string.IsNullOrEmpty(ResponseHeadersVariable))
458470
{
459471
PSVariableIntrinsics vi = SessionState.PSVariable;

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,12 @@ public virtual string CustomMethod
381381
[Parameter]
382382
public virtual SwitchParameter Resume { get; set; }
383383

384+
/// <summary>
385+
/// Gets or sets whether to skip checking HTTP status for error codes.
386+
/// </summary>
387+
[Parameter]
388+
public virtual SwitchParameter SkipHttpErrorCheck { get; set; }
389+
384390
#endregion
385391

386392
#endregion Virtual Properties
@@ -690,6 +696,11 @@ internal bool ShouldWriteToPipeline
690696
get { return (!ShouldSaveToOutFile || PassThru); }
691697
}
692698

699+
internal bool ShouldCheckHttpStatus
700+
{
701+
get { return !SkipHttpErrorCheck; }
702+
}
703+
693704
/// <summary>
694705
/// Determines whether writing to a file should Resume and append rather than overwrite.
695706
/// </summary>
@@ -1513,7 +1524,7 @@ protected override void ProcessRecord()
15131524
OutFile = null;
15141525
}
15151526

1516-
if (!_isSuccess)
1527+
if (ShouldCheckHttpStatus && !_isSuccess)
15171528
{
15181529
string message = string.Format(CultureInfo.CurrentCulture, WebCmdletStrings.ResponseStatusCodeFailure,
15191530
(int)response.StatusCode, response.ReasonPhrase);

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

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,13 @@ $redirectTests = @(
370370
Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
371371
BeforeAll {
372372
$WebListener = Start-WebListener
373+
$NotFoundQuery = @{
374+
statuscode = 404
375+
responsephrase = 'Not Found'
376+
contenttype = 'text/plain'
377+
body = 'oops'
378+
headers = "{}"
379+
}
373380
}
374381

375382
# Validate the output of Invoke-WebRequest
@@ -780,6 +787,23 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
780787
$result.Output.RelationLink["next"] | Should -BeExactly "${baseUri}?maxlinks=3&linknumber=2&type=${type}"
781788
}
782789

790+
It "Verify Invoke-WebRequest supresses terminating errors with -SkipHttpErrorCheck" {
791+
$uri = Get-WebListenerUrl -Test 'Response' -Query $NotFoundQuery
792+
$command = "Invoke-WebRequest -SkipHttpErrorCheck -Uri '$uri'"
793+
$result = ExecuteWebCommand -Command $command
794+
$result.output.StatusCode | Should -Be 404
795+
$result.output.Content | Should -BeExactly "oops"
796+
$result.error | Should -BeNullOrEmpty
797+
}
798+
799+
It "Verify Invoke-WebRequest terminates without -SkipHttpErrorCheck" {
800+
$uri = Get-WebListenerUrl -Test 'Response' -Query $NotFoundQuery
801+
$command = "Invoke-WebRequest -Uri '$uri'"
802+
$result = ExecuteWebCommand -Command $command
803+
$result.output | Should -BeNullOrEmpty
804+
$result.error | Should -Not -BeNullOrEmpty
805+
}
806+
783807
Context "Redirect" {
784808
It "Validates Invoke-WebRequest with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
785809
param($redirectType, $redirectedMethod)
@@ -1930,6 +1954,14 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
19301954
Describe "Invoke-RestMethod tests" -Tags "Feature", "RequireAdminOnWindows" {
19311955
BeforeAll {
19321956
$WebListener = Start-WebListener
1957+
1958+
$NotFoundQuery = @{
1959+
statuscode = 404
1960+
responsephrase = 'Not Found'
1961+
contenttype = 'application/json'
1962+
body = '{"message": "oops"}'
1963+
headers = "{}"
1964+
}
19331965
}
19341966

19351967
#User-Agent changes on different platforms, so tests should only be run if on the correct platform
@@ -2259,6 +2291,64 @@ Describe "Invoke-RestMethod tests" -Tags "Feature", "RequireAdminOnWindows" {
22592291
1..3 | ForEach-Object { $result.Output[$_ - 1].linknumber | Should -BeExactly $_ }
22602292
}
22612293

2294+
It "Verify Invoke-RestMethod supresses terminating errors with -SkipHttpErrorCheck" {
2295+
$uri = Get-WebListenerUrl -Test 'Response' -Query $NotFoundQuery
2296+
$command = "Invoke-RestMethod -SkipHttpErrorCheck -Uri '$uri'"
2297+
$result = ExecuteWebCommand -Command $command
2298+
$result.output.message | Should -BeExactly "oops"
2299+
$result.output.error | Should -BeNullOrEmpty
2300+
}
2301+
2302+
It "Verify Invoke-RestMethod terminates without -SkipHttpErrorCheck" {
2303+
$uri = Get-WebListenerUrl -Test 'Response' -Query $NotFoundQuery
2304+
$command = "Invoke-RestMethod -Uri '$uri'"
2305+
$result = ExecuteWebCommand -Command $command
2306+
$result.output | Should -BeNullOrEmpty
2307+
$result.error | Should -Not -BeNullOrEmpty
2308+
}
2309+
2310+
It "Verify Invoke-RestMethod assigns 200 status code with -StatusCodeVariable" {
2311+
$query = @{
2312+
statuscode = 200
2313+
responsephrase = 'OK'
2314+
contenttype = 'application/json'
2315+
body = '{"message": "works"}'
2316+
headers = "{}"
2317+
}
2318+
2319+
$uri = Get-WebListenerUrl -Test 'Response' -Query $query
2320+
Invoke-RestMethod -StatusCodeVariable code -Uri "$uri"
2321+
$code | Should -Be 200
2322+
}
2323+
2324+
It "Verify Invoke-RestMethod assigns 404 status code with -StatusCodeVariable" {
2325+
$query = @{
2326+
statuscode = 404
2327+
responsephrase = 'Not Found'
2328+
contenttype = 'application/json'
2329+
body = '{"message": "oops"}'
2330+
headers = "{}"
2331+
}
2332+
2333+
$uri = Get-WebListenerUrl -Test 'Response' -Query $query
2334+
Invoke-RestMethod -SkipHttpErrorCheck -StatusCodeVariable code -Uri "$uri"
2335+
$code | Should -Be 404
2336+
}
2337+
2338+
It "Verify Invoke-RestMethod assigns 500 status code with -StatusCodeVariable" {
2339+
$query = @{
2340+
statuscode = 500
2341+
responsephrase = 'Internal Server Error'
2342+
contenttype = 'application/json'
2343+
body = '{"message": "oops"}'
2344+
headers = "{}"
2345+
}
2346+
2347+
$uri = Get-WebListenerUrl -Test 'Response' -Query $query
2348+
Invoke-RestMethod -SkipHttpErrorCheck -StatusCodeVariable code -Uri "$uri"
2349+
$code | Should -Be 500
2350+
}
2351+
22622352
#region Redirect tests
22632353

22642354
It "Validates Invoke-RestMethod with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
@@ -3387,4 +3477,3 @@ Describe "Web cmdlets tests using the cmdlet's aliases" -Tags "CI", "RequireAdmi
33873477
$result.Hello | Should -Be "world"
33883478
}
33893479
}
3390-

0 commit comments

Comments
 (0)