Triki z PowerShellem #11 – ĆwierkamyW ramach wyposażania nowozainstalowanego systemu w niezbędne programy, przypomniałem sobie o istnieniu PowerShella. Kiedy jednak chciałem go ściągnąć, spotkała mnie przyjemna niespodzianka: PSh w Windows 7 jest już od razu zainstalowany, więc można go od razu zacząć go używać. Jak sądzę, przyczyni do zwiększenia jego popularności, co jest z pewnością dobrą rzeczą.
Fakt sprawił rzecz jasna, że zaraz zachciało mi się wypróbować go w jakimś nowym zastosowaniu. Padło na wysyłanie update'ów do Twittera, w którym to zresztą niedawno się zarejestrowałem (i wciąż nie wiem, dlaczego ;)). Sprawa na oko nie jest trudna, bo sprowadza się do wykonania jednego żądania HTTP POST. Ale jak wiadomo, diabeł zwykle tkwi w szczegółach. Oto skrypt:
[Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null
# Stałe
$LOGIN = "login" # lub e-mail
$PASS = "hasło"
# Pobranie statusu od użytkownika
$tweet = Read-Host -Prompt "Status"
# Złożenie żądania HTTP POST
$uri = [Uri]"http://twitter.com/statuses/update.xml"
$http = [Net.HttpWebRequest]::Create($uri)
$http.Credentials = New-Object Net.NetworkCredential @($LOGIN, $PASS)
$http.Method = [Net.WebRequestMethods+Http]::Post
$http.ServicePoint.Expect100Continue = $false # (*)
# Wysyłanie danych
$data = "status=" + [Web.HttpUtility]::UrlEncode($tweet)
$http.ContentLength = $data.Length
$sw = New-Object IO.StreamWriter @($http.GetRequestStream())
$sw.Write($data)
$sw.Close()
# Wyświetlamy ID nowego statusu
$resp = $http.GetResponse().GetResponseStream()
$sr = New-Object IO.StreamReader @($resp)
$xml = [xml]$sr.ReadToEnd()
"Status updated (ID: " + $xml.status.id + ")" | Out-Host
$sr.Close()
# Obsługa błędów
trap { "Error: " + $_.Exception.Message; return }
Jednym z owych detali było kodowanie statusu algorytmem dla URL-i (zamieniającym spacje na %20 itd.), wykonywane poprzez System.Web.HttpUtility.UrlEncode - stąd konieczność importowania assembly System.Web. Ale to jest w sumie pikuś.
Znacznie większym "trikiem" jest linijka oznaczona gwiazdką (*). Powoduje ona obejście domyślnego zachowania .NET, który do każdego żądania HTTP typu POST dodaje nagłówek:
Powoduje on wysłanie tak naprawdę dwóch requestów: w pierwszym serwer ma tylko sprawdzić poprawność nagłówków (logowania, na przykład) i zwrócić status 100 (Continue). Dopiero w drugim klient wysyła właściwe dane. Mechanizm ten jest w .NET opakowany przezroczyście i ma zapobiegać niepotrzebnemu przesyłaniu dużych ilości danych w żądaniu, które i tak byłoby odrzucone.
API Twittera jednak tego nie obsługuje i jest to właściwe. Trudno przecież nazwać status, mający maks. 160 znaków, "dużą ilością danych". Lepiej więc przesyłać go od razu, a domyślne zachowanie .NET-a obejść. To właśnie robi zaznaczony wiersz.
Przypomnę jeszcze tylko - gdy ktoś zechciał powyższego skryptu używać do przesyłania tweetów - że uruchomienie skryptu PSh z poziomu zwykłej linii poleceń wymaga parametru -Command i kropki:
Do takiej komendy można np. utworzyć skrót i przypisać mu kombinację klawiszy w celu szybkiego uruchamiania.