Skip to content

Commit 931bf16

Browse files
authored
Merge pull request #2711 from gautamdsheth/feature/resp-header-sp
Feature: Add support for response headers in Invoke-PnPSPRestMethod cmdlet
2 parents 18733cd + 3a5f354 commit 931bf16

File tree

3 files changed

+40
-97
lines changed

3 files changed

+40
-97
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
2323
- Added `-KeyColumn` to `Add-PnPDataRowsToSiteTemplate` which allows for overwriting existing list items in a site template [#2616](https://github.com/pnp/powershell/pull/2616)
2424
- Added `IsTeamsConnected`, `IsTeamsChannelConnected` and `TeamChannelType` to be returned when `Get-PnPTenantSite` cmdlet is executed. [#2656](https://github.com/pnp/powershell/pull/2656)
2525
- Added `-EnvironmentVariable` parameter to `Connect-PnPOnline` to connect using Azure environment variables. [#2681](https://github.com/pnp/powershell/pull/2681)
26+
- Added `ResponseHeadersVariable` parameter to the `Invoke-PnPSPRestMethod` which if specified will store the response headers values in the PowerShell variable name that is specified. [#2711](https://github.com/pnp/powershell/pull/2711)
2627
- Added `-Filter` parameter to `Get-PnPAzureADServicePrincipal` cmdlet to retrieve specific application registrations based on filter conditions. It supports simple and advanced queries. [#2710](https://github.com/pnp/powershell/pull/2710)
2728
- Added `-Filter` parameter to `Get-PnPMicrosoft365Group` cmdlet to retrieve specific M365 groups based on filter conditions. It supports simple and advanced queries.
2829

documentation/Invoke-PnPSPRestMethod.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Invoke-PnPSPRestMethod -Url <String>
2020
[-ContentType <String>]
2121
[-Raw]
2222
[-Connection <PnPConnection>]
23+
[-ResponseHeadersVariable <String>]
2324
```
2425

2526
## DESCRIPTION
@@ -66,6 +67,17 @@ Invoke-PnPSPRestMethod -Method Post -Url "/_api/web/lists/GetByTitle('Test')/ite
6667

6768
This example creates a new item in the list 'Test' and sets the title field to 'Test'
6869

70+
### ------------------EXAMPLE 6------------------
71+
```powershell
72+
$output = Invoke-PnPSPRestMethod -Url '/_api/web/lists?$select=Id,Title' -ResponseHeadersVariable headers
73+
$output.value
74+
$headers
75+
```
76+
77+
This example executes a GET request towards the current site collection and returns the id and title of all the lists and outputs them to the console. Notice the use of single quotes. If you want to use double quotes (") then you will have to escape the $ character with a backtick: `$
78+
79+
It will also store the response headers values in the PowerShell variable name that you specify. Enter a variable name without the dollar sign ($) symbol.
80+
6981
## PARAMETERS
7082

7183
### -Content
@@ -152,6 +164,17 @@ Position: Named
152164
Accept pipeline input: False
153165
```
154166
167+
### -ResponseHeadersVariable
168+
Creates a variable containing a Response Headers Dictionary. Enter a variable name without the dollar sign ($) symbol. The keys of the dictionary contain the field names and values of the Response Header returned by the web server.
169+
170+
```yaml
171+
Type: String
172+
Parameter Sets: (All)
173+
Required: False
174+
Position: Named
175+
Accept pipeline input: False
176+
```
177+
155178
## RELATED LINKS
156179
157180
[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp)
Lines changed: 16 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,14 @@
11
using Microsoft.SharePoint.Client;
2-
32
using PnP.Framework.Http;
43
using PnP.Framework.Utilities;
5-
64
using PnP.PowerShell.Commands.Enums;
7-
using PnP.PowerShell.Commands.Utilities.JSON;
8-
95
using System;
10-
using System.Collections;
116
using System.Collections.Generic;
127
using System.Linq;
138
using System.Management.Automation;
14-
using System.Net;
159
using System.Net.Http;
1610
using System.Net.Http.Headers;
17-
using System.Runtime.InteropServices;
18-
using System.Text;
1911
using System.Text.Json;
20-
using System.Text.RegularExpressions;
2112

2213
namespace PnP.PowerShell.Commands.Admin
2314
{
@@ -52,6 +43,10 @@ public class InvokeSPRestMethod : PnPSharePointCmdlet
5243
[Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Raw)]
5344
public SwitchParameter Raw;
5445

46+
[Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Parsed)]
47+
[Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Raw)]
48+
public string ResponseHeadersVariable;
49+
5550
protected override void ExecuteCmdlet()
5651
{
5752
if (Url.StartsWith("/"))
@@ -66,6 +61,8 @@ protected override void ExecuteCmdlet()
6661

6762
var requestUrl = Url;
6863

64+
bool isResponseHeaderRequired = !string.IsNullOrEmpty(ResponseHeadersVariable);
65+
6966
using (HttpRequestMessage request = new HttpRequestMessage(method, requestUrl))
7067
{
7168
if (string.IsNullOrEmpty(Accept))
@@ -99,10 +96,12 @@ protected override void ExecuteCmdlet()
9996
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(ContentType);
10097
}
10198
HttpResponseMessage response = httpClient.SendAsync(request, new System.Threading.CancellationToken()).Result;
99+
Dictionary<string, string> responseHeaders = response?.Content?.Headers?.ToDictionary(a => a.Key, a => string.Join(";", a.Value));
102100

103101
if (response.IsSuccessStatusCode)
104102
{
105103
var responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
104+
106105
if (responseString != null)
107106
{
108107
if (!Raw)
@@ -121,7 +120,6 @@ protected override void ExecuteCmdlet()
121120
{
122121
formattedObject.Properties.Add(new PSNoteProperty("odata.nextLink", nextLink));
123122
}
124-
125123
WriteObject(formattedObject, true);
126124
}
127125
else
@@ -134,100 +132,21 @@ protected override void ExecuteCmdlet()
134132
WriteObject(responseString);
135133
}
136134
}
135+
if (isResponseHeaderRequired)
136+
{
137+
SessionState.PSVariable.Set(ResponseHeadersVariable, responseHeaders.ToList());
138+
}
137139
}
138140
else
139141
{
142+
if (isResponseHeaderRequired)
143+
{
144+
SessionState.PSVariable.Set(ResponseHeadersVariable, responseHeaders.ToList());
145+
}
140146
// Something went wrong...
141147
throw new Exception(response.Content.ReadAsStringAsync().GetAwaiter().GetResult());
142148
}
143149
}
144150
}
145-
146-
private void SetAuthenticationCookies(HttpClientHandler handler, ClientContext context)
147-
{
148-
context.Web.EnsureProperty(w => w.Url);
149-
//if (context.Credentials is SharePointOnlineCredentials spCred)
150-
//{
151-
// handler.Credentials = context.Credentials;
152-
// handler.CookieContainer.SetCookies(new Uri(context.Web.Url), spCred.GetAuthenticationCookie(new Uri(context.Web.Url)));
153-
//}
154-
//else if (context.Credentials == null)
155-
//{
156-
var cookieString = CookieReader.GetCookie(context.Web.Url).Replace("; ", ",").Replace(";", ",");
157-
var authCookiesContainer = new System.Net.CookieContainer();
158-
// Get FedAuth and rtFa cookies issued by ADFS when accessing claims aware applications.
159-
// - or get the EdgeAccessCookie issued by the Web Application Proxy (WAP) when accessing non-claims aware applications (Kerberos).
160-
IEnumerable<string> authCookies = null;
161-
if (Regex.IsMatch(cookieString, "FedAuth", RegexOptions.IgnoreCase))
162-
{
163-
authCookies = cookieString.Split(',').Where(c => c.StartsWith("FedAuth", StringComparison.InvariantCultureIgnoreCase) || c.StartsWith("rtFa", StringComparison.InvariantCultureIgnoreCase));
164-
}
165-
else if (Regex.IsMatch(cookieString, "EdgeAccessCookie", RegexOptions.IgnoreCase))
166-
{
167-
authCookies = cookieString.Split(',').Where(c => c.StartsWith("EdgeAccessCookie", StringComparison.InvariantCultureIgnoreCase));
168-
}
169-
if (authCookies != null)
170-
{
171-
authCookiesContainer.SetCookies(new Uri(context.Web.Url), string.Join(",", authCookies));
172-
}
173-
handler.CookieContainer = authCookiesContainer;
174-
//}
175-
}
176-
}
177-
178-
//Taken from "Remote Authentication in SharePoint Online Using the Client Object Model"
179-
//https://code.msdn.microsoft.com/Remote-Authentication-in-b7b6f43c
180-
181-
/// <summary>
182-
/// WinInet.dll wrapper
183-
/// </summary>
184-
internal static class CookieReader
185-
{
186-
/// <summary>
187-
/// Enables the retrieval of cookies that are marked as "HTTPOnly".
188-
/// Do not use this flag if you expose a scriptable interface,
189-
/// because this has security implications. It is imperative that
190-
/// you use this flag only if you can guarantee that you will never
191-
/// expose the cookie to third-party code by way of an
192-
/// extensibility mechanism you provide.
193-
/// Version: Requires Internet Explorer 8.0 or later.
194-
/// </summary>
195-
private const int INTERNET_COOKIE_HTTPONLY = 0x00002000;
196-
197-
/// <summary>
198-
/// Returns cookie contents as a string
199-
/// </summary>
200-
/// <param name="url">Url to get cookie</param>
201-
/// <returns>Returns Cookie contents as a string</returns>
202-
public static string GetCookie(string url)
203-
{
204-
int size = 512;
205-
StringBuilder sb = new StringBuilder(size);
206-
if (!NativeMethods.InternetGetCookieEx(url, null, sb, ref size, INTERNET_COOKIE_HTTPONLY, IntPtr.Zero))
207-
{
208-
if (size < 0)
209-
{
210-
return null;
211-
}
212-
sb = new StringBuilder(size);
213-
if (!NativeMethods.InternetGetCookieEx(url, null, sb, ref size, INTERNET_COOKIE_HTTPONLY, IntPtr.Zero))
214-
{
215-
return null;
216-
}
217-
}
218-
return sb.ToString();
219-
}
220-
221-
private static class NativeMethods
222-
{
223-
[DllImport("wininet.dll", EntryPoint = "InternetGetCookieEx", CharSet = CharSet.Unicode, SetLastError = true)]
224-
public static extern bool InternetGetCookieEx(
225-
string url,
226-
string cookieName,
227-
StringBuilder cookieData,
228-
ref int size,
229-
int flags,
230-
IntPtr pReserved);
231-
}
232151
}
233152
}

0 commit comments

Comments
 (0)