Has anyone used the following code? How do I make it post a tweet? I know I have to use the "$req.Context.RawUri = [Uri]'http://api.twitter.com/version/statuses/update.xml" but I can't get the "$res = [xml][DevDefined.OAuth.Consumer.ConsumerRequestExtensions]::ReadBody($req)" right.
Add-Type -Path C:\OAuthDevDefined\DevDefined.OAuth.dll
$cons = New-Object devdefined.oauth.consumer.oauthconsumercontext
$cons.ConsumerKey = 'key'
$cons.ConsumerSecret = 'key'
$cons.SignatureMethod = [devdefined.oauth.framework.signaturemethod]::HmacSha1
$session = new-object DevDefined.OAuth.Consumer.OAuthSession $cons, $null, $null, $null
$accessToken = new-object DevDefined.OAuth.Framework.TokenBase
$at = import-cliXml C:\temp\myTwitterAccessToken.clixml
$accessToken.ConsumerKey, $accessToken.Realm, $accessToken.Token, $accessToken.TokenSecret = `
$at.ConsumerKey, $at.Realm, $at.Token, $at.TokenSecret
$req = $session.Request($accessToken)
$req.Context.RequestMethod = 'GET'
$req.Context.RawUri = [Uri]'http://api.twitter.com/1/statuses/friends_timeline.xml?count=5'
$res = [xml][DevDefined.OAuth.Consumer.ConsumerRequestExtensions]::ReadBody($req)
$res.statuses.status | % { $_.user.Name }
I use OAuth by DevDefined as well. My function looks like this:
function Post-Twitter {
param(
[Parameter(Mandatory=$true)][string]$url
)
if (!$script:accessToken) {
throw 'token is not initialized'
}
try {
$cons = New-Object devdefined.oauth.consumer.oauthconsumercontext
$cons.ConsumerKey = $consumerKey
$cons.ConsumerSecret = $consumerSecret
$cons.SignatureMethod = [devdefined.oauth.framework.signaturemethod]::HmacSha1
$session = new-object DevDefined.OAuth.Consumer.OAuthSession `
$cons,
"http://twitter.com/oauth/request_token",
"http://twitter.com/oauth/authorize",
"http://twitter.com/oauth/access_token"
$token = Get-AccessToken
$req = $session.Request($token)
$req.Context.RequestMethod = 'POST'
$req.Context.RawUri = new-object Uri $url
[DevDefined.OAuth.Consumer.ConsumerRequestExtensions]::ReadBody($req)
} catch {
Write-Warning "Exception: $_"
$null
}
}
Then for simplicity I pass status in query string:
add-type -assembly System.Web
$status = [system.Web.Httputility]::UrlEncode('some tweet')
Post-Twitter "http://api.twitter.com/1/statuses/update.xml?status=$status"
It seems that you know about the consumer key/secret and the token thing, so I'll leave it without further explanation.
I’ve just posted a Powershell Twitter REST API 1.1 Module on TechNet Gallery… You'll be able to post/get from Twitter API! https://goo.gl/s7pmmA
Related
I am looking for a solution to parse an error-response of a given web-service.
Below sample works great in general, but if the response is larger than 64kb then the content is not availabe in the exception at all.
I have seen some solutions recommending to use webHttpClient and increase the MaxResponseContentBufferSize here, but how can I do this for a given WebClient-object?
Is there any option to change that BufferSize globally for all net-webcalls like below TLS12-settings?
Here is my sample-code:
# using net-webclient to use individual user-side proxy-settings:
$web = new-object Net.WebClient
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = "address to web-service"
try {
$response = $web.DownloadString($url)
} catch [System.Net.WebException] {
# this part needs to work even if the error-response in larger than 64kb
# unfortunately the response-object is empty in such case
$message = $_.Exception.Response
$stream = $message.GetResponseStream()
$reader = new-object System.IO.StreamReader ($stream)
$body = $reader.ReadToEnd()
write-host "#error:$body"
}
I solved it at the end by switching to system.net.httpclient.
That way I still repect any custom proxy-settings and also avoid the above mentioned 64kb-limit in any error-response. Here a sample how to use it:
$url = "address to web-service"
$cred = Get-Credential
# define settings for the http-client:
Add-Type -AssemblyName System.Net.Http
$ignoreCerts = [System.Net.Http.HttpClientHandler]::DangerousAcceptAnyServerCertificateValidator
$handler = [System.Net.Http.HttpClientHandler]::new()
$handler.ServerCertificateCustomValidationCallback = $ignoreCerts
$handler.Credentials = $cred
$handler.PreAuthenticate = $true
$client = [System.Net.Http.HttpClient]::new($handler)
$client.Timeout = [System.TimeSpan]::FromSeconds(10)
$result = $client.GetAsync($url).result
$response = $result.Content.ReadAsStringAsync().Result
write-host $response
I want to upload a file using powershell to a RestAPI. Sadly the invoke-restmethod does not support MultipartForm Uploads and even with building my own body it does not work. Therefore, I have to get this working using another way :)
Luckily I found this excellent blog post: http://blog.majcica.com/2016/01/13/powershell-tips-and-tricks-multipartform-data-requests/ and now I'm trying to build me a request using the .NET Client within Powershell. But I do hit my borders right now. I need to edit the standard header to add an authorization token. And I have no clue how! Would somebody please so kind and point me in the right direction how to do this? Thank you very much!
function Invoke-MultipartFormDataUpload
{
[CmdletBinding()]
PARAM
(
[string][parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$InFile,
[string]$ContentType,
[Uri][parameter(Mandatory = $true)][ValidateNotNullOrEmpty()]$Uri,
[System.Management.Automation.PSCredential]$Credential
)
BEGIN
{
if (-not (Test-Path $InFile))
{
$errorMessage = ("File {0} missing or unable to read." -f $InFile)
$exception = New-Object System.Exception $errorMessage
$errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, 'MultipartFormDataUpload', ([System.Management.Automation.ErrorCategory]::InvalidArgument), $InFile
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
if (-not $ContentType)
{
Add-Type -AssemblyName System.Web
$mimeType = [System.Web.MimeMapping]::GetMimeMapping($InFile)
if ($mimeType)
{
$ContentType = $mimeType
}
else
{
$ContentType = "application/octet-stream"
}
}
}
PROCESS
{
Add-Type -AssemblyName System.Net.Http
$httpClientHandler = New-Object System.Net.Http.HttpClientHandler
if ($Credential)
{
$networkCredential = New-Object System.Net.NetworkCredential #($Credential.UserName, $Credential.Password)
$httpClientHandler.Credentials = $networkCredential
}
$httpClient = New-Object System.Net.Http.Httpclient $httpClientHandler
$packageFileStream = New-Object System.IO.FileStream #($InFile, [System.IO.FileMode]::Open)
$contentDispositionHeaderValue = New-Object System.Net.Http.Headers.ContentDispositionHeaderValue "form-data"
$contentDispositionHeaderValue.Name = "fileData"
$contentDispositionHeaderValue.FileName = (Split-Path $InFile -leaf)
$streamContent = New-Object System.Net.Http.StreamContent $packageFileStream
$streamContent.Headers.ContentDisposition = $contentDispositionHeaderValue
$streamContent.Headers.ContentType = New-Object System.Net.Http.Headers.MediaTypeHeaderValue $ContentType
$content = New-Object System.Net.Http.MultipartFormDataContent
$content.Add($streamContent)
try
{
$response = $httpClient.PostAsync($Uri, $content).Result
if (!$response.IsSuccessStatusCode)
{
$responseBody = $response.Content.ReadAsStringAsync().Result
$errorMessage = "Status code {0}. Reason {1}. Server reported the following message: {2}." -f $response.StatusCode, $response.ReasonPhrase, $responseBody
throw [System.Net.Http.HttpRequestException] $errorMessage
}
return $response.Content.ReadAsStringAsync().Result
}
catch [Exception]
{
$PSCmdlet.ThrowTerminatingError($_)
}
finally
{
if($null -ne $httpClient)
{
$httpClient.Dispose()
}
if($null -ne $response)
{
$response.Dispose()
}
}
}
END { }
}
Try this
#Authorization
$httpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $accesstoken);
I'm trying to modify Contacts in Gmail account, using the .Net API, and Loading it in powershell.
I'm following the steps described here (Updating contacts, Doh !)
To update a contact, first retrieve the contact entry, modify the data and send an authorized PUT request to the contact's edit URL with the modified contact entry in the body.
OK, got it so I'm succesfull to retrieve contact informations using this code :
$Settings = New-Object Google.GData.Client.RequestSettings( "MyApp", $username , $password )
$Credentials = New-Object System.Net.NetworkCredential( $username, $password )
$Request = New-Object Google.Contacts.ContactsRequest( $Settings )
$Contacts = $Request.GetContacts()
$GoogleContact = $Contacts.Entries |? { $_.PrimaryEmail.Address -eq "john.doe#gmail.com" }
$GoogleContact.Title
Of course, I had a Mail message from google indicating that an external App was bloqued, and I changed a security parameter to allow this code to work ...
And it works, the code is prompting my Google Contact Title.
And now, my problem:
I'm changing a property on my Object:
$GoogleContact.Title = "Mac Gyver"
I'm using a function called Execute-HTTPPostCommand, slightly modified to add the Etag value, required by google to make sure I'm not modifying an entry that is actually modified somewhere else :
function Execute-HTTPPostCommand() {
param(
[string] $TargetUrl = $null
,[string] $PostData = $null
,$Credentials
,$Etag
)
$ErrorActionPreference = "Stop"
$global:webRequest = [System.Net.WebRequest]::Create($TargetUrl)
$webRequest.Headers.Add("etag", $Etag )
$webRequest.ContentType = "text/html"
$PostStr = [System.Text.Encoding]::UTF8.GetBytes($PostData)
$webrequest.ContentLength = $PostStr.Length
$webRequest.ServicePoint.Expect100Continue = $false
$webRequest.Credentials = $Credentials
$webRequest.PreAuthenticate = $true
$webRequest.Method = "PUT"
$Global:requestStream = $webRequest.GetRequestStream()
$requestStream.Write($PostStr, 0,$PostStr.length)
$requestStream.Close()
[System.Net.WebResponse] $global:resp = $webRequest.GetResponse();
$rs = $resp.GetResponseStream();
[System.IO.StreamReader] $sr = New-Object System.IO.StreamReader -argumentList $rs;
[string] $results = $sr.ReadToEnd();
return $results;
}
And calling it this way:
Execute-HTTPPostCommand -TargetUrl $GoogleContact.Id -PostData $GoogleContact -Credentials $Credentials -Etag $GoogleContact.ETag
the Contact.ID value is the URL required by google to update the contact, it looks like this : https://www.google.com/m8/feeds/contacts/userEmail/full/{contactId}
I'm getting an error 401 : unautorized.
Being a Windows Sysadmin, I'm not familiar with webservices PUT requests ...
I'm using the same credentials to Read Datas and trying to update datas.
What am I missing?
Ok, that was pretty easy, I should have RTFM ...
#Loading Google API
$Settings = New-Object Google.GData.Client.RequestSettings( "MyApp", $username , $password )
$Credentials = New-Object System.Net.NetworkCredential( $username, $password )
#Loading Contacts, and getting the one I want
$Request = New-Object Google.Contacts.ContactsRequest( $Settings )
$Contacts = $Request.GetContacts()
$GoogleContact = $Contacts.Entries |? { $_.PrimaryEmail.Address -eq "john.doe#gmail.com" }
#Updating the fields
$GoogleContact.Name.FullName = "Mac Gyver"
$GoogleContact.Name.GivenName = "Mac"
$GoogleContact.Name.FamilyName = "Gyver"
$GoogleContact.Title = "Handyman Masterchief"
#Update
$MAJ = $ContactRequest.Update($GoogleContact)
And it works as is, just like the .Net example.
No need to load a heavy PUT request, the API do his Job.
Sorry for loss of time guys !
Read over the stackoverflow for the answer, still can't find what causing this..
Trying to connect and send a POST request using powershell, and getting "unable to connect to remote server" error. Tried 3 different dummy servers like http://posttestserver.com/post.php
Script:
Get-ExecutionPolicy
[string]$url = "http://posttestserver.com/post.php"
function Execute-HTTPPostCommand()
{
param([string]$target = $null)
#$username = "administrator"
#$password = "mypass"
$webRequest = [System.Net.WebRequest]::Create($target)
$webRequest.ContentType = "text/html"
$post = "abcdefg"
$PostStr = [System.Text.Encoding]::UTF8.GetBytes($Post)
$webrequest.ContentLength = $PostStr.Length
$webRequest.ServicePoint.Expect100Continue = $false
#$webRequest.Credentials = New-Object System.Net.NetworkCredential -ArgumentList $username, $password
#$webRequest.PreAuthenticate = $true
$webRequest.Method = "POST"
try
{
$requestStream = $webRequest.GetRequestStream()
}
catch
{
write-host $_.Exception
}
$requestStream.Write($PostStr, 0,$PostStr.length)
$requestStream.Close()
[System.Net.WebResponse]$resp = $webRequest.GetResponse();
$rs = $resp.GetResponseStream();
[System.IO.StreamReader]$sr = New-Object System.IO.StreamReader -argumentList $rs;
[string]$results = $sr.ReadToEnd();
return $results;
}
Execute-HTTPPostCommand $url
[System.GC]::Collect()
In Windows PowerShell 3.0 was introduced Invoke-RestMethod cmdlet.
Invoke-RestMethod cmdlet accepts -Body<Object> parameter for setting the body of the request.
Due to a certain limitations Invoke-RestMethod cmdlet could not be used in our case. From the other hand, an alternative solution described in article InvokeRestMethod for the Rest of Us suits our needs:
$request = [System.Net.WebRequest]::Create($url)
$request.Method="Get"
$response = $request.GetResponse()
$requestStream = $response.GetResponseStream()
$readStream = New-Object System.IO.StreamReader $requestStream
$data=$readStream.ReadToEnd()
if($response.ContentType -match "application/xml") {
$results = [xml]$data
} elseif($response.ContentType -match "application/json") {
$results = $data | ConvertFrom-Json
} else {
try {
$results = [xml]$data
} catch {
$results = $data | ConvertFrom-Json
}
}
$results
But it is intended for a GET method only.
Could you please suggest how to extend this code sample with the ability to send the body of the request using POST method (similar to Body parameter in Invoke-RestMethod)?
First, change the line that updates the HTTP method.
$request.Method= 'POST';
Next, you need to add the message body to the HttpWebRequest object. To do that, you need to grab a reference to the request stream, and then add data to it.
$Body = [byte[]][char[]]'asdf';
$Request = [System.Net.HttpWebRequest]::CreateHttp('http://www.mywebservicethatiwanttoquery.com/');
$Request.Method = 'POST';
$Stream = $Request.GetRequestStream();
$Stream.Write($Body, 0, $Body.Length);
$Request.GetResponse();
NOTE: PowerShell Core edition is now open source on GitHub, and cross-platform on Linux, Mac, and Windows. Any issues with the Invoke-RestMethod cmdlet should be reported on the GitHub issue tracker for this project, so they can be tracked and fixed.
$myID = 666;
#the xml body should begin on column 1 no indentation.
$reqBody = #"
<?xml version="1.0" encoding="UTF-8"?>
<ns1:MyRequest
xmlns:ns1="urn:com:foo:bar:v1"
xmlns:ns2="urn:com:foo:xyz:v1"
<ns2:MyID>$myID</ns2:MyID>
</ns13:MyRequest>
"#
Write-Host $reqBody;
try
{
$endPoint = "http://myhost:80/myUri"
Write-Host ("Querying "+$endPoint)
$wr = [System.Net.HttpWebRequest]::Create($endPoint)
$wr.Method= 'POST';
$wr.ContentType="application/xml";
$Body = [byte[]][char[]]$reqBody;
$wr.Timeout = 10000;
$Stream = $wr.GetRequestStream();
$Stream.Write($Body, 0, $Body.Length);
$Stream.Flush();
$Stream.Close();
$resp = $wr.GetResponse().GetResponseStream()
$sr = New-Object System.IO.StreamReader($resp)
$respTxt = $sr.ReadToEnd()
[System.Xml.XmlDocument] $result = $respTxt
[String] $rs = $result.DocumentElement.OuterXml
Write-Host "$($rs)";
}
catch
{
$errorStatus = "Exception Message: " + $_.Exception.Message;
Write-Host $errorStatus;
}