26

I know of a few good ways to build web clients in PowerShell: the .NET classes System.Net.WebClient and System.Net.HttpWebRequest, or the COM object Msxml2.XMLHTTP. From what I can tell, the only one which gives you access to the numeric status code (e.g. 200, 404), is the last, the COM object. The problem I have is that I don't like the way it works and I don't like relying on the COM object being there. I also know that from time to time Microsoft will decide to kill COM objects (ActiveX kill bits) due to security vulnerabilities and so on.

Is there another .NET method I'm missing? Is the status code in one of these other two objects and I just don't know how to get at it?

halr9000
  • 9,879
  • 5
  • 33
  • 34

4 Answers4

69

Using both x0n and joshua ewer's answers to come full circle with a code example, I hope that's not too bad form:

$url = 'http://google.com'
$req = [system.Net.WebRequest]::Create($url)

try {
    $res = $req.GetResponse()
} 
catch [System.Net.WebException] {
    $res = $_.Exception.Response
}

$res.StatusCode
#OK

[int]$res.StatusCode
#200
DarcyThomas
  • 1,218
  • 13
  • 30
halr9000
  • 9,879
  • 5
  • 33
  • 34
  • 2
    This throws an exception when it gets anything but a 2xx message (I think). That is not expected behavior in my opinion – Joe Phillips May 18 '12 at 17:22
  • 1
    @JoePhilllips I added a try..catch to the snippet so it can handle other status codes. – merv Jun 28 '12 at 23:14
  • @merv for some reason your edit was rejected by someone else, but I went ahead and upvoted your comment and did the edit manually. thx for the submission – halr9000 Jul 16 '12 at 02:47
  • Remember to set a `Timeout` for the `$req` or else it's infinite, caught me out! – Liam Jul 11 '13 at 12:25
  • Personally I liked using Invoke-WebRequest instead to simplify it. -- Import-Csv .\SomePages.csv | Select-Object @{Name="URL";Expression={$_.URL}}, @{Name="Status";Expression={ try{$res = (Invoke-WebRequest ("http://www.anonymized.com" + $_.URL) -MaximumRedirection 0 -ErrorAction Ignore)} catch [System.Net.WebException] { $res = $_.Exception.Response}; $res.StatusCode}} | Export-Csv .\SomePagesStatus.csv – CodeMonkey1313 Dec 06 '13 at 00:05
  • 3
    You should also add `$req.method = "HEAD"` to only download the header and not the whole response which could be very large. – Chris May 11 '15 at 17:51
  • If you are going to use this in a loop then you are going to want to add finally {$res.Close()} or your script bog down quickly. – personaelit Sep 21 '16 at 11:13
14

Use the [system.net.httpstatuscode] enumerated type.

ps> [enum]::getnames([system.net.httpstatuscode])
Continue
SwitchingProtocols
OK
Created
Accepted
NonAuthoritativeInformation
NoContent
ResetContent
...

To get the numeric code, cast to [int]:

ps> [int][system.net.httpstatuscode]::ok
200

Hope this helps,

-Oisin

Liam
  • 27,717
  • 28
  • 128
  • 190
x0n
  • 51,312
  • 7
  • 89
  • 111
  • Aha, the answer was there (I'd seen the OK string before), but I didn't know it! Perfect, thanks. :) – halr9000 Sep 25 '09 at 19:30
5

I realize the question's title is about powershell, but not really what the question is asking? Either way...

The WebClient is a very dumbed down wrapper for HttpWebRequest. WebClient is great if you just doing very simple consumption of services or posting a bit of Xml, but the tradeoff is that it's not as flexible as you might want it to be. You won't be able to get the information you are looking for from WebClient.

If you need the status code, get it from the HttpWebResponse. If you were doing something like this (just posting a string to a Url) w/ WebClient:

var bytes = 
    System.Text.Encoding.ASCII.GetBytes("my xml"); 

var response = 
    new WebClient().UploadData("http://webservice.com", "POST", bytes);

then you'd do this with HttpWebRequest to get status code. Same idea, just more options (and therefore more code).

//create a stream from whatever you want to post here
var bytes = 
  System.Text.Encoding.ASCII.GetBytes("my xml"); 
var request = 
  (HttpWebRequest)WebRequest.Create("http://webservice.com");

//set up your request options here (method, mime-type, length)

//write something to the request stream
var requestStream = request.GetRequestStream();
requestStream.Write(bytes, 0, bytes.Length);        
requestStream.Close();

var response = (HttpWebResponse)request.GetResponse();

//returns back the HttpStatusCode enumeration
var httpStatusCode = response.StatusCode;
joshua.ewer
  • 3,944
  • 3
  • 25
  • 35
  • Aha, resposne.statuscode is the string and then I use x0n's enumerator technique to convert to a number. thanks :) – halr9000 Sep 25 '09 at 19:31
  • P.S. If I had left off the PowerShell in the title then I am certain the answer I got back would be even further away from what I wanted. but I can certainly read your code. – halr9000 Sep 25 '09 at 19:32
0

It looks very easy

$wc = New-Object NET.WebClient
$wc.DownloadString($url)
$wc.ResponseHeaders.Item("status")

You can find the other available Response Headers in the ResponseHeaders property (like content-type, content-length, x-powered-by and such), and retrieve any of them via the Item() method.

... but as rob mentions below, sadly, the status property is not available here

CommonToast
  • 688
  • 12
  • 19
  • 3
    That is what everyone wants to work, but it does not. There is no "status" in the response headers after making a request with NET,WebClient. Worse, if it is not a 200 then an exception is thrown and the responseheaders property is null. – rob Jul 03 '17 at 10:44