Shane 的个人资料Knowledge Is Power(shell...照片日志列表 工具 帮助

日志


3月13日

Powershell REST Client

The past couple of days I have been working on a Powershell based client for our REST web services.  While the client is specific to our business the core logic behind the client should apply to just about all REST based web services.  I have tried to generalize(read: remove company stuff) the code so hopefully everything still makes sense.

In our implementation of REST, we allow a person to either submit a GET request with query string parameters or attach some XML to the request and submit as a POST.  Our response will always be some xml.  We handle it with 3 xml root types.  Success, Error, and Data.  Success just lets the user know a particular POST was successful.  Error will give the user back an error code with a description.  Data will return an xml document with whatever data the user requested.  Eventually a Success I would think will also return the changes made but hey, this is version 1.

The core of each GET request is just making the request with the query strings defined.  The declaration of the core function with the parameters section is:

function Invoke-APICall {
    param(
        [string]$Username,
        [string]$Password,
        [string]$UserAgent = "Powershell API Client",
        [string]$URL,
        [xml]$XMLObject
        )

 

   1: #Create a URI instance since the HttpWebRequest.Create Method will escape the URL by default.
   2: $URI = New-Object System.Uri($URL,$true)
   3:  
   4: #Create a request object using the URI
   5: $request = [System.Net.HttpWebRequest]::Create($URI)
   6:  
   7: #Build up a nice User Agent
   8: $request.UserAgent = $(
   9: "{0} (PowerShell {1}; .NET CLR {2}; {3})" -f $UserAgent, 
  10: $(if($Host.Version){$Host.Version}else{"1.0"}),
  11: [Environment]::Version,
  12: [Environment]::OSVersion.ToString().Replace("Microsoft Windows ", "Win")
  13: )
  14:  
  15: #Establish the credentials for the request
  16: $creds = New-Object System.Net.NetworkCredential($Username,$Password)
  17: $request.Credentials = $creds
  18:  
  19: $response = $request.GetResponse()
  20:  
  21: $reader = [IO.StreamReader] $response.GetResponseStream()
  22:  
  23: #Our response will always be xml except in 404/401 case so cast as such        
  24: [xml]$responseXML = $reader.ReadToEnd()
  25:         
  26: $reader.Close()
  27:         
  28: #Let others down the pipeline have fun with our xml object
  29: Write-Output $responseXML
  30:  
  31: $response.Close()

 

To decide if this is a POST I use the following: 

if($XMLObject){ Do the POST stuff}else{Do the GET stuff}

And now the POST specific code.

   1: $creds = New-Object System.Net.NetworkCredential($Username,$Password)
   2: $request.Credentials = $creds
   3:  
   4: #Since this is a POST we need to set the method type
   5: $request.Method = "POST"
   6:  
   7: #Set the Content Type as text/xml since the content will be a block of xml.
   8: $request.ContentType = "text/xml"
   9:  
  10: #Create a new stream writer to write the xml to the request stream.
  11: $stream = New-Object IO.StreamWriter $request.GetRequestStream()
  12: $stream.AutoFlush = $True
  13: $stream.Write($($XMLObject.psbase.OuterXML),0,$($XMLObject.psbase.OuterXml.Length))
  14: $stream.Close()
  15:  
  16: #Make the request and get the response
  17: $response = $request.GetResponse()
  18:  
  19: #Create a stream reader to read the response stream.
  20: $reader = New-Object IO.StreamReader $response.GetResponseStream()
  21:  
  22: #Read the response and cast the response to XML
  23: [xml]$responseXML = $reader.ReadToEnd()
  24:  
  25: #Dump the XML out to the pipeline for others to consume
  26: Write-Output $responseXML
  27:  
  28: $response.Close()

 

There was one thing in the POST that I had trouble with at first.  When trying to execute the $stream.Write method, if I had set the $request.ContentLength property with $request.ContentLength = $$XMLObject.psbase.OuterXML.Length, it would fail stating the bytes being written were longer than the bytes specified.  So I removed the code to set the $request.ContentLength and everything seemed to work fine.  Not sure how Joel Bennett got that to work but it drove me nuts for a while.

So that just leaves the calling functions.  Here is a GET

   1: function Invoke-APIGetStuffInGroup {
   2:     param( 
   3:       [string]$Group,
   4:       [string]$Username,
   5:       [string]$Password,
   6:       [string]$UserAgent = "Powershell API Client"      
   7:    )
   8:    
   9:    
  10:         $Group = [System.Web.HttpUtility]::UrlEncode($Group)
  11:         $URL = "https://api.website.com/api/1.0/Groups/GetStuff?group=$Group"
  12:         Invoke-APICall -URL $URL -Username $Username -Password $Password -UserAgent $UserAgent
  13:         
  14: }

 

And a POST

   1: function Invoke-APIAddStuffToGroups {
   2:     param( 
   3:       [string]$Username,
   4:       [string]$Password,
   5:       [string]$UserAgent = "Powershell API Client"      
   6:    )
   7:         $xml = #Create your XML Document here
   8:         
   9:         $URL = "https://api.website.com/api/1.0/Groups/AddStuff"
  10:             
  11:         Invoke-APICall -URL $URL -XMLObject $xml -Username $Username -Password $Password -UserAgent $UserAgent
  12: }
  13:         

 

One change I might make at some point is to require a SecureString for the password since clear text can be bad.

Hope this helps someone!

-Shane

评论

请稍候...
很抱歉,您输入的评论太长。请缩短您的评论。
您没有输入任何内容,请重试。
很抱歉,我们当前无法添加您的评论。请稍后重试。
若要添加评论,需要您的家长授予您相应权限。请求权限
您的家长禁用了评论功能。
很抱歉,我们当前无法删除您的评论。请稍后重试。
您已超过了一天之内允许提供的评论数上限。请在 24 小时后重试。
因为我们的系统表明您可能在向其他用户提供垃圾评论,您的帐户已禁用了评论功能。如果您认为我们错误地禁用了您的帐户,请联系 Windows Live 支持部门
完成下面的安全检查,您提供评论的过程才能完成。
您在安全检查中键入的字符必须与图片或音频中的字符一致。

若要添加评论,请使用您的 Windows Live ID 登录(如果您使用过 Hotmail、Messenger 或 Xbox LIVE,您就拥有 Windows Live ID)。登录


还没有 Windows Live ID 吗?请注册

引用通告

此日志的引用通告 URL 是:
http://shanepowser.spaces.live.com/blog/cns!CBEC59A3683A2168!205.trak
引用此项的网络日志