mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
InvokeRestMethodCommand.Common cleanup and merge partials (#18736)
This commit is contained in:
+133
-153
@@ -13,7 +13,15 @@ using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
public partial class InvokeRestMethodCommand
|
||||
/// <summary>
|
||||
/// The Invoke-RestMethod command
|
||||
/// This command makes an HTTP or HTTPS request to a web service,
|
||||
/// and returns the response in an appropriate way.
|
||||
/// Intended to work against the wide spectrum of "RESTful" web services
|
||||
/// currently deployed across the web.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096706", DefaultParameterSetName = "StandardMethod")]
|
||||
public class InvokeRestMethodCommand : WebRequestPSCmdlet
|
||||
{
|
||||
#region Parameters
|
||||
|
||||
@@ -83,8 +91,124 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
#endregion Parameters
|
||||
|
||||
#region Virtual Method Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Process the web response and output corresponding objects.
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
internal override void ProcessResponse(HttpResponseMessage response)
|
||||
{
|
||||
if (response == null) { throw new ArgumentNullException(nameof(response)); }
|
||||
|
||||
var baseResponseStream = StreamHelper.GetResponseStream(response);
|
||||
|
||||
if (ShouldWriteToPipeline)
|
||||
{
|
||||
using var responseStream = new BufferingStreamReader(baseResponseStream);
|
||||
|
||||
// First see if it is an RSS / ATOM feed, in which case we can
|
||||
// stream it - unless the user has overridden it with a return type of "XML"
|
||||
if (TryProcessFeedStream(responseStream))
|
||||
{
|
||||
// Do nothing, content has been processed.
|
||||
}
|
||||
else
|
||||
{
|
||||
// determine the response type
|
||||
RestReturnType returnType = CheckReturnType(response);
|
||||
|
||||
// Try to get the response encoding from the ContentType header.
|
||||
Encoding encoding = null;
|
||||
string charSet = response.Content.Headers.ContentType?.CharSet;
|
||||
if (!string.IsNullOrEmpty(charSet))
|
||||
{
|
||||
StreamHelper.TryGetEncoding(charSet, out encoding);
|
||||
}
|
||||
|
||||
object obj = null;
|
||||
Exception ex = null;
|
||||
|
||||
string str = StreamHelper.DecodeStream(responseStream, ref encoding);
|
||||
|
||||
string encodingVerboseName;
|
||||
try
|
||||
{
|
||||
encodingVerboseName = string.IsNullOrEmpty(encoding.HeaderName) ? encoding.EncodingName : encoding.HeaderName;
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
encodingVerboseName = encoding.EncodingName;
|
||||
}
|
||||
|
||||
// NOTE: Tests use this verbose output to verify the encoding.
|
||||
WriteVerbose(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Content encoding: {0}", encodingVerboseName));
|
||||
|
||||
bool convertSuccess = false;
|
||||
|
||||
if (returnType == RestReturnType.Json)
|
||||
{
|
||||
convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex);
|
||||
}
|
||||
// default to try xml first since it's more common
|
||||
else
|
||||
{
|
||||
convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex);
|
||||
}
|
||||
|
||||
if (!convertSuccess)
|
||||
{
|
||||
// fallback to string
|
||||
obj = str;
|
||||
}
|
||||
|
||||
WriteObject(obj);
|
||||
}
|
||||
}
|
||||
else if (ShouldSaveToOutFile)
|
||||
{
|
||||
StreamHelper.SaveStreamToFile(baseResponseStream, QualifiedOutFile, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(StatusCodeVariable))
|
||||
{
|
||||
PSVariableIntrinsics vi = SessionState.PSVariable;
|
||||
vi.Set(StatusCodeVariable, (int)response.StatusCode);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ResponseHeadersVariable))
|
||||
{
|
||||
PSVariableIntrinsics vi = SessionState.PSVariable;
|
||||
vi.Set(ResponseHeadersVariable, WebResponseHelper.GetHeadersDictionary(response));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Virtual Method Overrides
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private static RestReturnType CheckReturnType(HttpResponseMessage response)
|
||||
{
|
||||
if (response == null) { throw new ArgumentNullException(nameof(response)); }
|
||||
|
||||
RestReturnType rt = RestReturnType.Detect;
|
||||
string contentType = ContentHelper.GetContentType(response);
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
{
|
||||
rt = RestReturnType.Detect;
|
||||
}
|
||||
else if (ContentHelper.IsJson(contentType))
|
||||
{
|
||||
rt = RestReturnType.Json;
|
||||
}
|
||||
else if (ContentHelper.IsXml(contentType))
|
||||
{
|
||||
rt = RestReturnType.Xml;
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
private bool TryProcessFeedStream(Stream responseStream)
|
||||
{
|
||||
bool isRssOrFeed = false;
|
||||
@@ -111,7 +235,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
if (isRssOrFeed)
|
||||
{
|
||||
XmlDocument workingDocument = new();
|
||||
// performing a Read() here to avoid rrechecking
|
||||
|
||||
// Performing a Read() here to avoid rechecking
|
||||
// "rss" or "feed" items
|
||||
reader.Read();
|
||||
while (!reader.EOF)
|
||||
@@ -122,7 +247,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
string.Equals("Entry", reader.Name, StringComparison.OrdinalIgnoreCase))
|
||||
)
|
||||
{
|
||||
// this one will do reader.Read() internally
|
||||
// This one will do reader.Read() internally
|
||||
XmlNode result = workingDocument.ReadNode(reader);
|
||||
WriteObject(result);
|
||||
}
|
||||
@@ -178,7 +303,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
doc = null;
|
||||
}
|
||||
|
||||
return (doc != null);
|
||||
return doc != null;
|
||||
}
|
||||
|
||||
private static bool TryConvertToJson(string json, out object obj, ref Exception exRef)
|
||||
@@ -226,7 +351,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
return converted;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion Helper Methods
|
||||
|
||||
/// <summary>
|
||||
/// Enum for rest return type.
|
||||
@@ -265,20 +390,11 @@ namespace Microsoft.PowerShell.Commands
|
||||
private readonly MemoryStream _streamBuffer;
|
||||
private readonly byte[] _copyBuffer;
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public override bool CanSeek => true;
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
@@ -359,140 +475,4 @@ namespace Microsoft.PowerShell.Commands
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Merge Partials
|
||||
|
||||
/// <summary>
|
||||
/// The Invoke-RestMethod command
|
||||
/// This command makes an HTTP or HTTPS request to a web service,
|
||||
/// and returns the response in an appropriate way.
|
||||
/// Intended to work against the wide spectrum of "RESTful" web services
|
||||
/// currently deployed across the web.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096706", DefaultParameterSetName = "StandardMethod")]
|
||||
public partial class InvokeRestMethodCommand : WebRequestPSCmdlet
|
||||
{
|
||||
#region Virtual Method Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Process the web response and output corresponding objects.
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
internal override void ProcessResponse(HttpResponseMessage response)
|
||||
{
|
||||
if (response == null) { throw new ArgumentNullException(nameof(response)); }
|
||||
|
||||
var baseResponseStream = StreamHelper.GetResponseStream(response);
|
||||
|
||||
if (ShouldWriteToPipeline)
|
||||
{
|
||||
using var responseStream = new BufferingStreamReader(baseResponseStream);
|
||||
|
||||
// First see if it is an RSS / ATOM feed, in which case we can
|
||||
// stream it - unless the user has overridden it with a return type of "XML"
|
||||
if (TryProcessFeedStream(responseStream))
|
||||
{
|
||||
// Do nothing, content has been processed.
|
||||
}
|
||||
else
|
||||
{
|
||||
// determine the response type
|
||||
RestReturnType returnType = CheckReturnType(response);
|
||||
|
||||
// Try to get the response encoding from the ContentType header.
|
||||
Encoding encoding = null;
|
||||
string charSet = response.Content.Headers.ContentType?.CharSet;
|
||||
if (!string.IsNullOrEmpty(charSet))
|
||||
{
|
||||
StreamHelper.TryGetEncoding(charSet, out encoding);
|
||||
}
|
||||
|
||||
object obj = null;
|
||||
Exception ex = null;
|
||||
|
||||
string str = StreamHelper.DecodeStream(responseStream, ref encoding);
|
||||
|
||||
string encodingVerboseName;
|
||||
try
|
||||
{
|
||||
encodingVerboseName = string.IsNullOrEmpty(encoding.HeaderName) ? encoding.EncodingName : encoding.HeaderName;
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
encodingVerboseName = encoding.EncodingName;
|
||||
}
|
||||
// NOTE: Tests use this verbose output to verify the encoding.
|
||||
WriteVerbose(string.Format
|
||||
(
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
"Content encoding: {0}",
|
||||
encodingVerboseName)
|
||||
);
|
||||
bool convertSuccess = false;
|
||||
|
||||
if (returnType == RestReturnType.Json)
|
||||
{
|
||||
convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex);
|
||||
}
|
||||
// default to try xml first since it's more common
|
||||
else
|
||||
{
|
||||
convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex);
|
||||
}
|
||||
|
||||
if (!convertSuccess)
|
||||
{
|
||||
// fallback to string
|
||||
obj = str;
|
||||
}
|
||||
|
||||
WriteObject(obj);
|
||||
}
|
||||
}
|
||||
else if (ShouldSaveToOutFile)
|
||||
{
|
||||
StreamHelper.SaveStreamToFile(baseResponseStream, QualifiedOutFile, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(StatusCodeVariable))
|
||||
{
|
||||
PSVariableIntrinsics vi = SessionState.PSVariable;
|
||||
vi.Set(StatusCodeVariable, (int)response.StatusCode);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ResponseHeadersVariable))
|
||||
{
|
||||
PSVariableIntrinsics vi = SessionState.PSVariable;
|
||||
vi.Set(ResponseHeadersVariable, WebResponseHelper.GetHeadersDictionary(response));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Virtual Method Overrides
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private static RestReturnType CheckReturnType(HttpResponseMessage response)
|
||||
{
|
||||
if (response == null) { throw new ArgumentNullException(nameof(response)); }
|
||||
|
||||
RestReturnType rt = RestReturnType.Detect;
|
||||
string contentType = ContentHelper.GetContentType(response);
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
{
|
||||
rt = RestReturnType.Detect;
|
||||
}
|
||||
else if (ContentHelper.IsJson(contentType))
|
||||
{
|
||||
rt = RestReturnType.Json;
|
||||
}
|
||||
else if (ContentHelper.IsXml(contentType))
|
||||
{
|
||||
rt = RestReturnType.Xml;
|
||||
}
|
||||
|
||||
return (rt);
|
||||
}
|
||||
|
||||
#endregion Helper Methods
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user